@antscorp/antsomi-ui 1.3.7-beta.3 → 1.3.7-beta.31

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 (122) hide show
  1. package/es/components/atoms/InputDynamic/InputDynamic.js +3 -3
  2. package/es/components/atoms/index.d.ts +0 -2
  3. package/es/components/atoms/index.js +0 -2
  4. package/es/components/molecules/AddDynamicContent/AddDynamicContent.js +28 -2
  5. package/es/components/molecules/AddDynamicContent/constants.d.ts +2 -0
  6. package/es/components/molecules/AddDynamicContent/constants.js +2 -0
  7. package/es/components/molecules/EmojiPopover/EmojiPopover.d.ts +1 -0
  8. package/es/components/molecules/EmojiPopover/EmojiPopover.js +2 -2
  9. package/es/components/molecules/EmojiPopover/styled.d.ts +2 -2
  10. package/es/components/molecules/EmojiPopover/styled.js +1 -1
  11. package/es/components/molecules/SearchPopover/SearchPopover.js +2 -2
  12. package/es/components/molecules/SearchPopover/styled.d.ts +12 -1
  13. package/es/components/molecules/SearchPopover/styled.js +1 -2
  14. package/es/components/molecules/SearchPopover/types.d.ts +4 -3
  15. package/es/components/molecules/TagifyInput/TagifyInput.js +55 -19
  16. package/es/components/molecules/TagifyInput/types.d.ts +20 -1
  17. package/es/components/molecules/TagifyInput/utils.d.ts +6 -1
  18. package/es/components/molecules/TagifyInput/utils.js +19 -0
  19. package/es/components/molecules/VirtualizedMenu/VirtualizedMenu.d.ts +1 -0
  20. package/es/components/molecules/VirtualizedMenu/components/Item/Item.js +3 -7
  21. package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.d.ts +10 -10
  22. package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.js +46 -327
  23. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/index.d.ts +9 -0
  24. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/index.js +5 -0
  25. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useFocusManagement.d.ts +23 -0
  26. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useFocusManagement.js +81 -0
  27. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useItemInteraction.d.ts +24 -0
  28. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useItemInteraction.js +32 -0
  29. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useKeyboardNavigation.d.ts +26 -0
  30. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useKeyboardNavigation.js +93 -0
  31. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useTreeState.d.ts +24 -0
  32. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useTreeState.js +94 -0
  33. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useVisibleItems.d.ts +7 -0
  34. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useVisibleItems.js +132 -0
  35. package/es/components/molecules/VirtualizedMenu/styled.js +14 -3
  36. package/es/components/molecules/VirtualizedMenu/types.d.ts +1 -0
  37. package/es/components/molecules/VirtualizedMenu/utils.d.ts +1 -0
  38. package/es/components/molecules/VirtualizedMenu/utils.js +1 -0
  39. package/es/components/organism/ActivityTimeline/ActivityTimeline.js +3 -3
  40. package/es/components/organism/ActivityTimeline/constants.d.ts +9 -9
  41. package/es/components/organism/ActivityTimeline/constants.js +3 -3
  42. package/es/components/organism/ActivityTimeline/index.d.ts +530 -1
  43. package/es/components/organism/ActivityTimeline/index.js +9 -1
  44. package/es/components/organism/ActivityTimeline/utils.js +7 -6
  45. package/es/components/organism/TextEditor/TextEditor.d.ts +9 -2
  46. package/es/components/organism/TextEditor/TextEditor.js +181 -40
  47. package/es/components/organism/TextEditor/constants.d.ts +9 -0
  48. package/es/components/organism/TextEditor/constants.js +66 -0
  49. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu-plugin.d.ts +6 -128
  50. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu-plugin.js +7 -292
  51. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu.d.ts +1 -1
  52. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu.js +4 -0
  53. package/es/components/organism/TextEditor/extensions/Link.js +3 -3
  54. package/es/components/organism/TextEditor/extensions/ListItem.d.ts +10 -0
  55. package/es/components/organism/TextEditor/extensions/ListItem.js +93 -0
  56. package/es/components/organism/TextEditor/extensions/SmartTag.d.ts +5 -6
  57. package/es/components/organism/TextEditor/extensions/SmartTag.js +94 -2
  58. package/es/components/organism/TextEditor/hooks/useColorSet.js +7 -0
  59. package/es/components/organism/TextEditor/hooks/useMarkTracking.js +2 -1
  60. package/es/components/organism/TextEditor/index.d.ts +7 -5
  61. package/es/components/organism/TextEditor/index.scss +4 -7
  62. package/es/components/organism/TextEditor/provider.d.ts +1 -0
  63. package/es/components/organism/TextEditor/provider.js +6 -3
  64. package/es/components/organism/TextEditor/store.d.ts +11 -4
  65. package/es/components/organism/TextEditor/store.js +22 -2
  66. package/es/components/organism/TextEditor/stories/WithOldDynAndLink/froala-legacy-format.settings.json +95 -0
  67. package/es/components/organism/TextEditor/stories/WithOldDynAndLink/shared.d.ts +111 -0
  68. package/es/components/organism/TextEditor/stories/WithOldDynAndLink/shared.js +82 -0
  69. package/es/components/organism/TextEditor/stories/shared.d.ts +64 -0
  70. package/es/components/organism/TextEditor/stories/shared.js +57 -0
  71. package/es/components/organism/TextEditor/styled.d.ts +1 -1
  72. package/es/components/organism/TextEditor/styled.js +1 -0
  73. package/es/components/organism/TextEditor/types.d.ts +153 -9
  74. package/es/components/organism/TextEditor/types.js +1 -0
  75. package/es/components/organism/TextEditor/ui/BubbleMenu/BubbleMenu.d.ts +2 -1
  76. package/es/components/organism/TextEditor/ui/BubbleMenu/BubbleMenu.js +80 -51
  77. package/es/components/organism/TextEditor/ui/Emoji/EmojiList.js +1 -1
  78. package/es/components/organism/TextEditor/ui/Emoji/suggestion.d.ts +1 -1
  79. package/es/components/organism/TextEditor/ui/Emoji/suggestion.js +2 -2
  80. package/es/components/organism/TextEditor/ui/FontPopover/FontPopover.js +12 -2
  81. package/es/components/organism/TextEditor/ui/LinkInsertForm/LinkInsertForm.d.ts +16 -0
  82. package/es/components/organism/TextEditor/ui/LinkInsertForm/LinkInsertForm.js +61 -0
  83. package/es/components/organism/TextEditor/ui/LinkInsertForm/index.d.ts +2 -0
  84. package/es/components/organism/TextEditor/ui/LinkInsertForm/index.js +1 -0
  85. package/es/components/organism/TextEditor/ui/LinkPopover/LinkPopover.d.ts +9 -0
  86. package/es/components/organism/TextEditor/ui/LinkPopover/LinkPopover.js +126 -0
  87. package/es/components/organism/TextEditor/ui/LinkPopover/index.d.ts +2 -0
  88. package/es/components/organism/TextEditor/ui/LinkPopover/index.js +1 -0
  89. package/es/components/organism/TextEditor/ui/Popover/Popover.js +1 -1
  90. package/es/components/organism/TextEditor/ui/TextAlignSelect/TextAlignSelect.js +1 -3
  91. package/es/components/organism/TextEditor/ui/Toolbar/{Toolbar.d.ts → FormattingToolbar.d.ts} +3 -4
  92. package/es/components/organism/TextEditor/ui/Toolbar/FormattingToolbar.js +85 -0
  93. package/es/components/organism/TextEditor/ui/Toolbar/LinkPreviewToolbar.d.ts +10 -0
  94. package/es/components/organism/TextEditor/ui/Toolbar/LinkPreviewToolbar.js +39 -0
  95. package/es/components/organism/TextEditor/ui/Toolbar/actions/EmojiAction.js +4 -3
  96. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontFamilyAction.d.ts +1 -0
  97. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontFamilyAction.js +2 -2
  98. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontSizeAction.js +6 -1
  99. package/es/components/organism/TextEditor/ui/Toolbar/index.d.ts +2 -1
  100. package/es/components/organism/TextEditor/ui/Toolbar/index.js +2 -1
  101. package/es/components/organism/TextEditor/utils/documentState.d.ts +14 -0
  102. package/es/components/organism/TextEditor/utils/documentState.js +25 -0
  103. package/es/components/organism/TextEditor/utils/htmlProcessing.js +60 -0
  104. package/es/components/organism/TextEditor/utils/link.d.ts +10 -1
  105. package/es/components/organism/TextEditor/utils/link.js +161 -7
  106. package/es/components/organism/TextEditor/utils/menu.js +2 -1
  107. package/es/components/organism/TextEditor/utils/selection.js +3 -2
  108. package/es/components/organism/TextEditor/utils/smartTag.js +2 -1
  109. package/es/components/organism/index.d.ts +1 -1
  110. package/es/hooks/index.d.ts +1 -1
  111. package/es/hooks/index.js +1 -1
  112. package/es/services/MediaTemplateDesign/UploadFile/index.js +4 -4
  113. package/es/types/index.d.ts +1 -1
  114. package/es/types/index.js +1 -1
  115. package/es/utils/common.d.ts +1 -1
  116. package/es/utils/common.js +3 -3
  117. package/package.json +23 -35
  118. package/es/components/organism/TextEditor/ui/BubbleToolbar/BubbleToolbar.d.ts +0 -1
  119. package/es/components/organism/TextEditor/ui/BubbleToolbar/BubbleToolbar.js +0 -1
  120. package/es/components/organism/TextEditor/ui/BubbleToolbar/index.d.ts +0 -0
  121. package/es/components/organism/TextEditor/ui/BubbleToolbar/index.js +0 -1
  122. package/es/components/organism/TextEditor/ui/Toolbar/Toolbar.js +0 -39
