playbook_ui 18.2.0.pre.rc.2 → 18.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_background/_background.tsx +3 -1
  3. data/app/pb_kits/playbook/pb_background/kit.schema.json +1 -0
  4. data/app/pb_kits/playbook/pb_caption/_caption.tsx +3 -1
  5. data/app/pb_kits/playbook/pb_caption/kit.schema.json +1 -0
  6. data/app/pb_kits/playbook/pb_container/_container.tsx +3 -1
  7. data/app/pb_kits/playbook/pb_container/kit.schema.json +1 -0
  8. data/app/pb_kits/playbook/pb_currency/_currency.tsx +3 -1
  9. data/app/pb_kits/playbook/pb_currency/kit.schema.json +1 -0
  10. data/app/pb_kits/playbook/pb_dashboard_value/_dashboard_value.tsx +3 -1
  11. data/app/pb_kits/playbook/pb_dashboard_value/kit.schema.json +1 -0
  12. data/app/pb_kits/playbook/pb_detail/_detail.tsx +3 -1
  13. data/app/pb_kits/playbook/pb_detail/kit.schema.json +1 -0
  14. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +6 -5
  15. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom.md +7 -2
  16. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_custom_rails.md +7 -2
  17. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.html.erb +33 -0
  18. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.jsx +42 -0
  19. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_quickpick_enable_time.md +24 -0
  20. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_highlight.jsx +101 -0
  21. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_highlight.md +3 -0
  22. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +3 -0
  23. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +2 -0
  24. data/app/pb_kits/playbook/pb_dropdown/index.js +19 -10
  25. data/app/pb_kits/playbook/pb_dropdown/quickpick/index.ts +60 -18
  26. data/app/pb_kits/playbook/pb_dropdown/quickpick_helper.rb +92 -3
  27. data/app/pb_kits/playbook/pb_dropdown/utilities/index.ts +8 -1
  28. data/app/pb_kits/playbook/pb_dropdown/utilities/quickpickDateSync.ts +137 -0
  29. data/app/pb_kits/playbook/pb_filter/Filter/InteractiveFilter.tsx +1 -1
  30. data/app/pb_kits/playbook/pb_highlight/_highlight.tsx +3 -1
  31. data/app/pb_kits/playbook/pb_highlight/kit.schema.json +1 -0
  32. data/app/pb_kits/playbook/pb_icon_stat_value/_icon_stat_value.tsx +3 -1
  33. data/app/pb_kits/playbook/pb_icon_stat_value/kit.schema.json +1 -0
  34. data/app/pb_kits/playbook/pb_icon_value/_icon_value.tsx +3 -1
  35. data/app/pb_kits/playbook/pb_icon_value/kit.schema.json +1 -0
  36. data/app/pb_kits/playbook/pb_label_value/_label_value.tsx +3 -1
  37. data/app/pb_kits/playbook/pb_label_value/kit.schema.json +1 -0
  38. data/app/pb_kits/playbook/pb_map/docs/_map_default.md +3 -3
  39. data/app/pb_kits/playbook/pb_message/_message.tsx +3 -1
  40. data/app/pb_kits/playbook/pb_message/kit.schema.json +1 -0
  41. data/app/pb_kits/playbook/pb_multiple_users/_multiple_users.tsx +3 -1
  42. data/app/pb_kits/playbook/pb_multiple_users/kit.schema.json +1 -0
  43. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +4 -2
  44. data/app/pb_kits/playbook/pb_multiple_users_stacked/kit.schema.json +1 -0
  45. data/app/pb_kits/playbook/pb_rich_text_editor/TipTap/MoreExtensionsDropdown.tsx +10 -3
  46. data/app/pb_kits/playbook/pb_rich_text_editor/TipTap/Toolbar.tsx +16 -1
  47. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_playground.json +2 -3
  48. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_playground.overrides.json +2 -3
  49. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_attributes.jsx +1 -5
  50. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_default.jsx +1 -5
  51. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_default.md +2 -3
  52. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_focus.jsx +1 -5
  53. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_height.jsx +3 -13
  54. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_inline.jsx +1 -5
  55. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_label.jsx +2 -3
  56. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_markdown_support.jsx +1 -2
  57. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_min_height.jsx +3 -13
  58. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_preview.jsx +1 -5
  59. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_required_indicator.jsx +1 -5
  60. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_simple.jsx +1 -5
  61. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_sticky.jsx +1 -5
  62. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_advanced_templates.jsx +1 -5
  63. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_more_extensions.jsx +3 -8
  64. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_more_extensions.md +1 -1
  65. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_rails_default.md +2 -2
  66. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.test.js +2 -1
  67. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_advanced.test.js +7 -5
  68. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor_rails.js +15 -4
  69. data/app/pb_kits/playbook/pb_stat_value/_stat_value.tsx +3 -1
  70. data/app/pb_kits/playbook/pb_stat_value/kit.schema.json +1 -0
  71. data/app/pb_kits/playbook/pb_table/_table.tsx +70 -197
  72. data/app/pb_kits/playbook/pb_table/index.ts +53 -122
  73. data/app/pb_kits/playbook/pb_table/styles/_sticky_columns.scss +26 -3
  74. data/app/pb_kits/playbook/pb_table/utilities/stickyColumns.test.ts +266 -0
  75. data/app/pb_kits/playbook/pb_table/utilities/stickyColumns.ts +145 -0
  76. data/app/pb_kits/playbook/pb_title_count/_title_count.tsx +3 -1
  77. data/app/pb_kits/playbook/pb_title_count/kit.schema.json +1 -0
  78. data/app/pb_kits/playbook/pb_title_detail/_title_detail.tsx +3 -1
  79. data/app/pb_kits/playbook/pb_title_detail/kit.schema.json +1 -0
  80. data/app/pb_kits/playbook/utilities/test/globalProps/onClick.test.js +237 -0
  81. data/dist/ai/all-schemas.json +16 -0
  82. data/dist/ai/forms.json +1 -1
  83. data/dist/ai/index.json +2 -2
  84. data/dist/ai/kits/background.schema.json +1 -0
  85. data/dist/ai/kits/caption.schema.json +1 -0
  86. data/dist/ai/kits/container.schema.json +1 -0
  87. data/dist/ai/kits/currency.schema.json +1 -0
  88. data/dist/ai/kits/dashboard_value.schema.json +1 -0
  89. data/dist/ai/kits/detail.schema.json +1 -0
  90. data/dist/ai/kits/highlight.schema.json +1 -0
  91. data/dist/ai/kits/icon_stat_value.schema.json +1 -0
  92. data/dist/ai/kits/icon_value.schema.json +1 -0
  93. data/dist/ai/kits/label_value.schema.json +1 -0
  94. data/dist/ai/kits/message.schema.json +1 -0
  95. data/dist/ai/kits/multiple_users.schema.json +1 -0
  96. data/dist/ai/kits/multiple_users_stacked.schema.json +1 -0
  97. data/dist/ai/kits/stat_value.schema.json +1 -0
  98. data/dist/ai/kits/title_count.schema.json +1 -0
  99. data/dist/ai/kits/title_detail.schema.json +1 -0
  100. data/dist/ai/playgrounds/rich_text_editor.json +1 -1
  101. data/dist/chunks/{_detail-BqqzPTNr.js → _detail-Bsvl694i.js} +2 -2
  102. data/dist/chunks/{_pb_line_graph-BGyCJjgG.js → _pb_line_graph-Du7xE9vl.js} +1 -1
  103. data/dist/chunks/_table-Bf6LGyL9.js +1 -0
  104. data/dist/chunks/_typeahead-w6S5Vx4b.js +1 -0
  105. data/dist/chunks/{globalProps-K1DCn3il.js → globalProps-B120NbSJ.js} +1 -1
  106. data/dist/chunks/{lib-7vm9BMqp.js → lib-Buo6zHEw.js} +1 -1
  107. data/dist/chunks/quickpickDateSync-DLDx4zce.js +1 -0
  108. data/dist/chunks/{time_picker_helper-BZutIzB3.js → time_picker_helper-CYGg0UQ0.js} +1 -1
  109. data/dist/chunks/vendor.js +2 -2
  110. data/dist/playbook-rails-react-bindings.js +1 -1
  111. data/dist/playbook-rails.js +1 -1
  112. data/dist/playbook.css +1 -1
  113. data/lib/playbook/version.rb +1 -1
  114. metadata +19 -13
  115. data/dist/chunks/_table-CEZU0qUK.js +0 -1
  116. data/dist/chunks/_typeahead-C7iPR5ZF.js +0 -1
