Repository navigation
Add defaultStyle to a TipTap text block - #6384
VPS-Andreas wants to merge 10 commits into
Conversation
ab4807c to
021d328
Compare
021d328 to
791d707
Compare
|
We'll review this once #6383 has been merged. |
3ac786e to
7695789
Compare
7695789 to
709b81b
Compare
709b81b to
07ba087
Compare
07ba087 to
cf07062
Compare
cf07062 to
eb97bbb
Compare
|
97283f1 to
785125b
Compare
…6383) A text block style had to be configured in two places: globally in `textBlockStyles`, and again through an `appliesTo` listing the text block types it was allowed for. Which styles a text block offers could only be read by scanning every style's `appliesTo`, and two text blocks sharing a tag could not be told apart at all. A text block now carries its style definitions directly, and `textBlockStyles` is gone: ```tsx const headlineStyles: TipTapTextBlockStyle[] = [ { name: "headline300", label: "Headline 300", element: (props, Tag) => <Tag {...props} /> }, { name: "headline400", label: "Headline 400", element: (props, Tag) => <Tag {...props} /> }, ]; createTipTapRichTextBlock({ textBlocks: [ { name: "heading-1", label: "Heading 1", tag: "h1", styles: headlineStyles }, { name: "heading-2", label: "Heading 2", tag: "h2", styles: headlineStyles }, { name: "display", label: "Display", tag: "h1", element: (props, Tag) => <Tag style={{ fontSize: 64 }} {...props} /> }, ], orderedList: { styles: copyStyles }, }); ``` Text blocks that offer the same style share its definition, which makes the shared set explicit instead of implying it through repeated names. A style's `element` receives the tag of the text block it is applied to, so one definition works for several tags. A text block that needs no style choice carries its own `element` instead of `styles`; the two exclude each other in the types, so a text block either offers a choice or renders one way, and the styling select is only shown where there is something to choose. Lists take the same shape instead of `appliesTo: ["ordered-list", "unordered-list"]`. The stored format is unchanged — a style is still identified by its `name` in `textBlockStyle` — so no migration is needed. Second of four stacked pull requests, on top of #6382; #6384 adds `defaultStyle`, #6385 splits the block's Storybook stories by topic. ## Example The unit tests in `createTipTapRichTextBlock.test.ts`/`.test.tsx`, and in Storybook: - [Text Block Styles](https://tiptap-text-block-styles--69df3371c46abe69b5199825.chromatic.com/?path=/story/blocks-tiptaprichtextblock--text-block-styles) - [Text Block Style Interactions](https://tiptap-text-block-styles--69df3371c46abe69b5199825.chromatic.com/?path=/story/blocks-tiptaprichtextblock--text-block-style-interactions) - [List Text Block Styles](https://tiptap-text-block-styles--69df3371c46abe69b5199825.chromatic.com/?path=/story/blocks-tiptaprichtextblock--list-text-block-styles) - [Text Block Element](https://tiptap-text-block-styles--69df3371c46abe69b5199825.chromatic.com/?path=/story/blocks-tiptaprichtextblock--text-block-element) Session: https://claude.ai/code/session_f3c6ad37-c68b-4187-85b6-2abe5aa95c35 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The styling select always offered a "Default" entry standing for "no style", even where a design has no unstyled variant. A text block with a defaultStyle drops that entry, so every paragraph or heading of that type carries one of its styles. The editor creates text blocks in several ways that don't go through the toolbar, so a ProseMirror plugin fills the style in for any node that has none, rather than reimplementing Enter, the keyboard shortcuts and paste one by one. It runs only on a document change, so opening older content leaves it untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Toggling a list through the toolbar hands the cursor's paragraph from its text block to the list or back, so the styles of whichever now holds it apply. Mod-Shift-7 and Mod-Shift-8 went through the list extensions' own shortcuts and left the previous style in place, which the list may not offer at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Only the toolbar's type select kept the style within what the new text block offers. The `Mod-Alt-<level>` shortcuts and the `#` input rules change the text block alone, so a paragraph styled "intro" turned into a heading that still named it: the editor rendered it without a style, the styling select stood empty although a configured `defaultStyle` leaves it no empty entry, and the API rejects that content. The plugin that fills in a missing default style now corrects a style neither the text block nor the list it sits in offers, which covers every path at once. Switching the type inside a list keeps the list's style as well, instead of falling back to the new text block's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Toggling a list or switching the type wrote one style onto every text block the selection spans: `updateAttributes` applies the same value to all of them, and the value came from whichever node the toolbar read the active style off. Selecting two paragraphs styled differently and toggling a list left both with the style of one of them, even where the list offers the other. Each text block in the selection now resolves its style from the style it carries, so it keeps one the list offers and falls back to a default only where it has to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`makeListItem` and `addListItem` grew past two positional parameters while the list's styles were added, where the repository's TypeScript guideline asks for a single options object - with three list-shaped arguments next to each other, the call sites read as guesswork. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
785125b to
2f97d30
Compare
|
@greptileai review |
| name: "textBlockList", | ||
|
|
||
| // Must beat the list extensions, whose shortcuts use the same keys. | ||
| priority: 1100, |
There was a problem hiding this comment.
Will this clash with the text block's priority declared here? https://github.com/vivid-planet/dextinity/pull/6520/changes#diff-6adff581824fc49917cd599da9fa0e7a62c2242f5a3bc20bcd959ae4ece67024R38
There was a problem hiding this comment.
No — textBlockList is an Extension, not a Node, so it contributes nothing to the schema: TipTap builds the schema from splitExtensions(...).nodeExtensions, and this priority only orders plugins and shortcuts. With #6520 merged into this branch, the running editor reports defaultType: textBlock and a node order starting with textBlock.
The keys don't overlap either: textBlock takes Mod-Alt-<level>, textBlockList takes Mod-Shift-7/Mod-Shift-8. The 1100 only has to beat @tiptap/extension-list, which registers bulletList, orderedList and listKeymap without a priority of their own, so at the default 100.
|
@VPS-Andreas please test this change in Demo as well and verify that saving is possible to prevent issues such as described here: #6520. Unfortunately, we can't test this end to end in Storybook alone. |
`defaultStyle` had no example in Demo, so reviewers could only see it in Storybook, and saving a text block that carries its default went untested. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Tested in Demo. Saving works, and the content comes back unchanged after a reload. A paragraph gets its default filled in on the first edit and the styling select drops its "Default" entry; Before #6520 was merged into this branch, saving did fail here: toggling a list at the end of the content left an empty |
The styling select always offered a "Default" entry standing for "no style", even where a design has no unstyled variant and every text block is meant to carry one of the configured styles. Editors had to pick the right style by hand on every new text block, and forgetting it produced unstyled content.
A text block (or list) with a
defaultStylehas no such state:The select drops its "Default" entry, and the style is applied to new content and to every text block the editor creates without one — pressing Enter at the end of a text block, the
Mod-Alt-<level>shortcuts, pasting. Rather than reimplementing each of those paths, a ProseMirror plugin fills in a missing style on document change, so opening older content leaves it untouched and does not mark the document as changed. Switching the type keeps a style the new text block also offers and falls back to itsdefaultStyleotherwise, and toggling a list hands the text block to the list's styles or back — through the toolbar buttons andMod-Shift-7/Mod-Shift-8alike.Because the default sits on the text block rather than on a tag, two text blocks sharing a tag can have different defaults, and a text block without one keeps its "Default" entry next to text blocks that have one.
Third of four stacked pull requests, on top of #6383 and #6382; #6385 splits the block's Storybook stories by topic.
Example
createTipTapRichTextBlock.test.ts/.test.tsxcover the validation and the content a default style produces. The Storybook story Default Text Block Style walks through a text block with a default, one without, and the styled lists.Session: https://claude.ai/code/session_f3c6ad37-c68b-4187-85b6-2abe5aa95c35