@@ -1,16 +1,16 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ANTSOMI_COMPONENT_PREFIX_CLS } from '@antscorp/antsomi-ui/es/constants';
3
- import { useDeepCompareMemo } from '@antscorp/antsomi-ui/es/hooks';
3
+ import { useDeepCompareEffect, useDeepCompareMemo } from '@antscorp/antsomi-ui/es/hooks';
4
4
  import SubScript from '@tiptap/extension-subscript';
5
5
  import Superscript from '@tiptap/extension-superscript';
6
6
  import TextAlign from '@tiptap/extension-text-align';
7
7
  import { TextStyleKit } from '@tiptap/extension-text-style';
8
- import { Selection } from '@tiptap/extensions';
8
+ import { Selection, Focus } from '@tiptap/extensions';
9
9
  import { useEditor } from '@tiptap/react';
10
10
  import StarterKit from '@tiptap/starter-kit';
11
11
  import clsx from 'clsx';
12
- import { isBoolean, omit } from 'lodash';
13
- import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, } from 'react';
12
+ import { isBoolean } from 'lodash';
13
+ import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, } from 'react';
14
14
  import { useDebouncedCallback } from 'use-debounce';
15
15
  import { DEFAULT_TEXT_STYLE } from './constants';
16
16
  import { BackgroundColor } from './extensions/BackgroundColor';