@@ -1,8 +1,9 @@
1
1
  import React from 'react'
2
2
  import classnames from 'classnames'
3
3
 
4
- import { globalProps } from '../utilities/globalProps'
5
4
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
5
+ import { globalProps } from '../utilities/globalProps'
6
+ import { GlobalEventProps, globalEventProps } from '../utilities/globalEventProps'
6
7
 
7
8
  import Avatar from '../pb_avatar/_avatar'
8
9
  import Badge from '../pb_badge/_badge'
@@ -17,7 +18,7 @@ type MultipleUsersStackedProps = {
17
18
  size?: "md" | "lg" | "sm" | "xl"
18
19
  users: Array<{ [key: string]: string }>,
19
20
  variant: "default" | "bubble",
20
- }
21
+ } & GlobalEventProps
21
22
 
22
23
  const MultipleUsersStacked = (props: MultipleUsersStackedProps) => {
23
24
  const {
@@ -132,6 +133,7 @@ const MultipleUsersStacked = (props: MultipleUsersStackedProps) => {
132
133
  {...ariaProps}
133
134
  {...dataProps}
134
135
  {...htmlProps}
136
+ {...globalEventProps(props)}
135
137
  className={classes}
136
138
  id={id}
137
139
  >
@@ -48,6 +48,7 @@
48
48
  }
49
49
  },
50
50
  "globalProps": true,
51
+ "globalEventProps": true,
51
52
  "usage": {
52
53
  "react": {
53
54
  "import": "import { MultipleUsersStacked } from 'playbook-ui'",
@@ -54,9 +54,15 @@ const popoverReference = (
54
54
  paddingTop={extensions.length > 1 ? "xs" : "none"}
55
55
  variant="subtle"
56
56
  >
57
- {extensions && extensions.map(({ icon, text, onclick, isActive}: any, index: number) => (
57
+ {extensions && extensions.map(({ icon, text, onclick, isActive}: any, index: number) => {
58
+ // TipTap v3: host useEditor no longer re-renders on selection by default.
59
+ // Prefer isActive as a function so EditorToolbar's transaction subscription
60
+ // can refresh custom extension highlights without shouldRerenderOnTransaction.
61
+ const active = typeof isActive === "function" ? isActive() : !!isActive
62
+
63
+ return (
58
64
  <NavItem
59
- className={`pb_tiptap_toolbar_dropdown_list_item ${isActive ? "is-active" : ""}`}
65
+ className={`pb_tiptap_toolbar_dropdown_list_item ${active ? "is-active" : ""}`}
60
66
  cursor="pointer"
61
67
  iconLeft={icon}
62
68
  key={`${text}_${index}`}
@@ -66,7 +72,8 @@ const popoverReference = (
66
72
  paddingTop='xxs'
67
73
  text={text}
68
74
  />
69
- ))}
75
+ )
76
+ })}
70
77
  </Nav>
71
78
  </PbReactPopover>
72
79
  )
@@ -1,4 +1,4 @@
1
- import React, { useLayoutEffect, useRef, useState } from "react";
1
+ import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
2
2
  import Background from "../../pb_background/_background";
3
3
  import Flex from "../../pb_flex/_flex";
4
4
  import SectionSeparator from "../../pb_section_separator/_section_separator";
@@ -72,6 +72,21 @@ const EditorToolbar = ({ editor, extensions, simple, sticky }: any): React.React
72
72
  const toolbarInnerRef = useRef<HTMLDivElement>(null);
73
73
  const stackedAtWidthRef = useRef(0);
74
74
  const [toolsStacked, setToolsStacked] = useState(false);
75
+ // TipTap v3 defaults shouldRerenderOnTransaction to false; re-render active states locally.
76
+ const [, setEditorRenderTick] = useState(0);
77
+
78
+ useEffect(() => {
79
+ if (!editor) return;
80
+
81
+ const rerenderToolbar = () => setEditorRenderTick((tick) => tick + 1);
82
+ editor.on("selectionUpdate", rerenderToolbar);
83
+ editor.on("transaction", rerenderToolbar);
84
+
85
+ return () => {
86
+ editor.off("selectionUpdate", rerenderToolbar);
87
+ editor.off("transaction", rerenderToolbar);
88
+ };
89
+ }, [editor]);
75
90
 
76
91
  useLayoutEffect(() => {
77
92
  if (simple) return;
@@ -148,11 +148,10 @@
148
148
  "label": "Advanced",
149
149
  "template": "<RichTextEditor\n advancedEditor={editor}{{props}}\n>\n <EditorContent editor={editor} />\n</RichTextEditor>",
150
150
  "children": "",
151
- "wrapper": "const RichTextEditorPlayground = () => {\n const editor = useEditor({\n extensions: [StarterKit, TipTapLink],\n content: 'Add your text here. You can format your text, add links, quotes, and bullets.',\n })\n\n if (!editor) return null\n\n return {{component}}\n}\n\n<RichTextEditorPlayground />",
151
+ "wrapper": "const RichTextEditorPlayground = () => {\n const editor = useEditor({\n extensions: [StarterKit.configure({ trailingNode: false })],\n content: 'Add your text here. You can format your text, add links, quotes, and bullets.',\n })\n\n if (!editor) return null\n\n return {{component}}\n}\n\n<RichTextEditorPlayground />",
152
152
  "externalImports": [
153
153
  "import { useEditor, EditorContent } from '@tiptap/react'",
154
- "import StarterKit from '@tiptap/starter-kit'",
155
- "import TipTapLink from '@tiptap/extension-link'"
154
+ "import StarterKit from '@tiptap/starter-kit'"
156
155
  ]
157
156
  }
158
157
  }
@@ -19,11 +19,10 @@
19
19
  "label": "Advanced",
20
20
  "template": "<RichTextEditor\n advancedEditor={editor}{{props}}\n>\n <EditorContent editor={editor} />\n</RichTextEditor>",
21
21
  "children": "",
22
- "wrapper": "const RichTextEditorPlayground = () => {\n const editor = useEditor({\n extensions: [StarterKit, TipTapLink],\n content: 'Add your text here. You can format your text, add links, quotes, and bullets.',\n })\n\n if (!editor) return null\n\n return {{component}}\n}\n\n<RichTextEditorPlayground />",
22
+ "wrapper": "const RichTextEditorPlayground = () => {\n const editor = useEditor({\n extensions: [StarterKit.configure({ trailingNode: false })],\n content: 'Add your text here. You can format your text, add links, quotes, and bullets.',\n })\n\n if (!editor) return null\n\n return {{component}}\n}\n\n<RichTextEditorPlayground />",
23
23
  "externalImports": [
24
24
  "import { useEditor, EditorContent } from '@tiptap/react'",
25
- "import StarterKit from '@tiptap/starter-kit'",
26
- "import TipTapLink from '@tiptap/extension-link'"
25
+ "import StarterKit from '@tiptap/starter-kit'"
27
26
  ]
28
27
  }
29
28
  }
@@ -2,16 +2,12 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
 
8
7
  const RichTextEditorAdvancedAttributes = (props) => {
9
8
 
10
9
  const editor = useEditor({
11
- extensions: [
12
- StarterKit,
13
- Link
14
- ],
10
+ extensions: [StarterKit.configure({ trailingNode: false })],
15
11
  content:"Add your text here. You can format your text, add links, quotes, and bullets."
16
12
  })
17
13
  if (!editor) {
@@ -2,16 +2,12 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
 
8
7
  const RichTextEditorAdvancedDefault = (props) => {
9
8
 
10
9
  const editor = useEditor({
11
- extensions: [
12
- StarterKit,
13
- Link
14
- ],
10
+ extensions: [StarterKit.configure({ trailingNode: false })],
15
11
  content:"Add your text here. You can format your text, add links, quotes, and bullets."
16
12
  })
17
13
  if (!editor) {
@@ -1,4 +1,3 @@
1
- The advanced variant leverages Tiptap to unlock additional UI options (e.g., consolidated nav toolbar, styling, etc.) as well as several extensions (see [Tiptap docs](https://tiptap.dev/extensions) for more). To leverage this variant, Tiptap __must be installed__ in your project. Complete docs for using the library can be found [here](https://tiptap.dev/). To get started with this variant, see the Code Example below for required imports as well as the basic setup.
1
+ The advanced variant leverages Tiptap to unlock additional UI options (e.g., consolidated nav toolbar, styling, etc.) as well as several extensions (see [Tiptap docs](https://tiptap.dev/extensions) for more). To leverage this variant, Tiptap __must be installed__ in your project at `3.0.7` (same version as Nitro RTE). Complete docs for using the library can be found [here](https://tiptap.dev/). To get started with this variant, see the Code Example below for required imports as well as the basic setup.
2
2
 
3
-
4
- NOTE: Once the Tiptap editor is initialized as shown below, you must pass that instance to the kit via the `advancedEditor` prop.
3
+ NOTE: TipTap v3 StarterKit includes Link and Underline. Do not also register `@tiptap/extension-link` / `@tiptap/extension-underline` unless you disable them on StarterKit first. Prefer `StarterKit.configure({ trailingNode: false })` so saved HTML does not pick up TipTap’s synthetic trailing empty paragraph. Once the Tiptap editor is initialized as shown below, you must pass that instance to the kit via the `advancedEditor` prop.
@@ -2,15 +2,11 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
 
8
7
  const RichTextEditorAdvancedFocus = (props) => {
9
8
  const editor = useEditor({
10
- extensions: [
11
- StarterKit,
12
- Link
13
- ],
9
+ extensions: [StarterKit.configure({ trailingNode: false })],
14
10
  content:"Click inside to see the toolbar. Click outside to hide it."
15
11
  })
16
12
 
@@ -2,33 +2,23 @@ import React from 'react'
2
2
  import RichTextEditor from '../_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
  import { Caption } from "playbook-ui"
7
6
 
8
7
 
9
8
  const RichTextEditorAdvancedHeight = (props) => {
10
9
 
11
10
  const editor = useEditor({
12
- extensions: [
13
- StarterKit,
14
- Link
15
- ],
11
+ extensions: [StarterKit.configure({ trailingNode: false })],
16
12
  content: "Add your text here. You can format your text, add links, quotes, and bullets."
17
13
  })
18
14
 
19
15
  const editor2 = useEditor({
20
- extensions: [
21
- StarterKit,
22
- Link
23
- ],
16
+ extensions: [StarterKit.configure({ trailingNode: false })],
24
17
  content: "Add your text here. You can format your text, add links, quotes, and bullets."
25
18
  })
26
19
 
27
20
  const editor3 = useEditor({
28
- extensions: [
29
- StarterKit,
30
- Link
31
- ],
21
+ extensions: [StarterKit.configure({ trailingNode: false })],
32
22
  content: "Add your text here. You can format your text, add links, quotes, and bullets."
33
23
  })
34
24
 
@@ -2,16 +2,12 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
 
8
7
  const RichTextEditorAdvancedInline = (props) => {
9
8
 
10
9
  const editor = useEditor({
11
- extensions: [
12
- StarterKit,
13
- Link
14
- ],
10
+ extensions: [StarterKit.configure({ trailingNode: false })],
15
11
  content:"Add your text here. You can format your text, add links, quotes, and bullets."
16
12
  })
17
13
  if (!editor) {
@@ -2,18 +2,17 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
 
8
7
  const RichTextEditorAdvancedLabel = (props) => {
9
8
 
10
9
  const editor = useEditor({
11
- extensions: [StarterKit, Link],
10
+ extensions: [StarterKit.configure({ trailingNode: false })],
12
11
  content: "Add your text here. You can format your text, add links, quotes, and bullets.",
13
12
  })
14
13
 
15
14
  const editorNoLabel = useEditor({
16
- extensions: [StarterKit, Link],
15
+ extensions: [StarterKit.configure({ trailingNode: false })],
17
16
  content: "Add your text here. You can format your text, add links, quotes, and bullets.",
18
17
  })
19
18
 
@@ -2,13 +2,12 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from '@tiptap/react'
4
4
  import StarterKit from '@tiptap/starter-kit'
5
- import Link from '@tiptap/extension-link'
6
5
  import { defaultMarkdownParser } from 'prosemirror-markdown'
7
6
  import { DOMSerializer } from 'prosemirror-model'
8
7
 
9
8
  const RichTextEditorAdvancedMarkdownSupport = (props) => {
10
9
  const editor = useEditor({
11
- extensions: [StarterKit, Link],
10
+ extensions: [StarterKit.configure({ trailingNode: false })],
12
11
  content: 'Paste Markdown here to preserve headings, emphasis, links, lists, quotes, and code blocks.',
13
12
  })
14
13
 
@@ -2,33 +2,23 @@ import React from 'react'
2
2
  import RichTextEditor from '../_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
  import { Caption } from "playbook-ui"
7
6
 
8
7
 
9
8
  const RichTextEditorAdvancedMinHeight = (props) => {
10
9
 
11
10
  const editor1 = useEditor({
12
- extensions: [
13
- StarterKit,
14
- Link
15
- ],
11
+ extensions: [StarterKit.configure({ trailingNode: false })],
16
12
  content: "Add your text here. You can format your text, add links, quotes, and bullets."
17
13
  })
18
14
 
19
15
  const editor2 = useEditor({
20
- extensions: [
21
- StarterKit,
22
- Link
23
- ],
16
+ extensions: [StarterKit.configure({ trailingNode: false })],
24
17
  content: "Add your text here. You can format your text, add links, quotes, and bullets."
25
18
  })
26
19
 
27
20
  const editor3 = useEditor({
28
- extensions: [
29
- StarterKit,
30
- Link
31
- ],
21
+ extensions: [StarterKit.configure({ trailingNode: false })],
32
22
  content: "Add your text here. You can format your text, add links, quotes, and bullets."
33
23
  })
34
24
 
@@ -4,16 +4,12 @@ import Button from '../../pb_button/_button'
4
4
  import Card from '../../pb_card/_card'
5
5
  import { useEditor, EditorContent } from "@tiptap/react"
6
6
  import StarterKit from "@tiptap/starter-kit"
7
- import Link from '@tiptap/extension-link'
8
7
 
9
8
 
10
9
  const RichTextEditorAdvancedPreview = (props) => {
11
10
 
12
11
  const editor = useEditor({
13
- extensions: [
14
- StarterKit,
15
- Link
16
- ],
12
+ extensions: [StarterKit.configure({ trailingNode: false })],
17
13
  content: "Add text here, format it, and press \"Preview Output\" to see what your stylized output will look like on the page."
18
14
  })
19
15
 
@@ -2,16 +2,12 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
 
8
7
  const RichTextEditorAdvancedRequiredIndicator = (props) => {
9
8
 
10
9
  const editor = useEditor({
11
- extensions: [
12
- StarterKit,
13
- Link
14
- ],
10
+ extensions: [StarterKit.configure({ trailingNode: false })],
15
11
  content:"Add your text here. You can format your text, add links, quotes, and bullets."
16
12
  })
17
13
  if (!editor) {
@@ -2,16 +2,12 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
 
8
7
  const RichTextEditorAdvancedSimple = (props) => {
9
8
 
10
9
  const editor = useEditor({
11
- extensions: [
12
- StarterKit,
13
- Link
14
- ],
10
+ extensions: [StarterKit.configure({ trailingNode: false })],
15
11
  content:"Add your text here. Use the simple toolbar above to format your text"
16
12
  })
17
13
  if (!editor) {
@@ -2,16 +2,12 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
 
8
7
  const RichTextEditorAdvancedSticky = (props) => {
9
8
 
10
9
  const editor = useEditor({
11
- extensions: [
12
- StarterKit,
13
- Link
14
- ],
10
+ extensions: [StarterKit.configure({ trailingNode: false })],
15
11
  content:"In this example, when you scroll down, the rich text editor's toolbar will scroll along with the page and it will no longer be visible at the top of the page. Dummy text to enable scroll.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lorem ut pellentesque tempor. Vivamus ut ex vestibulum velit rich text editor eleifend fringilla. Sed non metus dictum, elementum mauris wysiwyg html editor non, sagittis odio. Nullam pellentesque leo sit amet ante suscipit wysiwyg html editor sagittis. Donec tempus vulputate suscipit. Ut non felis rich text editor ac dolor pulvinar lacinia eu eget urna. Sed tincidunt sapien vulputate tellus fringilla sodales. Morbi accumsan dui wysiwyg html editor sed massa pellentesque, quis vestibulum lectus scelerisque. Nulla ultrices mi id felis luctus aliquet. Donec nec ligula wysiwyg html editor pretium sapien semper dictum eu id quam. Etiam ut sollicitudin nibh. Quisque eu ultrices dui. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus rich text editor mi eu consequat. Nullam tincidunt erat et placerat mattis. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus mi eu consequat. Nullam tincidunt erat et placerat mattis.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lorem ut pellentesque tempor. Vivamus ut ex vestibulum velit rich text editor eleifend fringilla. Sed non metus dictum, elementum mauris wysiwyg html editor non, sagittis odio. Nullam pellentesque leo sit amet ante suscipit wysiwyg html editor sagittis. Donec tempus vulputate suscipit. Ut non felis rich text editor ac dolor pulvinar lacinia eu eget urna. Sed tincidunt sapien vulputate tellus fringilla sodales. Morbi accumsan dui wysiwyg html editor sed massa pellentesque, quis vestibulum lectus scelerisque. Nulla ultrices mi id felis luctus aliquet. Donec nec ligula wysiwyg html editor pretium sapien semper dictum eu id quam. Etiam ut sollicitudin nibh. Quisque eu ultrices dui. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor vulputate suscipit. Ut non felis rich text editor ac dolor pulvinar lacinia eu eget urna. Sed tincidunt sapien vulputate tellus fringilla sodales. Morbi accumsan dui wysiwyg html editor sed massa pellentesque, quis vestibulum lectus scelerisque. Nulla ultrices mi id felis luctus aliquet. Donec nec ligula wysiwyg html editor pretium sapien semper dictum eu id quam. Etiam ut sollicitudin nibh. Quisque eu ultrices dui. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus rich text editor mi eu consequat. Nullam tincidunt erat et placerat mattis. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus mi eu consequat. Nullam tincidunt erat et placerat mattis.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lorem ut pellentesque tempor. Vivamus ut ex vestibulum velit rich text editor eleifend fringilla. Sed non metus dictum, elementum mauris wysiwyg html editor non, sagittis odio. Nullam pellentesque leo sit amet ante suscipit wysiwyg html editor sagittis. Donec tempus vulputate suscipit. Ut non felis rich text editor ac dolor pulvinar lacinia eu eget urna. Sed tincidunt sapien vulputate tellus fringilla sodales. Morbi accumsan dui wysiwyg html editor sed massa pellentesque, quis vestibulum lectus scelerisque. Nulla ultrices mi id felis luctus aliquet. Donec nec ligula wysiwyg html editor pretium sapien semper dictum eu id quam. Etiam ut sollicitudin nibh. Quisque eu ultrices dui. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus rich text editor mi eu consequat. Nullam tincidunt erat et placerat mattis. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus mi eu consequat. Nullam tincidunt erat et placerat mattis.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lorem ut pellentesque tempor. Vivamus ut ex vestibulum velit rich text editor eleifend fringilla. Sed non metus dictum, elementum mauris wysiwyg html editor non, sagittis odio. Nullam pellentesque leo sit amet ante suscipit wysiwyg html editor sagittis. Donec tempus vulputate suscipit. Ut non felis rich text editor ac dolor pulvinar lacinia eu eget urna. Sed tincidunt sapien vulputate tellus fringilla sodales. Morbi accumsan dui wysiwyg html editor sed massa pellentesque, quis vestibulum lectus scelerisque. Nulla ultrices mi id felis luctus aliquet. Donec nec ligula wysiwyg html editor pretium sapien semper dictum eu id quam. Etiam ut sollicitudin nibh. Quisque eu ultrices dui. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus rich text editor mi eu consequat. Nullam tincidunt erat et placerat mattis. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus mi eu consequat. Nullam tincidunt erat et placerat mattis.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lorem ut pellentesque tempor. Vivamus ut ex vestibulum velit rich text editor eleifend fringilla. Sed non metus dictum, elementum mauris wysiwyg html editor non, sagittis odio. Nullam pellentesque leo sit amet ante suscipit wysiwyg html editor sagittis. Donec tempus vulputate suscipit. Ut non felis rich text editor ac dolor pulvinar lacinia eu eget urna. Sed tincidunt sapien vulputate tellus fringilla sodales. Morbi accumsan dui wysiwyg html editor sed massa pellentesque, quis vestibulum lectus scelerisque. Nulla ultrices mi id felis luctus aliquet. Donec nec ligula wysiwyg html editor pretium sapien semper dictum eu id quam. Etiam ut sollicitudin nibh. Quisque eu ultrices dui. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus rich text editor mi eu consequat. Nullam tincidunt erat et placerat mattis. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus mi eu consequat. Nullam tincidunt erat et placerat mattis.sit amet ex. Aliquam cursus maximus rich text editor mi eu consequat. Nullam tincidunt erat et placerat mattis. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus mi eu consequat. Nullam tincidunt erat et placerat mattis.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lorem ut pellentesque tempor. Vivamus ut ex vestibulum velit rich text editor eleifend fringilla. Sed non metus dictum, elementum mauris wysiwyg html editor non, sagittis odio. Nullam pellentesque leo sit amet ante suscipit wysiwyg html editor sagittis. Donec tempus vulputate suscipit. Ut non felis rich text editor ac dolor pulvinar lacinia eu eget urna. Sed tincidunt sapien vulputate tellus fringilla sodales. Morbi accumsan dui wysiwyg html editor sed massa pellentesque, quis vestibulum lectus scelerisque. Nulla ultrices mi id felis luctus aliquet. Donec nec ligula wysiwyg html editor pretium sapien semper dictum eu id quam. Etiam ut sollicitudin nibh. Quisque eu ultrices dui. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus rich text editor mi eu consequat. Nullam tincidunt erat et placerat mattis. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus mi eu consequat. Nullam tincidunt erat et placerat mattis.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare lorem ut pellentesque tempor. Vivamus ut ex vestibulum velit rich text editor eleifend fringilla. Sed non metus dictum, elementum mauris wysiwyg html editor non, sagittis odio. Nullam pellentesque leo sit amet ante suscipit wysiwyg html editor sagittis. Donec tempus vulputate suscipit. Ut non felis rich text editor ac dolor pulvinar lacinia eu eget urna. Sed tincidunt sapien vulputate tellus fringilla sodales. Morbi accumsan dui wysiwyg html editor sed massa pellentesque, quis vestibulum lectus scelerisque. Nulla ultrices mi id felis luctus aliquet. Donec nec ligula wysiwyg html editor pretium sapien semper dictum eu id quam. Etiam ut sollicitudin nibh. Quisque eu ultrices dui. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus rich text editor mi eu consequat. Nullam tincidunt erat et placerat mattis. Nunc rich text editor congue, enim vitae dictum dignissim, libero nisl sagittis augue, non aliquet nibh tortor sit amet ex. Aliquam cursus maximus mi eu consequat. Nullam tincidunt erat et placerat mattis."
16
12
 
17
13
  })
@@ -2,7 +2,6 @@ import React, {useState, useEffect} from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
5
 
7
6
  import Select from '../../pb_select/_select'
8
7
 
@@ -101,10 +100,7 @@ const handleChange = (event) => {
101
100
  ]
102
101
 
103
102
  const editor = useEditor({
104
- extensions: [
105
- StarterKit,
106
- Link
107
- ],
103
+ extensions: [StarterKit.configure({ trailingNode: false })],
108
104
  content: editorContent
109
105
  })
110
106
 
@@ -2,9 +2,6 @@ import React from 'react'
2
2
  import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
3
3
  import { useEditor, EditorContent } from "@tiptap/react"
4
4
  import StarterKit from "@tiptap/starter-kit"
5
- import Link from '@tiptap/extension-link'
6
-
7
- import HorizontalRule from "@tiptap/extension-horizontal-rule"
8
5
  import Highlight from '@tiptap/extension-highlight'
9
6
 
10
7
 
@@ -12,9 +9,7 @@ const RichTextEditorMoreExtensions = (props) => {
12
9
 
13
10
  const editor = useEditor({
14
11
  extensions: [
15
- StarterKit,
16
- Link,
17
- HorizontalRule,
12
+ StarterKit.configure({ trailingNode: false }),
18
13
  Highlight.configure({ multicolor: true })
19
14
  ],
20
15
  content:"Add your text here. You can format your text, add links, quotes, and bullets."
@@ -26,13 +21,13 @@ const RichTextEditorMoreExtensions = (props) => {
26
21
  const ExtensionsList = [
27
22
  {
28
23
  icon: "horizontal-rule",
29
- isActive: editor.isActive("horizontalRule"),
24
+ isActive: () => editor.isActive("horizontalRule"),
30
25
  text: "Horizontal Rule",
31
26
  onclick: () => editor.chain().focus().setHorizontalRule().run(),
32
27
  },
33
28
  {
34
29
  icon: "highlighter",
35
- isActive: editor.isActive("highlight"),
30
+ isActive: () => editor.isActive("highlight"),
36
31
  text: "Highlighter",
37
32
  onclick: () => editor.chain().focus().toggleHighlight().run(),
38
33
  }
@@ -5,7 +5,7 @@ __NOTE__: In order to leverage this prop, you __must__ install the extension you
5
5
  In order to add the extension to the editor toolbar, create an array of objects (as shown in the ExtensionsList array in the example below). Each object in this array should include:
6
6
 
7
7
  `icon`: the icon to display within the toolbar dropdown (any Fontawesome icons can be used)
8
- `isActive`: sets the extension to active within the dropdown, when applicable
8
+ `isActive`: sets the extension to active within the dropdown, when applicable. Prefer a function (`() => editor.isActive(...)`) so TipTap v3 selection changes refresh the highlight (TipTap no longer re-renders `useEditor` hosts on every transaction by default)
9
9
  `text`: the label within the toolbar dropdown
10
10
  `onclick`: initializes the extension when it’s clicked within the dropdown (snytax varies with extension, see Tiptap's docs for more information)
11
11
 
@@ -2,11 +2,11 @@ The Rails rich text editor is a TipTap surface with no React. The UI (toolbar, b
2
2
 
3
3
  ### How TipTap is loaded (Rails)
4
4
 
5
- - The kit’s module script (`rich_text_editor_rails.js`) uses `import()` with **full URLs** on [esm.sh](https://esm.sh) (e.g. `@tiptap/core@2.8.0`). esm.sh resolves dependencies server-side, so **no `<script type="importmap">`** is required—this avoids conflicts when the host page already has an import map (e.g. Vite in dev, or another app map) because Firefox only applies one native map.
5
+ - The kit’s module script (`rich_text_editor_rails.js`) uses `import()` with **full URLs** on [esm.sh](https://esm.sh) (e.g. `@tiptap/core@3.0.7`). esm.sh resolves dependencies server-side, so **no `<script type="importmap">`** is required—this avoids conflicts when the host page already has an import map (e.g. Vite in dev, or another app map) because Firefox only applies one native map.
6
6
  - You do not need TipTap in your app’s npm dependencies or Gemfile for this kit; the browser loads modules from esm.sh when the page runs.
7
7
  - Ensure **CSP** allows loading scripts from `https://esm.sh` (and esm.sh’s redirected module URLs) if you use a strict `script-src` / `connect-src`.
8
8
 
9
9
  ### Relation to the React implementation
10
10
 
11
- - Same core: both use TipTap v2 on top of ProseMirror; styling lives in Playbook SCSS (`_tiptap_styles.scss`) so the editor chrome lines up between platforms.
11
+ - Same core: both use TipTap v3 on top of ProseMirror; styling lives in Playbook SCSS (`_tiptap_styles.scss`) so the editor chrome lines up between platforms.
12
12
  - Different shell: Rails uses ERB + Playbook Rails components + inline module script. React uses `RichTextEditor` / `_tiptap_editor.tsx` and TipTap wired through the bundled Playbook React package—see Advanced Default for that stack and when you need TipTap installed in your JavaScript bundle.
@@ -68,8 +68,9 @@ test('returns "Dark" class name', () => {
68
68
  // TipTap testing
69
69
  const TestAdvancedEditor = ({ toolbarOnFocus = false, ...props }) => {
70
70
  const editor = useEditor({
71
- extensions: [StarterKit],
71
+ extensions: [StarterKit.configure({ trailingNode: false })],
72
72
  content: '<p>Test content</p>',
73
+ shouldRerenderOnTransaction: true,
73
74
  })
74
75
 
75
76
  if (!editor) return null
@@ -3,7 +3,6 @@ import { fireEvent, render, waitFor } from "../utilities/test-utils";
3
3
  import { Editor } from "@tiptap/core";
4
4
  import { useEditor, EditorContent } from "@tiptap/react";
5
5
  import StarterKit from "@tiptap/starter-kit";
6
- import Link from "@tiptap/extension-link";
7
6
  import { defaultMarkdownParser } from "prosemirror-markdown";
8
7
  import { DOMSerializer } from "prosemirror-model";
9
8
 
@@ -13,11 +12,14 @@ import { createMarkdownToHTML, handleMarkdownPaste } from "./TipTap/markdownPast
13
12
 
14
13
  const kitClass = "pb_rich_text_editor_advanced_container";
15
14
  const markdownToHTML = createMarkdownToHTML(defaultMarkdownParser, DOMSerializer);
15
+ const starterKit = StarterKit.configure({ trailingNode: false });
16
16
 
17
17
  const EditorTest = (props) => {
18
18
  const editor = useEditor({
19
- extensions: [StarterKit, Link],
19
+ extensions: [starterKit],
20
20
  content: "",
21
+ // TipTap v3: keep toolbar/tests in sync with editor state without host-app config.
22
+ shouldRerenderOnTransaction: true,
21
23
  });
22
24
 
23
25
  return (
@@ -59,7 +61,7 @@ describe("TipTap list selection", () => {
59
61
  ["bulletList", "ul"],
60
62
  ["orderedList", "ol"],
61
63
  ])("excludes a heading touched only at its end boundary from a %s", (listType, tag) => {
62
- const editor = new Editor({ extensions: [StarterKit], content });
64
+ const editor = new Editor({ extensions: [starterKit], content });
63
65
  const headingEnd = editor.state.doc.child(0).nodeSize - 1;
64
66
  const paragraphEnd = headingEnd + editor.state.doc.child(1).nodeSize;
65
67
 
@@ -76,7 +78,7 @@ describe("TipTap list selection", () => {
76
78
  });
77
79
 
78
80
  test("excludes a paragraph touched only at its start boundary", () => {
79
- const editor = new Editor({ extensions: [StarterKit], content });
81
+ const editor = new Editor({ extensions: [starterKit], content });
80
82
  const firstParagraphStart = editor.state.doc.child(0).nodeSize + 1;
81
83
  const secondParagraphStart =
82
84
  editor.state.doc.child(0).nodeSize + editor.state.doc.child(1).nodeSize + 1;
@@ -94,7 +96,7 @@ describe("TipTap list selection", () => {
94
96
 
95
97
  test("separates a pasted heading and body joined by a hard break", () => {
96
98
  const editor = new Editor({
97
- extensions: [StarterKit],
99
+ extensions: [starterKit],
98
100
  content: "<p><strong>How should it work?</strong><br>Keep React and Rails visible.</p>",
99
101
  });
100
102
  const hardBreakEnd = editor.state.doc.child(0).child(1).nodeSize +
@@ -1,7 +1,8 @@
1
1
  // Rails TipTap: dynamic import() from esm.sh (no import map — avoids 2nd map ignored by Firefox vs Vite/host).
2
2
  // Idempotent: data-pb-rte-initialized / data-pb-rte-pending.
3
3
 
4
- const RTE_TIPTAP_VERSION = "2.8.0";
4
+ // Keep in sync with TipTap versions used by Playbook docs / Nitro RTE alignment (TipTap v3).
5
+ const RTE_TIPTAP_VERSION = "3.0.7";
5
6
  const RTE_TIPTAP_ESM = (pkg) => `https://esm.sh/${pkg}@${RTE_TIPTAP_VERSION}`;
6
7
  const RTE_EXTENSIONS = new Set(["underline", "text_align", "horizontal_rule", "image"]);
7
8
 
@@ -211,7 +212,6 @@ async function initPlaybookRichTextEditorRails(container) {
211
212
  try {
212
213
  const { Editor } = await import(RTE_TIPTAP_ESM("@tiptap/core"));
213
214
  const { default: StarterKit } = await import(RTE_TIPTAP_ESM("@tiptap/starter-kit"));
214
- const { default: Link } = await import(RTE_TIPTAP_ESM("@tiptap/extension-link"));
215
215
  const optionalExtensions = await loadOptionalExtensions(extensionNames);
216
216
  const neutralClipboardTags = new Set(["html", "head", "body", "meta", "div", "span", "p"]);
217
217
  const markdownSourceTags = new Set([...neutralClipboardTags, "pre", "code"]);
@@ -246,11 +246,22 @@ async function initPlaybookRichTextEditorRails(container) {
246
246
  }
247
247
  }
248
248
 
249
+ // TipTap v3 StarterKit includes Link + Underline. Keep underline opt-in via extensions prop;
250
+ // configure Link here so we do not load a second @tiptap/extension-link instance.
249
251
  const editor = new Editor({
250
252
  element: editorNode,
251
253
  extensions: [
252
- StarterKit.configure({ heading: { levels: [1, 2, 3] } }),
253
- Link.configure({ openOnClick: false, HTMLAttributes: { target: "_blank", rel: "noopener" } }),
254
+ StarterKit.configure({
255
+ heading: { levels: [1, 2, 3] },
256
+ link: {
257
+ openOnClick: false,
258
+ HTMLAttributes: { target: "_blank", rel: "noopener" },
259
+ },
260
+ // TipTap v3 enables TrailingNode by default; disable so getHTML() stays
261
+ // free of a synthetic trailing <p></p> that would dirty/persist blank lines.
262
+ trailingNode: false,
263
+ underline: false,
264
+ }),
254
265
  ...optionalExtensions,
255
266
  ],
256
267
  content: initialHtml,
@@ -2,6 +2,7 @@ import React from 'react'
2
2
  import classnames from 'classnames'
3
3
 
4
4
  import { globalProps } from '../utilities/globalProps'
5
+ import { GlobalEventProps, globalEventProps } from '../utilities/globalEventProps'
5
6
  import { buildHtmlProps } from '../utilities/props'
6
7
  import Title from '../pb_title/_title'
7
8
 
@@ -11,7 +12,7 @@ type StatValueProps = {
11
12
  id?: string,
12
13
  unit?: string,
13
14
  value: string | number,
14
- }
15
+ } & GlobalEventProps
15
16
 
16
17
  const StatValue = (props: StatValueProps): React.ReactElement => {
17
18
  const {
@@ -53,6 +54,7 @@ const StatValue = (props: StatValueProps): React.ReactElement => {
53
54
  className={classnames('pb_stat_value_kit', globalProps(props), className)}
54
55
  id={id}
55
56
  {...htmlProps}
57
+ {...globalEventProps(props)}
56
58
  >
57
59
  <div className="pb_stat_value_wrapper">
58
60
  {displayValue(value)}