playbook_ui 16.4.0.pre.rc.2 → 16.4.0.pre.rc.3

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.html.erb +1 -1
  3. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.jsx +6 -3
  4. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.html.erb +3 -3
  5. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.jsx +6 -3
  6. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.html.erb +3 -3
  7. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.jsx +6 -3
  8. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +35 -134
  9. data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_editor.tsx +51 -0
  10. data/app/pb_kits/playbook/pb_rich_text_editor/_trix_editor.tsx +206 -0
  11. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.jsx +56 -0
  12. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.md +1 -0
  13. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +13 -21
  14. data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +0 -10
  15. data/app/pb_kits/playbook/pb_rich_text_editor/inlineFocus.ts +5 -4
  16. data/dist/chunks/{_pb_line_graph-BGY7jEks.js → _pb_line_graph-BI5wY8Wj.js} +1 -1
  17. data/dist/chunks/_typeahead-8CvXJGlb.js +1 -0
  18. data/dist/chunks/{globalProps-CK2YuA9O.js → globalProps-Bn1WUHLp.js} +1 -1
  19. data/dist/chunks/{lib-DspaUdlc.js → lib-qwWYiGtH.js} +1 -1
  20. data/dist/chunks/vendor.js +4 -4
  21. data/dist/playbook-rails-react-bindings.js +1 -1
  22. data/dist/playbook-rails.js +1 -1
  23. data/lib/playbook/version.rb +1 -1
  24. metadata +9 -30
  25. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_attributes.html.erb +0 -5
  26. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_attributes.jsx +0 -15
  27. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.html.erb +0 -1
  28. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_focus.html.erb +0 -3
  29. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_focus.jsx +0 -17
  30. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.html.erb +0 -6
  31. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.jsx +0 -16
  32. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.jsx +0 -28
  33. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.md +0 -1
  34. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_preview.html.erb +0 -35
  35. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_preview.jsx +0 -45
  36. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.html.erb +0 -10
  37. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.jsx +0 -22
  38. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.md +0 -3
  39. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_simple.html.erb +0 -1
  40. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_simple.jsx +0 -13
  41. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_sticky.html.erb +0 -1
  42. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_sticky.jsx +0 -15
  43. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_templates.html.erb +0 -115
  44. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_templates.jsx +0 -42
  45. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.html.erb +0 -4
  46. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.jsx +0 -14
  47. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +0 -6
  48. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +0 -63
  49. data/dist/chunks/_typeahead-DdGKR1rQ.js +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6e2317099ce7cdcf1c23ce70c26f0a51914272dec95c7ef7529921e0c1029f5
4
- data.tar.gz: 720485d1b32fb893c00fd2167b9b2a7e636fac03df2fa6e1596fa62202803970
3
+ metadata.gz: ae3803bd5b745bfb80cc3735185a84ad54261d66a835da1430b4a9e490e99a79
4
+ data.tar.gz: b43613542fe8f44fb33f03ceefb5e2420ad38e371e4489fc4085187cdcd12361
5
5
  SHA512:
6
- metadata.gz: 121b5a84eff2012cfa17154a770deeae351f475f2cca5768f2bbc3b71b1b71bd6a06488d002289e196b09c4f6d8f78b06441af3106f4382e368f997bb8a90178
7
- data.tar.gz: 104dec1624b69165f12ccffe0f6312981eaf6f7503eddce88889626bf28ea2e6a27fb6db4e60bf75e01afba81867f47b6a0ea5e6ba61970e661ce91c35e406cc
6
+ metadata.gz: 9d563bf3c9b4ce0829a5dca7747f5365507a24fa0bfeed2a6caaef00ab51f52aeae7df2664060159995d041bced0a521bd0c9fbf0b2bb907386d0e8071184988
7
+ data.tar.gz: ad84d871ff6f43acdf3da0898888ec99e6b173bc68c71334932dc3a59f938e3bac7f4023028727ca2e6462c5a607c02dc254c768e740d5c2b7f03862edbaffaa
@@ -28,7 +28,7 @@
28
28
  <% end %>