@@ -23,29 +23,53 @@ import { Indent } from './extensions/Indent';
23
23
  import { LetterSpacing } from './extensions/LetterSpacing';
24
24
  import { LineHeight } from './extensions/LineHeight';
25
25
  import { CustomLink } from './extensions/Link';
26
+ import { CustomListItem } from './extensions/ListItem';
26
27
  import { CustomOrderedList } from './extensions/OrderedList';
27
28
  import { SmartTag } from './extensions/SmartTag';
28
29
  import { TextTransform } from './extensions/TextTransform';
29
30
  import { CustomUnorderedList } from './extensions/UnorderedList';
30
31
  import { ClearFormatting } from './extensions/ClearFormatting';
31
- import { useTextEditorStore } from './provider';
32
+ import { TextEditorProvider, useTextEditorStore, } from './provider';
32
33
  import { StyledEditorContent } from './styled';
34
+ import { isSmartTagNode, } from './types';
33
35
  import { emojiSuggestion } from './ui/Emoji';
34
- import { Toolbar } from './ui/Toolbar';
35
- import { analyzeFont, defaultShouldShowBubbleMenu, handleLinkAction, handleSmartTagAction, isShowLinkToolbar, safeParseHTMLContent, htmlSerializerForOutput, } from './utils';
36
+ import { LinkPreviewToolbar, FormattingToolbar } from './ui/Toolbar';
37
+ import { analyzeFont, defaultShouldShowBubbleMenu, getAllFullLinkGroups, handleLinkAction, handleSmartTagAction, isShowLinkToolbar, safeParseHTMLContent, htmlSerializerForOutput, } from './utils';
36
38
  import { BubbleMenu } from './ui/BubbleMenu';
37
- export const TextEditor = memo(forwardRef((props, ref) => {
38
- const { id, className, config, editable = true, initialContent = '', dataAttributes, onUpdateDebounced = 400, defaultTextStyle: defaultTextStyleProp, linkHandler: outerLinkHandler, smartTagHandler: outerSmartTagHandler, bubbleMenuProps, style, render, onUpdate, onFocus, onChangeFont, } = props;
39
- const isShowBubbleMenu = useTextEditorStore(state => state.isShowBubbleMenu);
39
+ import { LinkPopover } from './ui/LinkPopover';
40
+ const TextEditorInternal = memo(forwardRef((props, ref) => {
41
+ const { id, className, config, editable = true, initialContent = '', dataAttributes, onUpdateDebounced = 300, defaultTextStyle: defaultTextStyleProp, linkHandler: outerLinkHandler, smartTagHandler: outerSmartTagHandler, bubbleMenuProps, style, render, onUpdate, onFocus, onCreate, onChangeFont, } = props;
42
+ // Initialize bubbleMenuContainer with default value (document.body)
43
+ // This ensures BubbleMenu receives a valid appendTo prop on first render
44
+ const bubbleMenuContainer = useRef((() => {
45
+ const appendTo = bubbleMenuProps?.appendTo;
46
+ if (!appendTo)
47
+ return document.body;
48
+ return typeof appendTo === 'function' ? appendTo() : appendTo;
49
+ })());
50
+ const linkFormVisible = useTextEditorStore(state => state.linkFormState?.isVisible);
40
51
  const setBubbleMenuContainer = useTextEditorStore(state => state.setBubbleMenuContainer);
52
+ const showLinkForm = useTextEditorStore(state => state.showLinkForm);
41
53
  const defaultTextStyle = useDeepCompareMemo(() => ({
42
54
  ...DEFAULT_TEXT_STYLE,
43
55
  ...defaultTextStyleProp,
44
56
  }), [defaultTextStyleProp]);
57
+ useEffect(() => {
58
+ const bubbleMenuAppendTo = bubbleMenuProps?.appendTo;
59
+ // Default to document.body when no appendTo is provided
60
+ // This prevents bubble menu from being clipped by overflow containers
61
+ const el = bubbleMenuAppendTo
62
+ ? typeof bubbleMenuAppendTo === 'function'
63
+ ? bubbleMenuAppendTo()
64
+ : bubbleMenuAppendTo
65
+ : document.body;
66
+ setBubbleMenuContainer(el);
67
+ bubbleMenuContainer.current = el;
68
+ }, [bubbleMenuProps?.appendTo, setBubbleMenuContainer]);
45
69
  const handleOnUpdateDebounce = useDebouncedCallback((editor) => {
46
- const html = editor.getHTML();
47
70
  const text = editor.getText();
48
71
  const json = editor.getJSON();
72
+ const html = editor.getHTML();
49
73
  onUpdate?.({
50
74
  html: htmlSerializerForOutput(html),
51
75
  text,
@@ -53,21 +77,40 @@ export const TextEditor = memo(forwardRef((props, ref) => {
53
77
  });
54
78
  }, onUpdateDebounced);
55
79
  const contentRef = useRef(null);
80
+ // Compose linkHandler: merge outerLinkHandler with default handlers
81
+ // Allows partial override - user can provide only setNew or only edit
82
+ const linkHandler = useMemo(() => {
83
+ const defaultSetNew = ({ selectionText }) => {
84
+ showLinkForm('insert', {
85
+ url: '',
86
+ text: selectionText,
87
+ openInNewTab: false,
88
+ });
89
+ };
90
+ const defaultEdit = ({ attrs, selectionText, }) => {
91
+ showLinkForm('edit', {
92
+ url: attrs.href || '',
93
+ text: selectionText,
94
+ openInNewTab: attrs.target === '_blank',
95
+ });
96
+ };
97
+ return {
98
+ setNew: outerLinkHandler?.setNew || defaultSetNew,
99
+ edit: outerLinkHandler?.edit || defaultEdit,
100
+ };
101
+ }, [outerLinkHandler, showLinkForm]);
56
102
  const editor = useEditor({
57
103
  extensions: [
58
104
  StarterKit.configure({
59
105
  link: false,
60
106
  bulletList: false,
61
107
  orderedList: false,
62
- listItem: {
63
- HTMLAttributes: {
64
- style: 'line-height:normal;',
65
- },
66
- },
108
+ listItem: false,
67
109
  undoRedo: {
68
110
  depth: 100,
69
111
  },
70
112
  }),
113
+ CustomListItem,
71
114
  CustomUnorderedList,
72
115
  CustomOrderedList,
73
116
  FontWeight.configure({
@@ -107,7 +150,12 @@ export const TextEditor = memo(forwardRef((props, ref) => {
107
150
  Selection.configure({
108
151
  className: 'selection',
109
152
  }),
110
- SmartTag,
153
+ Focus.configure({
154
+ className: 'has-focused',
155
+ }),
156
+ SmartTag.configure({
157
+ generateSmartTagId: config?.SmartTag?.generateId,
158
+ }),
111
159
  TextAlign.configure({
112
160
  types: ['heading', 'paragraph'],
113
161
  }),
@@ -121,7 +169,7 @@ export const TextEditor = memo(forwardRef((props, ref) => {
121
169
  Indent,
122
170
  Emoji.configure({
123
171
  suggestion: emojiSuggestion({
124
- container: bubbleMenuProps?.container,
172
+ container: bubbleMenuContainer.current,
125
173
  }),
126
174
  }),
127
175
  ClearFormatting.configure({
@@ -141,6 +189,14 @@ export const TextEditor = memo(forwardRef((props, ref) => {
141
189
  if (safeParseContent !== initialContent) {
142
190
  editor.commands.setContent(safeParseContent);
143
191
  }
192
+ const text = editor.getText();
193
+ const json = editor.getJSON();
194
+ const html = editor.getHTML();
195
+ onCreate?.({
196
+ html: htmlSerializerForOutput(html),
197
+ text,
198
+ json,
199
+ });
144
200
  },
145
201
  onUpdate: ({ editor }) => {
146
202
  handleOnUpdateDebounce(editor);
@@ -154,7 +210,7 @@ export const TextEditor = memo(forwardRef((props, ref) => {
154
210
  if (event.ctrlKey && event.key === 'k') {
155
211
  // Add this line to prevent browser's default behavior.
156
212
  event.preventDefault();
157
- handleLinkAction(view, outerLinkHandler);
213
+ handleLinkAction(view, linkHandler);
158
214
  }
159
215
  },
160
216
  },
@@ -183,10 +239,15 @@ export const TextEditor = memo(forwardRef((props, ref) => {
183
239
  return updatedAttrs;
184
240
  })
185
241
  .run(), [editor]);
186
- const shouldShowBubbleMenu = useCallback((params) => {
242
+ const shouldShowLinkPreview = useCallback((params) => {
243
+ const { state } = params;
244
+ return isShowLinkToolbar(state);
245
+ }, []);
246
+ const shouldShowFormattingToolbar = useCallback((params) => {
187
247
  const { state, view, element } = params;
248
+ // Don't show if link preview is visible
188
249
  if (isShowLinkToolbar(state)) {
189
- return true;
250
+ return false;
190
251
  }
191
252
  return defaultShouldShowBubbleMenu({
192
253
  state,
@@ -203,9 +264,23 @@ export const TextEditor = memo(forwardRef((props, ref) => {
203
264
  editor?.chain().blur().setTextSelection({ from: 0, to: 0 }).run();
204
265
  }
205
266
  }, [editor]);
206
- const handleBubbleMenuRef = useCallback((htmlElement) => {
207
- setBubbleMenuContainer(htmlElement);
208
- }, [setBubbleMenuContainer]);
267
+ const handleEachLinkGroup = useCallback(callback => {
268
+ if (!editor)
269
+ return;
270
+ const linkGroups = getAllFullLinkGroups(editor.state);
271
+ linkGroups.forEach(callback);
272
+ }, [editor]);
273
+ const handleEachSmartag = useCallback(callback => {
274
+ if (!editor)
275
+ return;
276
+ const { state } = editor;
277
+ const { doc } = state;
278
+ doc.descendants(node => {
279
+ if (isSmartTagNode(node)) {
280
+ callback(node.attrs);
281
+ }
282
+ });
283
+ }, [editor]);
209
284
  useImperativeHandle(ref, () => ({
210
285
  setLink: params => {
211
286
  editor?.chain().setCustomLink(params).run();
@@ -226,6 +301,8 @@ export const TextEditor = memo(forwardRef((props, ref) => {
226
301
  })
227
302
  .run();
228
303
  },
304
+ eachLinkGroup: handleEachLinkGroup,
305
+ eachSmartag: handleEachSmartag,
229
306
  setSmartTag: handleSetSmartTag,
230
307
  deleteSmartTag: handleDeleteSmartTag,
231
308
  updateSmartTagAttrs: handleUpdateSmartTagAttrs,
@@ -236,23 +313,87 @@ export const TextEditor = memo(forwardRef((props, ref) => {
236
313
  if (!editor) {
237
314
  return null;
238
315
  }
316
+ const bubbleMenus = (bubbleMenuProps?.enabled ?? true) ? (_jsxs(_Fragment, { children: [_jsx(LinkPopover, { editor: editor }), !linkFormVisible && (_jsxs(_Fragment, { children: [_jsx(BubbleMenu, { pluginKey: "linkPreviewBubbleMenu", ...bubbleMenuProps, appendTo: bubbleMenuContainer.current, editor: editor, shouldShow: shouldShowLinkPreview, children: _jsx(LinkPreviewToolbar, { editor: editor, linkHanlder: {
317
+ onUpsert: () => {
318
+ handleLinkAction(editor.view, linkHandler);
319
+ },
320
+ }, render: render }) }), _jsx(BubbleMenu, { pluginKey: "formattingBubbleMenu", ...bubbleMenuProps, appendTo: bubbleMenuContainer.current, editor: editor, shouldShow: shouldShowFormattingToolbar, children: _jsx(FormattingToolbar, { config: config, editor: editor, defaultTextStyle: defaultTextStyle, linkHanlder: {
321
+ onUpsert: () => {
322
+ handleLinkAction(editor.view, linkHandler);
323
+ },
324
+ }, smartTagHandler: {
325
+ onUpsert: (event) => {
326
+ handleSmartTagAction(event, editor.view, outerSmartTagHandler);
327
+ },
328
+ }, onChangeFont: ({ font, weight }) => {
329
+ onChangeFont?.({
330
+ font,
331
+ weight,
332
+ analysis: analyzeFont(font),
333
+ });
334
+ } }) })] }))] })) : null;
239
335
  return (_jsxs(_Fragment, { children: [_jsx(StyledEditorContent, { className: clsx(`${ANTSOMI_COMPONENT_PREFIX_CLS}-text-editor`, className), "$textStyle": defaultTextStyle, style: {
240
336
  // Inline styles apply to inner html elements (support email client)
241
- ...omit(defaultTextStyle, ['fontSize']),
337
+ ...defaultTextStyle,
242
338
  ...style,
243
- }, id: id, ref: contentRef, editor: editor, ...dataAttributes }), _jsx(BubbleMenu, { ref: handleBubbleMenuRef, editor: editor, shouldShow: shouldShowBubbleMenu, resizeDelay: 700, ...bubbleMenuProps, children: isShowBubbleMenu && (_jsx(Toolbar, { config: config, editor: editor, defaultTextStyle: defaultTextStyle, linkHanlder: {
244
- onUpsert: () => {
245
- handleLinkAction(editor.view, outerLinkHandler);
246
- },
247
- }, smartTagHandler: {
248
- onUpsert: (event) => {
249
- handleSmartTagAction(event, editor.view, outerSmartTagHandler);
250
- },
251
- }, render: render, onChangeFont: ({ font, weight }) => {
252
- onChangeFont?.({
253
- font,
254
- weight,
255
- analysis: analyzeFont(font),
256
- });
257
- } })) })] }));
339
+ }, id: id, ref: contentRef, editor: editor, ...dataAttributes }), bubbleMenus] }));
258
340
  }));
341
+ TextEditorInternal.displayName = 'TextEditorInternal';
342
+ const TextEditorWithProvider = forwardRef((props, ref) => {
343
+ const { colors, onChangeColors, ...editorProps } = props;
344
+ const providerRef = useRef(null);
345
+ const editorRef = useRef(null);
346
+ // Sync external colors to internal store
347
+ useDeepCompareEffect(() => {
348
+ if (colors !== undefined) {
349
+ providerRef.current?.updateColors?.(colors);
350
+ }
351
+ }, [colors]);
352
+ useImperativeHandle(ref, () => ({
353
+ // Forward TextEditorRef methods - accessed at call time
354
+ get style() {
355
+ return editorRef.current?.style;
356
+ },
357
+ get editor() {
358
+ return editorRef.current?.editor ?? null;
359
+ },
360
+ get setLink() {
361
+ return editorRef.current.setLink;
362
+ },
363
+ get deleteLink() {
364
+ return editorRef.current.deleteLink;
365
+ },
366
+ get updateLinkAttrs() {
367
+ return editorRef.current.updateLinkAttrs;
368
+ },
369
+ get updateLinkText() {
370
+ return editorRef.current.updateLinkText;
371
+ },
372
+ get eachLinkGroup() {
373
+ return editorRef.current.eachLinkGroup;
374
+ },
375
+ get eachSmartag() {
376
+ return editorRef.current.eachSmartag;
377
+ },
378
+ get setSmartTag() {
379
+ return editorRef.current.setSmartTag;
380
+ },
381
+ get deleteSmartTag() {
382
+ return editorRef.current.deleteSmartTag;
383
+ },
384
+ get updateSmartTagAttrs() {
385
+ return editorRef.current.updateSmartTagAttrs;
386
+ },
387
+ get blur() {
388
+ return editorRef.current.blur;
389
+ },
390
+ // Forward TextEditorProviderRefHandler methods
391
+ updateColors: colors => providerRef.current?.updateColors?.(colors),
392
+ }), []);
393
+ return (_jsx(TextEditorProvider, { ref: providerRef, colors: colors, onChangeColors: onChangeColors, children: _jsx(TextEditorInternal, { ...editorProps, ref: editorRef }) }));
394
+ });
395
+ TextEditorWithProvider.displayName = 'TextEditorWithProvider';
396
+ export const TextEditor = Object.assign(TextEditorWithProvider, {
397
+ Provider: TextEditorProvider,
398
+ Internal: TextEditorInternal,
399
+ });
@@ -185,3 +185,12 @@ export declare const DEFAULT_FONT_GROUPING: {
185
185
  readonly label: "Non-standard";
186
186
  };
187
187
  };
188
+ /**
189
+ * All available toolbar actions in default order
190
+ */
191
+ export declare const ALL_TOOLBAR_ACTIONS: readonly ["fontFamily", "fontSize", "bold", "italic", "underline", "link", "strike", "superscript", "subscript", "textTransform", "textColor", "backgroundColor", "emoji", "smartTag", "bulletList", "orderedList", "textAlign", "lineSpacing", "letterSpacing", "indent", "outdent", "history", "clearFormatting"];
192
+ /**
193
+ * Default grouping of toolbar actions into rows
194
+ */
195
+ export declare const DEFAULT_TOOLBAR_GROUPING: readonly [readonly ["fontFamily", "fontSize", "bold", "italic", "underline", "link", "strike", "superscript", "subscript", "textTransform", "textColor", "backgroundColor", "emoji", "smartTag"], readonly ["bulletList", "orderedList", "textAlign", "lineSpacing", "letterSpacing", "indent", "outdent", "history", "clearFormatting"]];
196
+ export declare const ATTR_WHITESPACE: RegExp;
@@ -330,3 +330,69 @@ export const DEFAULT_FONT_GROUPING = {
330
330
  label: 'Non-standard',
331
331
  },
332
332
  };
333
+ /**
334
+ * All available toolbar actions in default order
335
+ */
336
+ export const ALL_TOOLBAR_ACTIONS = [
337
+ // Row 1: Text formatting and styling
338
+ 'fontFamily',
339
+ 'fontSize',
340
+ 'bold',
341
+ 'italic',
342
+ 'underline',
343
+ 'link',
344
+ 'strike',
345
+ 'superscript',
346
+ 'subscript',
347
+ 'textTransform',
348
+ 'textColor',
349
+ 'backgroundColor',
350
+ 'emoji',
351
+ 'smartTag',
352
+ // Row 2: Lists, alignment, and utilities
353
+ 'bulletList',
354
+ 'orderedList',
355
+ 'textAlign',
356
+ 'lineSpacing',
357
+ 'letterSpacing',
358
+ 'indent',
359
+ 'outdent',
360
+ 'history',
361
+ 'clearFormatting',
362
+ ];
363
+ /**
364
+ * Default grouping of toolbar actions into rows
365
+ */
366
+ export const DEFAULT_TOOLBAR_GROUPING = [
367
+ // Row 1
368
+ [
369
+ 'fontFamily',
370
+ 'fontSize',
371
+ 'bold',
372
+ 'italic',
373
+ 'underline',
374
+ 'link',
375
+ 'strike',
376
+ 'superscript',
377
+ 'subscript',
378
+ 'textTransform',
379
+ 'textColor',
380
+ 'backgroundColor',
381
+ 'emoji',
382
+ 'smartTag',
383
+ ],
384
+ // Row 2
385
+ [
386
+ 'bulletList',
387
+ 'orderedList',
388
+ 'textAlign',
389
+ 'lineSpacing',
390
+ 'letterSpacing',
391
+ 'indent',
392
+ 'outdent',
393
+ 'history',
394
+ 'clearFormatting',
395
+ ],
396
+ ];
397
+ // eslint-disable-next-line no-control-regex
398
+ export const ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
@@ -1,140 +1,18 @@
1
- import { arrow, autoPlacement, flip, hide, inline, offset, shift, size } from '@floating-ui/dom';
2
- import type { Editor } from '@tiptap/core';
3
- import type { EditorState, PluginView } from '@tiptap/pm/state';
4
- import { Plugin, PluginKey } from '@tiptap/pm/state';
1
+ import type { EditorState } from '@tiptap/pm/state';
2
+ import { BubbleMenuView as TiptapBubbleMenuView } from '@tiptap/extension-bubble-menu';
3
+ import { Plugin } from '@tiptap/pm/state';
5
4
  import type { EditorView } from '@tiptap/pm/view';
6
- export interface BubbleMenuPluginProps {
7
- /**
8
- * The plugin key.
9
- * @type {PluginKey | string}
10
- * @default 'bubbleMenu'
11
- */
12
- pluginKey: PluginKey | string;
13
- /**
14
- * The editor instance.
15
- */
16
- editor: Editor;
17
- /**
18
- * The DOM element that contains your menu.
19
- * @type {HTMLElement}
20
- * @default null
21
- */
22
- element: HTMLElement;
23
- container?: HTMLElement | (() => HTMLElement) | null;
24
- /**
25
- * The delay in milliseconds before the menu should be updated.
26
- * This can be useful to prevent performance issues.
27
- * @type {number}
28
- * @default 250
29
- */
30
- updateDelay?: number;
31
- /**
32
- * The delay in milliseconds before the menu position should be updated on window resize.
33
- * This can be useful to prevent performance issues.
34
- * @type {number}
35
- * @default 60
36
- */
37
- resizeDelay?: number;
38
- /**
39
- * A function that determines whether the menu should be shown or not.
40
- * If this function returns `false`, the menu will be hidden, otherwise it will be shown.
41
- */
42
- shouldShow?: ((props: {
43
- editor: Editor;
44
- element: HTMLElement;
45
- view: EditorView;
46
- state: EditorState;
47
- oldState?: EditorState;
48
- from: number;
49
- to: number;
50
- }) => boolean) | null;
51
- /**
52
- * The options for the bubble menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, arrow, size, autoPlacement,
53
- * hide, and inline middlewares.
54
- * @default {}
55
- * @see https://floating-ui.com/docs/computePosition#options
56
- */
57
- options?: {
58
- strategy?: 'absolute' | 'fixed';
59
- placement?: 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
60
- offset?: Parameters<typeof offset>[0] | boolean;
61
- flip?: Parameters<typeof flip>[0] | boolean;
62
- shift?: Parameters<typeof shift>[0] | boolean;
63
- arrow?: Parameters<typeof arrow>[0] | false;
64
- size?: Parameters<typeof size>[0] | boolean;
65
- autoPlacement?: Parameters<typeof autoPlacement>[0] | boolean;
66
- hide?: Parameters<typeof hide>[0] | boolean;
67
- inline?: Parameters<typeof inline>[0] | boolean;
68
- onShow?: () => void;
69
- onHide?: () => void;
70
- onUpdate?: () => void;
71
- onDestroy?: () => void;
72
- };
73
- }
74
- export type BubbleMenuViewProps = BubbleMenuPluginProps & {
75
- view: EditorView;
76
- };
77
- export declare class BubbleMenuView implements PluginView {
78
- editor: Editor;
79
- element: HTMLElement;
80
- container?: HTMLElement | (() => HTMLElement) | null;
81
- view: EditorView;
82
- preventHide: boolean;
83
- updateDelay: number;
84
- resizeDelay: number;
85
- private updateDebounceTimer;
86
- private resizeDebounceTimer;
5
+ import type { BubbleMenuPluginProps, BubbleMenuViewProps } from '@tiptap/extension-bubble-menu';
6
+ export declare class BubbleMenuView extends TiptapBubbleMenuView {
87
7
  private resizeObserver;
88
- private isVisible;
89
- private floatingUIOptions;
90
- shouldShow: Exclude<BubbleMenuPluginProps['shouldShow'], null>;
91
- get middlewares(): {
92
- name: string;
93
- options?: any;
94
- fn: (state: {
95
- x: number;
96
- y: number;
97
- platform: import("@floating-ui/core").Platform;
98
- placement: import("@floating-ui/utils").Placement;
99
- initialPlacement: import("@floating-ui/utils").Placement;
100
- strategy: import("@floating-ui/utils").Strategy;
101
- middlewareData: import("@floating-ui/core").MiddlewareData;
102
- rects: import("@floating-ui/utils").ElementRects;
103
- elements: import("@floating-ui/dom").Elements;
104
- }) => import("@floating-ui/core").MiddlewareReturn | Promise<import("@floating-ui/core").MiddlewareReturn>;
105
- }[];
106
- constructor({ editor, element, view, updateDelay, resizeDelay, container, shouldShow, options, }: BubbleMenuViewProps);
107
- mousedownHandler: () => void;
108
- dragstartHandler: () => void;
109
- /**
110
- * Handles the window resize event and element resize event to update the position of the bubble menu.
111
- * It uses a debounce mechanism to prevent excessive updates.
112
- * The delay is defined by the `resizeDelay` property.
113
- */
114
- resizeHandler: (delay?: number) => void;
115
- /**
116
- * Window resize handler that uses the default resize delay.
117
- * This is a separate function to maintain the same reference for addEventListener/removeEventListener.
118
- */
119
- windowResizeHandler: () => void;
8
+ constructor(props: BubbleMenuViewProps);
120
9
  /**
121
10
  * Sets up ResizeObserver to watch for bubble menu element size changes.
122
11
  * This ensures the bubble menu position is updated when the element resizes
123
12
  * due to React component rendering different children, not just when the window resizes.
124
13
  */
125
14
  setupResizeObserver: () => void;
126
- focusHandler: () => void;
127
- blurHandler: ({ event }: {
128
- event: FocusEvent;
129
- }) => void;
130
- updatePosition(): void;
131
- update(view: EditorView, oldState?: EditorState): void;
132
- handleDebouncedUpdate: (view: EditorView, oldState?: EditorState) => void;
133
- getShouldShow(oldState?: EditorState): boolean | undefined;
134
15
  updateHandler: (view: EditorView, selectionChanged: boolean, docChanged: boolean, oldState?: EditorState) => void;
135
- private getContainer;
136
- show(): void;
137
- hide(): void;
138
16
  destroy(): void;
139
17
  }
140
18
  export declare const BubbleMenuPlugin: (options: BubbleMenuPluginProps) => Plugin<any>;