29
29
  <%= pb_rails("dialog/dialog_body") do %>
30
30
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
31
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
31
+ <%= pb_rails("textarea", props: {id: "default"}) %>
32
32
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
33
33
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
34
34
  <%= pb_rails("dropdown", props: {options: options, autocomplete: true}) %>
@@ -4,7 +4,7 @@ import Body from '../../pb_body/_body'
4
4
  import Button from '../../pb_button/_button'
5
5
  import Caption from '../../pb_caption/_caption'
6
6
  import Dialog from '../../pb_dialog/_dialog'
7
- import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
7
+ import Textarea from '../../pb_textarea/_textarea'
8
8
  import Typeahead from '../../pb_typeahead/_typeahead'
9
9
 
10
10
  const DialogCompound = () => {
@@ -25,8 +25,11 @@ const DialogCompound = () => {
25
25
  <Body>{'What do you need us to take care of?'}</Body>
26
26
  </Dialog.Header>
27
27
  <Dialog.Body>
28
- <Caption marginBottom="xs">{'Description'}</Caption>
29
- <RichTextEditor />
28
+ <Textarea
29
+ id="default-example-1"
30
+ label="Description"
31
+ rows={4}
32
+ />
30
33
  <br />
31
34
  <Caption>
32
35
  {
@@ -12,7 +12,7 @@
12
12
  <% end %>
13
13
  <%= pb_rails("dialog/dialog_body") do %>
14
14
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
15
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
15
+ <%= pb_rails("textarea", props: {id: "default-7"}) %>
16
16
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
17
17
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
18
18
 
@@ -31,7 +31,7 @@
31
31
  <% end %>
32
32
  <%= pb_rails("dialog/dialog_body") do %>
33
33
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
34
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
34
+ <%= pb_rails("textarea", props: {id: "default-8"}) %>
35
35
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
36
36
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
37
37
 
@@ -49,7 +49,7 @@
49
49
  <% end %>
50
50
  <%= pb_rails("dialog/dialog_body") do %>
51
51
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
52
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
52
+ <%= pb_rails("textarea", props: {id: "default-9"}) %>
53
53
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
54
54
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
55
55
 
@@ -5,7 +5,7 @@ import Button from '../../pb_button/_button'
5
5
  import Dialog from '../../pb_dialog/_dialog'
6
6
  import Flex from '../../pb_flex/_flex'
7
7
  import Caption from '../../pb_caption/_caption'
8
- import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
8
+ import Textarea from '../../pb_textarea/_textarea'
9
9
  import Typeahead from '../../pb_typeahead/_typeahead'
10
10
 
11
11
  const useDialog = (visible = false) => {
@@ -77,8 +77,11 @@ const DialogFullHeight = () => {
77
77
  <Body>{title}</Body>
78
78
  </Dialog.Header>
79
79
  <Dialog.Body>
80
- <Caption marginBottom="xs">{"Description"}</Caption>
81
- <RichTextEditor />
80
+ <Textarea
81
+ id="default-example-1"
82
+ label="Description"
83
+ rows={4}
84
+ />
82
85
  <br />
83
86
  <Caption>
84
87
  {
@@ -13,7 +13,7 @@
13
13
  <% end %>
14
14
  <%= pb_rails("dialog/dialog_body") do %>
15
15
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
16
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
16
+ <%= pb_rails("textarea", props: {id: "default-2"}) %>
17
17
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
18
18
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
19
19
  <% end %>
@@ -32,7 +32,7 @@
32
32
  <% end %>
33
33
  <%= pb_rails("dialog/dialog_body") do %>
34
34
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
35
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
35
+ <%= pb_rails("textarea", props: {id: "default-3"}) %>
36
36
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
37
37
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
38
38
  <% end %>
@@ -50,7 +50,7 @@
50
50
  <% end %>
51
51
  <%= pb_rails("dialog/dialog_body") do %>
52
52
  <%= pb_rails("caption", props: { text: "Description", margin_bottom: "xs" }) %>
53
- <%= pb_rails("rich_text_editor", props: {id: "default", value: "Add your text here"}) %>
53
+ <%= pb_rails("textarea", props: {id: "default-4"}) %>
54
54
  <%= pb_rails("caption", props: { text: "Type in a word or term too help find tickets later. ex. training, phone setup, hr", margin_bottom: "xs", margin_top: "sm" }) %>
55
55
  <%= pb_rails("typeahead", props: { placeholder: "Tags.."}) %>
56
56
  <% end %>
@@ -5,7 +5,7 @@ import Button from '../../pb_button/_button'
5
5
  import Dialog from '../../pb_dialog/_dialog'
6
6
  import Flex from '../../pb_flex/_flex'
7
7
  import Caption from '../../pb_caption/_caption'
8
- import RichTextEditor from '../../pb_rich_text_editor/_rich_text_editor'
8
+ import Textarea from "../../pb_textarea/_textarea";
9
9
  import Typeahead from '../../pb_typeahead/_typeahead'
10
10
 
11
11
  const useDialog = (visible = false) => {
@@ -76,8 +76,11 @@ const DialogFullHeightPlacement = () => {
76
76
  <Body>{title}</Body>
77
77
  </Dialog.Header>
78
78
  <Dialog.Body>
79
- <Caption marginBottom="xs">{"Description"}</Caption>
80
- <RichTextEditor />
79
+ <Textarea
80
+ id={`default-example-2-${index}`}
81
+ label="Description"
82
+ rows={4}
83
+ />
81
84
  <br />
82
85
  <Caption>
83
86
  {
@@ -1,33 +1,12 @@
1
1
  import React, { useEffect, useState, useRef } from 'react'
2
2
  import classnames from 'classnames'
3
- import { TrixEditor } from 'react-trix'
4
3
 
5
- import inlineFocus from './inlineFocus'
6
- import useFocus from './useFocus'
7
4
  import Caption from '../pb_caption/_caption'
8
5
  import colors from '../tokens/exports/_colors.module.scss'
9
6
  import { globalProps, GlobalProps } from '../utilities/globalProps'
10
7
  import { buildAriaProps, buildDataProps, noop, buildHtmlProps } from '../utilities/props'
11
-
12
- import Trix from 'trix'
13
- import './_dedupe_trix_toolbar'
14
-
15
- Trix.config.textAttributes.inlineCode = {
16
- tagName: 'code',
17
- inheritable: true,
18
- }
19
-
20
- import EditorToolbar from './TipTap/Toolbar'
21
-
22
- type Editor = {
23
- attributeIsActive?: ([any]: string) => boolean,
24
- element?: HTMLElement,
25
- getSelectedDocument?: () => any,
26
- getSelectedRange?: () => Array<number>,
27
- insertHTML?: ([any]: string) => void,
28
- loadHTML?: ([any]: string) => void,
29
- setSelectedRange?: (range: Array<number>) => void,
30
- }
8
+ import TipTapEditor from './_tiptap_editor'
9
+ import TrixTextEditor from './_trix_editor'
31
10
 
32
11
  type RichTextEditorProps = {
33
12
  aria?: { [key: string]: string },
@@ -55,6 +34,8 @@ type RichTextEditorProps = {
55
34
  template: string,
56
35
  value?: string,
57
36
  maxWidth?: string
37
+ TrixEditor?: React.ComponentType<any>,
38
+ trixInstance?: any,
58
39
  } & GlobalProps
59
40
 
60
41
  const RichTextEditor = (props: RichTextEditorProps): React.ReactElement => {
@@ -84,50 +65,19 @@ const RichTextEditor = (props: RichTextEditorProps): React.ReactElement => {
84
65
  maxWidth = "md",
85
66
  requiredIndicator = false,
86
67
  label,
68
+ TrixEditor,
69
+ trixInstance: trixInstance = undefined,
87
70
  } = props
88
71
 
89
72
  const ariaProps = buildAriaProps(aria),
90
73
  dataProps = buildDataProps(data),
91
- [editor, setEditor] = useState<Editor>(),
92
74
  [showToolbarOnFocus, setShowToolbarOnFocus] = useState(false),
93
75
  containerRef = useRef<HTMLDivElement>(null)
94
76
 
95
77
  const htmlProps = buildHtmlProps(htmlOptions)
96
78
 
97
79
  const fieldId = id ? (id as string) : null
98
- const labelElementId = fieldId ? `${fieldId}-label` : null
99
-
100
- const handleOnEditorReady = (editorInstance: Editor) => {
101
- setEditor(editorInstance)
102
-
103
- setTimeout(() => {
104
- const oldId = editorInstance.element?.getAttribute("input")
105
- if (!oldId) return
106
-
107
- const hiddenInput = document.getElementById(oldId) as HTMLElement | null
108
- if (!hiddenInput) return
109
-
110
- const hiddenInputId = (inputOptions.id as string) || oldId
111
-
112
- if (hiddenInputId !== oldId) {
113
- hiddenInput.id = hiddenInputId
114
- editorInstance.element?.setAttribute("input", hiddenInputId)
115
- }
116
-
117
- if (inputOptions.name) {
118
- hiddenInput.setAttribute("name", inputOptions.name as string)
119
- }
120
-
121
- const editorDomId = (id as string) || `${hiddenInputId}_trix`
122
- const trixLabelId = ((id as string) || hiddenInputId) + "-label"
123
-
124
- if (label) {
125
- editorInstance.element?.setAttribute("aria-labelledby", trixLabelId)
126
- }
127
- editorInstance.element!.id = editorDomId
128
- })
129
- }
130
-
80
+ const labelElementId = fieldId ? `${fieldId}-label` : undefined
131
81
  useEffect(() => {
132
82
  if (!advancedEditor || !fieldId || !labelElementId) return
133
83
 
@@ -139,34 +89,6 @@ const RichTextEditor = (props: RichTextEditorProps): React.ReactElement => {
139
89
  dom.setAttribute("aria-multiline", "true")
140
90
  }, [advancedEditor, fieldId, labelElementId])
141
91
 
142
- // DOM manipulation must wait for editor to be ready
143
- if (editor && editor.element) {
144
- const toolbarElement = editor.element.parentElement.querySelector('trix-toolbar') as HTMLElement,
145
- blockCodeButton = toolbarElement.querySelector('[data-trix-attribute=code]') as HTMLElement
146
-
147
- // replace default trix "block code" button with "inline code" button
148
- let inlineCodeButton = toolbarElement.querySelector('[data-trix-attribute=inlineCode]') as HTMLElement
149
- if (!inlineCodeButton) {
150
- inlineCodeButton = blockCodeButton.cloneNode(true) as HTMLElement
151
- blockCodeButton.hidden = true
152
- // set button attributes
153
- inlineCodeButton.dataset.trixAttribute = 'inlineCode'
154
- blockCodeButton.insertAdjacentElement('afterend', inlineCodeButton)
155
- }
156
-
157
- if (toolbarBottom) editor.element.after(toolbarElement)
158
-
159
- focus
160
- ? (document.addEventListener('trix-focus', useFocus),
161
- document.addEventListener('trix-blur', useFocus),
162
- // eslint-disable-next-line react-hooks/rules-of-hooks
163
- useFocus())
164
- : null
165
-
166
- document.addEventListener('trix-focus', inlineFocus)
167
- document.addEventListener('trix-blur', inlineFocus)
168
- }
169
-
170
92
  //===========focus prop with advanced editor=================
171
93
  const isClickInPopover = (event: Event): boolean => {
172
94
  return !!(event.target as Element).closest('.pb_tiptap_toolbar_dropdown_popover')
@@ -203,26 +125,6 @@ const RichTextEditor = (props: RichTextEditorProps): React.ReactElement => {
203
125
 
204
126
  //============= end focus prop with advanced editor=================
205
127
 
206
- useEffect(() => {
207
- if (!editor || !template) return
208
- editor.loadHTML('')
209
- editor.setSelectedRange([0, 0])
210
- editor.insertHTML(template)
211
- }, [editor, template])
212
-
213
- useEffect(() => {
214
- if (!editor?.element) return
215
- editor.element.addEventListener('click', ({ target }: Event) => {
216
- const trixEditorContainer = (target as Element).closest('.pb_rich_text_editor_kit')
217
- if (!trixEditorContainer) return
218
-
219
- const anchorElement = (target as Element).closest('a')
220
- if (!anchorElement) return
221
-
222
- if (anchorElement.hasAttribute('href')) window.open(anchorElement.href)
223
- })
224
- }, [editor])
225
-
226
128
  // Generate CSS classes
227
129
  const css = classnames(
228
130
  'pb_rich_text_editor_kit',
@@ -279,35 +181,34 @@ const RichTextEditor = (props: RichTextEditorProps): React.ReactElement => {
279
181
 
280
182
  </label>
281
183
  )}
282
- {
283
- advancedEditor ? (
284
- <div
285
- className={classnames(
286
- "pb_rich_text_editor_advanced_container",
287
- { [`input_height_${inputHeight}`]: !!inputHeight,[`input_min_height_${inputMinHeight}`]: !!inputMinHeight ,["toolbar-active"]: shouldShowToolbar }
288
- )}
289
- >
290
- {shouldShowToolbar && (
291
- <EditorToolbar editor={advancedEditor}
292
- extensions={extensions}
293
- simple={simple}
294
- sticky={sticky}
295
- />
296
- )}
297
- { children }
298
- </div>
299
- ) : (
300
- <TrixEditor
301
- className=""
302
- fileParamName={name}
303
- mergeTags={[]}
304
- onChange={onChange}
305
- onEditorReady={handleOnEditorReady}
306
- placeholder={placeholder}
307
- value={value}
308
- />
309
- )
310
- }
184
+ {advancedEditor ? (
185
+ <TipTapEditor
186
+ editor={advancedEditor}
187
+ extensions={extensions}
188
+ inputHeight={inputHeight}
189
+ inputMinHeight={inputMinHeight}
190
+ shouldShowToolbar={shouldShowToolbar}
191
+ simple={simple}
192
+ sticky={sticky}
193
+ >
194
+ {children}
195
+ </TipTapEditor>
196
+ ) : (
197
+ <TrixTextEditor
198
+ TrixEditor={TrixEditor}
199
+ focus={focus}
200
+ id={id}
201
+ inputOptions={inputOptions}
202
+ label={label}
203
+ name={name}
204
+ onChange={onChange}
205
+ placeholder={placeholder}
206
+ template={template}
207
+ toolbarBottom={toolbarBottom}
208
+ trixInstance={trixInstance}
209
+ value={value}
210
+ />
211
+ )}
311
212
  </div>
312
213
  )
313
214
  }
@@ -0,0 +1,51 @@
1
+ import React from 'react'
2
+ import classnames from 'classnames'
3
+
4
+ import EditorToolbar from './TipTap/Toolbar'
5
+
6
+ type TipTapEditorProps = {
7
+ children?: React.ReactNode | React.ReactNode[],
8
+ editor: unknown,
9
+ extensions?: { [key: string]: string }[],
10
+ inputHeight?: 'sm' | 'md' | 'lg',
11
+ inputMinHeight?: 'sm' | 'md' | 'lg',
12
+ shouldShowToolbar: boolean,
13
+ simple?: boolean,
14
+ sticky?: boolean,
15
+ }
16
+
17
+ const TipTapEditor = ({
18
+ children,
19
+ editor,
20
+ extensions,
21
+ inputHeight,
22
+ inputMinHeight,
23
+ shouldShowToolbar,
24
+ simple = false,
25
+ sticky = false,
26
+ }: TipTapEditorProps): React.ReactElement => {
27
+ return (
28
+ <div
29
+ className={classnames(
30
+ 'pb_rich_text_editor_advanced_container',
31
+ {
32
+ [`input_height_${inputHeight}`]: !!inputHeight,
33
+ [`input_min_height_${inputMinHeight}`]: !!inputMinHeight,
34
+ 'toolbar-active': shouldShowToolbar,
35
+ }
36
+ )}
37
+ >
38
+ {shouldShowToolbar && (
39
+ <EditorToolbar
40
+ editor={editor}
41
+ extensions={extensions}
42
+ simple={simple}
43
+ sticky={sticky}
44
+ />
45
+ )}
46
+ {children}
47
+ </div>
48
+ )
49
+ }
50
+
51
+ export default TipTapEditor
@@ -0,0 +1,206 @@
1
+ import React, { useEffect, useState } from 'react'
2
+
3
+ import inlineFocus from './inlineFocus'
4
+ import applyFocusState from './useFocus'
5
+ import './_dedupe_trix_toolbar'
6
+
7
+ type Editor = {
8
+ element?: HTMLElement,
9
+ insertHTML?: (html: string) => void,
10
+ loadHTML?: (html: string) => void,
11
+ setSelectedRange?: (range: Array<number>) => void,
12
+ }
13
+
14
+ type TrixConfig = {
15
+ textAttributes?: {
16
+ inlineCode?: {
17
+ tagName: string,
18
+ inheritable: boolean,
19
+ }
20
+ }
21
+ }
22
+
23
+ type TrixInstance = {
24
+ config: TrixConfig,
25
+ }
26
+
27
+ type TrixEditorComponentProps = {
28
+ className: string,
29
+ fileParamName?: string,
30
+ mergeTags: unknown[],
31
+ onChange: (html: string, text: string) => void,
32
+ onEditorReady: (editorInstance: Editor) => void,
33
+ placeholder?: string,
34
+ value?: string,
35
+ }
36
+
37
+ type TrixTextEditorProps = {
38
+ TrixEditor?: React.ComponentType<TrixEditorComponentProps>,
39
+ focus?: boolean,
40
+ id?: string,
41
+ inputOptions?: { [key: string]: string | number | boolean | (() => void) },
42
+ label?: string,
43
+ name?: string,
44
+ onChange: (html: string, text: string) => void,
45
+ placeholder?: string,
46
+ template: string,
47
+ toolbarBottom?: boolean,
48
+ trixInstance?: TrixInstance,
49
+ value?: string,
50
+ }
51
+
52
+ const TrixTextEditor = ({
53
+ TrixEditor,
54
+ focus = false,
55
+ id,
56
+ inputOptions = {},
57
+ label,
58
+ name,
59
+ onChange,
60
+ placeholder,
61
+ template,
62
+ toolbarBottom = false,
63
+ trixInstance = undefined,
64
+ value = '',
65
+ }: TrixTextEditorProps): React.ReactElement => {
66
+ const [editor, setEditor] = useState<Editor>()
67
+
68
+ useEffect(() => {
69
+ const textAttributes = trixInstance?.config?.textAttributes
70
+ if (!textAttributes) return
71
+
72
+ textAttributes.inlineCode = {
73
+ tagName: 'code',
74
+ inheritable: true,
75
+ }
76
+ }, [trixInstance])
77
+
78
+ const handleOnEditorReady = (editorInstance: Editor) => {
79
+ setEditor(editorInstance)
80
+
81
+ setTimeout(() => {
82
+ const oldId = editorInstance.element?.getAttribute('input')
83
+ if (!oldId) return
84
+
85
+ const hiddenInput = document.getElementById(oldId) as HTMLElement | null
86
+ if (!hiddenInput) return
87
+
88
+ const hiddenInputId = (inputOptions.id as string) || oldId
89
+
90
+ if (hiddenInputId !== oldId) {
91
+ hiddenInput.id = hiddenInputId
92
+ editorInstance.element?.setAttribute('input', hiddenInputId)
93
+ }
94
+
95
+ if (inputOptions.name) {
96
+ hiddenInput.setAttribute('name', inputOptions.name as string)
97
+ }
98
+
99
+ const editorDomId = (id as string) || `${hiddenInputId}_trix`
100
+ const trixLabelId = ((id as string) || hiddenInputId) + '-label'
101
+
102
+ if (label) {
103
+ editorInstance.element?.setAttribute('aria-labelledby', trixLabelId)
104
+ }
105
+ if (editorInstance.element) {
106
+ editorInstance.element.id = editorDomId
107
+ }
108
+ })
109
+ }
110
+
111
+ useEffect(() => {
112
+ if (!editor || !editor.element) return
113
+
114
+ const toolbarElement = editor.element.parentElement?.querySelector('trix-toolbar') as HTMLElement | null
115
+ if (!toolbarElement) return
116
+
117
+ const blockCodeButton = toolbarElement.querySelector('[data-trix-attribute=code]') as HTMLElement | null
118
+ if (!blockCodeButton) return
119
+
120
+ let inlineCodeButton = toolbarElement.querySelector('[data-trix-attribute=inlineCode]') as HTMLElement | null
121
+ if (!inlineCodeButton) {
122
+ inlineCodeButton = blockCodeButton.cloneNode(true) as HTMLElement
123
+ blockCodeButton.hidden = true
124
+ inlineCodeButton.dataset.trixAttribute = 'inlineCode'
125
+ blockCodeButton.insertAdjacentElement('afterend', inlineCodeButton)
126
+ }
127
+
128
+ if (toolbarBottom) {
129
+ editor.element.after(toolbarElement)
130
+ }
131
+ }, [editor, toolbarBottom])
132
+
133
+ useEffect(() => {
134
+ if (!focus) return
135
+
136
+ document.addEventListener('trix-focus', applyFocusState)
137
+ document.addEventListener('trix-blur', applyFocusState)
138
+ applyFocusState()
139
+
140
+ return () => {
141
+ document.removeEventListener('trix-focus', applyFocusState)
142
+ document.removeEventListener('trix-blur', applyFocusState)
143
+ }
144
+ }, [focus])
145
+
146
+ useEffect(() => {
147
+ document.addEventListener('trix-focus', inlineFocus)
148
+ document.addEventListener('trix-blur', inlineFocus)
149
+
150
+ return () => {
151
+ document.removeEventListener('trix-focus', inlineFocus)
152
+ document.removeEventListener('trix-blur', inlineFocus)
153
+ }
154
+ }, [])
155
+
156
+ useEffect(() => {
157
+ if (!editor || !template) return
158
+ editor.loadHTML && editor.loadHTML('')
159
+ editor.setSelectedRange && editor.setSelectedRange([0, 0])
160
+ editor.insertHTML && editor.insertHTML(template)
161
+ }, [editor, template])
162
+
163
+ useEffect(() => {
164
+ if (!editor?.element) return
165
+
166
+ const clickHandler = ({ target }: Event) => {
167
+ const trixEditorContainer = (target as Element).closest('.pb_rich_text_editor_kit')
168
+ if (!trixEditorContainer) return
169
+
170
+ const anchorElement = (target as Element).closest('a') as HTMLAnchorElement | null
171
+ if (!anchorElement) return
172
+
173
+ if (anchorElement.hasAttribute('href')) window.open(anchorElement.href)
174
+ }
175
+
176
+ editor.element.addEventListener('click', clickHandler)
177
+
178
+ return () => {
179
+ editor.element?.removeEventListener('click', clickHandler)
180
+ }
181
+ }, [editor])
182
+
183
+ if (!TrixEditor) {
184
+ return (
185
+ <div style={{ color: 'red', padding: '1em', border: '1px solid #f00', background: '#fff0f0' }}>
186
+ <strong>Trix Editor is not available.</strong>
187
+ <br />
188
+ Please import <code>TrixEditor</code> from <code>react-trix</code> and pass it as a prop to <code>RichTextEditor</code>.
189
+ <br />
190
+ <pre>{`import { TrixEditor } from 'react-trix';\n<RichTextEditor TrixEditor={TrixEditor} ... />`}</pre>
191
+ </div>
192
+ )
193
+ }
194
+
195
+ return React.createElement(TrixEditor, {
196
+ className: '',
197
+ fileParamName: name,
198
+ mergeTags: [],
199
+ onChange,
200
+ onEditorReady: handleOnEditorReady,
201
+ placeholder,
202
+ value,
203
+ })
204
+ }
205
+
206
+ export default TrixTextEditor