@antscorp/antsomi-ui 1.3.7-beta.6 → 1.3.7-beta.61

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 (159) hide show
  1. package/es/components/atoms/Input/Input.js +3 -1
  2. package/es/components/atoms/InputDynamic/InputDynamic.js +3 -3
  3. package/es/components/icons/LazyIcon/LazyIcon.d.ts +2 -0
  4. package/es/components/icons/LazyIcon/LazyIcon.js +2 -0
  5. package/es/components/icons/QRCodeManagementIcon.d.ts +3 -0
  6. package/es/components/icons/QRCodeManagementIcon.js +7 -0
  7. package/es/components/icons/QRSetDashboardIcon.d.ts +3 -0
  8. package/es/components/icons/QRSetDashboardIcon.js +7 -0
  9. package/es/components/icons/index.d.ts +2 -0
  10. package/es/components/icons/index.js +2 -0
  11. package/es/components/molecules/AddDynamicContent/AddDynamicContent.js +28 -2
  12. package/es/components/molecules/AddDynamicContent/constants.d.ts +2 -0
  13. package/es/components/molecules/AddDynamicContent/constants.js +2 -0
  14. package/es/components/molecules/ColorPicker/index.d.ts +2 -0
  15. package/es/components/molecules/ColorPicker/index.js +8 -3
  16. package/es/components/molecules/EmojiPopover/EmojiPopover.d.ts +1 -0
  17. package/es/components/molecules/EmojiPopover/EmojiPopover.js +2 -2
  18. package/es/components/molecules/EmojiPopover/styled.d.ts +2 -2
  19. package/es/components/molecules/EmojiPopover/styled.js +1 -1
  20. package/es/components/molecules/HeaderV2/HeaderV2.d.ts +1 -1
  21. package/es/components/molecules/Modal/Modal.js +7 -1
  22. package/es/components/molecules/SearchPopover/SearchPopover.js +2 -2
  23. package/es/components/molecules/SearchPopover/styled.d.ts +12 -1
  24. package/es/components/molecules/SearchPopover/styled.js +1 -2
  25. package/es/components/molecules/SearchPopover/types.d.ts +4 -3
  26. package/es/components/molecules/Select/Select.js +1 -1
  27. package/es/components/molecules/ShareAccess/ShareAccess.d.ts +2 -1
  28. package/es/components/molecules/ShareAccess/ShareAccess.js +12 -9
  29. package/es/components/molecules/ShareAccess/components/LayoutContent/LayoutContent.js +5 -5
  30. package/es/components/molecules/ShareAccess/components/PeopleAccess/PeopleAccess.js +2 -2
  31. package/es/components/molecules/ShareAccess/hooks/useShareAccess.d.ts +2 -1
  32. package/es/components/molecules/ShareAccess/index.d.ts +1 -0
  33. package/es/components/molecules/ShareAccess/reducer.d.ts +1 -1
  34. package/es/components/molecules/ShareAccess/reducer.js +7 -4
  35. package/es/components/molecules/ShareAccess/types.d.ts +22 -0
  36. package/es/components/molecules/ShareAccess/utils.d.ts +3 -1
  37. package/es/components/molecules/ShareAccess/utils.js +35 -2
  38. package/es/components/molecules/TagifyInput/TagifyInput.js +48 -27
  39. package/es/components/molecules/TagifyInput/types.d.ts +14 -8
  40. package/es/components/molecules/TagifyInput/utils.d.ts +6 -1
  41. package/es/components/molecules/TagifyInput/utils.js +19 -0
  42. package/es/components/molecules/VirtualizedMenu/VirtualizedMenu.d.ts +1 -0
  43. package/es/components/molecules/VirtualizedMenu/components/Item/Item.js +3 -7
  44. package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.d.ts +10 -10
  45. package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.js +46 -327
  46. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/index.d.ts +9 -0
  47. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/index.js +5 -0
  48. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useFocusManagement.d.ts +23 -0
  49. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useFocusManagement.js +81 -0
  50. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useItemInteraction.d.ts +24 -0
  51. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useItemInteraction.js +32 -0
  52. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useKeyboardNavigation.d.ts +26 -0
  53. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useKeyboardNavigation.js +93 -0
  54. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useTreeState.d.ts +24 -0
  55. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useTreeState.js +94 -0
  56. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useVisibleItems.d.ts +7 -0
  57. package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useVisibleItems.js +132 -0
  58. package/es/components/molecules/VirtualizedMenu/styled.js +14 -3
  59. package/es/components/molecules/VirtualizedMenu/types.d.ts +1 -0
  60. package/es/components/molecules/VirtualizedMenu/utils.d.ts +1 -0
  61. package/es/components/molecules/VirtualizedMenu/utils.js +1 -0
  62. package/es/components/molecules/index.d.ts +1 -0
  63. package/es/components/organism/AccountSharing/AccountSharing.d.ts +1 -0
  64. package/es/components/organism/AccountSharing/AccountSharing.js +11 -4
  65. package/es/components/organism/AccountSharing/styled.js +1 -1
  66. package/es/components/organism/ActivityTimeline/ActivityTimeline.js +3 -3
  67. package/es/components/organism/ActivityTimeline/constants.d.ts +9 -9
  68. package/es/components/organism/ActivityTimeline/constants.js +3 -3
  69. package/es/components/organism/ActivityTimeline/index.d.ts +530 -1
  70. package/es/components/organism/ActivityTimeline/index.js +9 -1
  71. package/es/components/organism/ActivityTimeline/utils.js +175 -8
  72. package/es/components/organism/LeftMenu/LeftMenu.js +3 -3
  73. package/es/components/organism/LeftMenu/hooks/usePermission.js +1 -1
  74. package/es/components/organism/LeftMenu/types/index.d.ts +7 -0
  75. package/es/components/organism/LeftMenu/utils/index.js +1 -1
  76. package/es/components/organism/Login/Login.js +12 -3
  77. package/es/components/organism/Login/components/SignIn/SignIn.js +2 -2
  78. package/es/components/organism/Login/types/index.d.ts +2 -0
  79. package/es/components/organism/TextEditor/TextEditor.d.ts +9 -2
  80. package/es/components/organism/TextEditor/TextEditor.js +203 -42
  81. package/es/components/organism/TextEditor/constants.d.ts +9 -0
  82. package/es/components/organism/TextEditor/constants.js +66 -0
  83. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu-plugin.d.ts +6 -128
  84. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu-plugin.js +7 -292
  85. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu.d.ts +1 -1
  86. package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu.js +4 -0
  87. package/es/components/organism/TextEditor/extensions/Link.js +33 -14
  88. package/es/components/organism/TextEditor/extensions/ListItem.d.ts +10 -0
  89. package/es/components/organism/TextEditor/extensions/ListItem.js +93 -0
  90. package/es/components/organism/TextEditor/extensions/SmartTag.d.ts +5 -6
  91. package/es/components/organism/TextEditor/extensions/SmartTag.js +96 -4
  92. package/es/components/organism/TextEditor/hooks/useColorSet.js +7 -0
  93. package/es/components/organism/TextEditor/hooks/useMarkTracking.js +2 -1
  94. package/es/components/organism/TextEditor/index.d.ts +7 -5
  95. package/es/components/organism/TextEditor/index.scss +4 -7
  96. package/es/components/organism/TextEditor/provider.d.ts +1 -0
  97. package/es/components/organism/TextEditor/provider.js +6 -3
  98. package/es/components/organism/TextEditor/store.d.ts +11 -4
  99. package/es/components/organism/TextEditor/store.js +22 -2
  100. package/es/components/organism/TextEditor/stories/WithOldDynAndLink/froala-legacy-format.settings.json +95 -0
  101. package/es/components/organism/TextEditor/stories/WithOldDynAndLink/shared.d.ts +111 -0
  102. package/es/components/organism/TextEditor/stories/WithOldDynAndLink/shared.js +82 -0
  103. package/es/components/organism/TextEditor/stories/shared.d.ts +64 -0
  104. package/es/components/organism/TextEditor/stories/shared.js +57 -0
  105. package/es/components/organism/TextEditor/styled.d.ts +1 -1
  106. package/es/components/organism/TextEditor/styled.js +1 -0
  107. package/es/components/organism/TextEditor/types.d.ts +161 -11
  108. package/es/components/organism/TextEditor/types.js +1 -0
  109. package/es/components/organism/TextEditor/ui/BubbleMenu/BubbleMenu.d.ts +2 -1
  110. package/es/components/organism/TextEditor/ui/BubbleMenu/BubbleMenu.js +80 -51
  111. package/es/components/organism/TextEditor/ui/Emoji/EmojiList.js +1 -1
  112. package/es/components/organism/TextEditor/ui/Emoji/suggestion.d.ts +1 -1
  113. package/es/components/organism/TextEditor/ui/Emoji/suggestion.js +2 -2
  114. package/es/components/organism/TextEditor/ui/FontPopover/FontPopover.js +12 -2
  115. package/es/components/organism/TextEditor/ui/LinkInsertForm/LinkInsertForm.d.ts +16 -0
  116. package/es/components/organism/TextEditor/ui/LinkInsertForm/LinkInsertForm.js +61 -0
  117. package/es/components/organism/TextEditor/ui/LinkInsertForm/index.d.ts +2 -0
  118. package/es/components/organism/TextEditor/ui/LinkInsertForm/index.js +1 -0
  119. package/es/components/organism/TextEditor/ui/LinkPopover/LinkPopover.d.ts +9 -0
  120. package/es/components/organism/TextEditor/ui/LinkPopover/LinkPopover.js +126 -0
  121. package/es/components/organism/TextEditor/ui/LinkPopover/index.d.ts +2 -0
  122. package/es/components/organism/TextEditor/ui/LinkPopover/index.js +1 -0
  123. package/es/components/organism/TextEditor/ui/Popover/Popover.js +1 -1
  124. package/es/components/organism/TextEditor/ui/TextAlignSelect/TextAlignSelect.js +1 -3
  125. package/es/components/organism/TextEditor/ui/Toolbar/{Toolbar.d.ts → FormattingToolbar.d.ts} +3 -4
  126. package/es/components/organism/TextEditor/ui/Toolbar/FormattingToolbar.js +90 -0
  127. package/es/components/organism/TextEditor/ui/Toolbar/LinkPreviewToolbar.d.ts +10 -0
  128. package/es/components/organism/TextEditor/ui/Toolbar/LinkPreviewToolbar.js +39 -0
  129. package/es/components/organism/TextEditor/ui/Toolbar/actions/EmojiAction.js +4 -3
  130. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontFamilyAction.d.ts +1 -0
  131. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontFamilyAction.js +2 -2
  132. package/es/components/organism/TextEditor/ui/Toolbar/actions/FontSizeAction.js +6 -1
  133. package/es/components/organism/TextEditor/ui/Toolbar/index.d.ts +2 -1
  134. package/es/components/organism/TextEditor/ui/Toolbar/index.js +2 -1
  135. package/es/components/organism/TextEditor/utils/documentState.d.ts +14 -0
  136. package/es/components/organism/TextEditor/utils/documentState.js +25 -0
  137. package/es/components/organism/TextEditor/utils/htmlProcessing.js +60 -0
  138. package/es/components/organism/TextEditor/utils/link.d.ts +10 -1
  139. package/es/components/organism/TextEditor/utils/link.js +161 -7
  140. package/es/components/organism/TextEditor/utils/menu.js +2 -1
  141. package/es/components/organism/TextEditor/utils/selection.js +3 -2
  142. package/es/components/organism/TextEditor/utils/smartTag.js +2 -1
  143. package/es/components/organism/index.d.ts +1 -1
  144. package/es/hooks/index.d.ts +1 -1
  145. package/es/hooks/index.js +1 -1
  146. package/es/providers/ConfigProvider/ConfigProvider.d.ts +4 -0
  147. package/es/providers/ConfigProvider/ConfigProvider.js +2 -3
  148. package/es/providers/ConfigProvider/GlobalStyle.d.ts +2 -2
  149. package/es/providers/ConfigProvider/GlobalStyle.js +67 -64
  150. package/es/services/MediaTemplateDesign/UploadFile/index.js +4 -4
  151. package/es/utils/common.d.ts +1 -1
  152. package/es/utils/common.js +3 -3
  153. package/es/utils/cookie.js +2 -1
  154. package/package.json +18 -23
  155. package/es/components/organism/TextEditor/ui/BubbleToolbar/BubbleToolbar.d.ts +0 -1
  156. package/es/components/organism/TextEditor/ui/BubbleToolbar/BubbleToolbar.js +0 -1
  157. package/es/components/organism/TextEditor/ui/BubbleToolbar/index.d.ts +0 -0
  158. package/es/components/organism/TextEditor/ui/BubbleToolbar/index.js +0 -1
  159. package/es/components/organism/TextEditor/ui/Toolbar/Toolbar.js +0 -39
@@ -1,4 +1,7 @@
1
- import { Node, mergeAttributes } from '@tiptap/core';
1
+ import { Node, getMarksBetween, mergeAttributes } from '@tiptap/core';
2
+ import { Plugin, PluginKey } from '@tiptap/pm/state';
3
+ import uniqid from 'uniqid';
4
+ import { isSmartTagNode } from '../types';
2
5
  export const EXTENSION_NAME = 'smartTag';
3
6
  /**
4
7
  * Represents a dynamic tag node in the editor.
@@ -12,6 +15,7 @@ export const SmartTag = Node.create({
12
15
  addOptions() {
13
16
  return {
14
17
  HTMLAttributes: {},
18
+ generateSmartTagId: () => uniqid(),
15
19
  };
16
20
  },
17
21
  addAttributes() {
@@ -29,6 +33,11 @@ export const SmartTag = Node.create({
29
33
  parseHTML: el => el.getAttribute('tag'),
30
34
  renderHTML: attrs => (attrs.tag ? { tag: attrs.tag } : null),
31
35
  },
36
+ // Transient attribute - not persisted to HTML, used for tracking paste operations
37
+ sourceId: {
38
+ default: null,
39
+ rendered: false, // Don't include in HTML output
40
+ },
32
41
  };
33
42
  },
34
43
  parseHTML() {
@@ -57,9 +66,10 @@ export const SmartTag = Node.create({
57
66
  addCommands() {
58
67
  return {
59
68
  setSmartTag: attrs => ({ chain, state }) => {
60
- const { selection, storedMarks } = state;
61
- const { $from } = selection;
62
- const marks = storedMarks || $from.marks();
69
+ const { selection } = state;
70
+ const { from, to } = selection;
71
+ // Get marks from inside the selection to capture boundary marks
72
+ const marks = getMarksBetween(from, to, state.doc).map(markRange => markRange.mark);
63
73
  const smartTagNode = this.type.create(attrs);
64
74
  const nodeWithMarks = marks.length > 0 ? smartTagNode.mark(marks) : smartTagNode;
65
75
  return chain()
@@ -99,4 +109,86 @@ export const SmartTag = Node.create({
99
109
  },
100
110
  };
101
111
  },
112
+ addProseMirrorPlugins() {
113
+ // Smart Tag Deduplication Plugin
114
+ // Responsibility: Set sourceId for pasted smart tags (for external component to handle cloning)
115
+ const deduplicationPlugin = new Plugin({
116
+ key: new PluginKey('smartTagDeduplication'),
117
+ appendTransaction: (transactions, oldState, newState) => {
118
+ // Only process if there was a paste or document change
119
+ const hasPaste = transactions.some(tr => tr.getMeta('paste'));
120
+ const hasDocChange = transactions.some(tr => tr.docChanged);
121
+ if (!hasPaste && !hasDocChange) {
122
+ return null;
123
+ }
124
+ // Build position mapping from oldState to newState
125
+ // This accounts for position shifts when pasting
126
+ const { mapping } = transactions[0];
127
+ // Build a map of existing smart tag IDs in oldState (before paste)
128
+ const oldStateIds = new Map();
129
+ oldState.doc.descendants((node, pos) => {
130
+ if (isSmartTagNode(node)) {
131
+ oldStateIds.set(node.attrs.id, { pos });
132
+ }
133
+ });
134
+ // Collect all smart tag IDs in newState (after paste)
135
+ const newStateIds = new Map();
136
+ newState.doc.descendants((node, pos) => {
137
+ if (isSmartTagNode(node)) {
138
+ const { id } = node.attrs;
139
+ if (!newStateIds.has(id)) {
140
+ newStateIds.set(id, []);
141
+ }
142
+ newStateIds.get(id).push({ pos, node });
143
+ }
144
+ });
145
+ // Find duplicates: IDs that appear multiple times in newState
146
+ const nodesToSetSourceId = new Map();
147
+ newStateIds.forEach((positions, id) => {
148
+ if (positions.length > 1) {
149
+ // This ID has duplicates
150
+ const oldInfo = oldStateIds.get(id);
151
+ if (oldInfo) {
152
+ // Map old position to new position accounting for document changes
153
+ const mappedPos = mapping.map(oldInfo.pos);
154
+ // Filter out the original node (by mapped position), keep only pasted nodes
155
+ const pastedNodes = positions.filter(({ pos }) => pos !== mappedPos);
156
+ if (pastedNodes.length > 0) {
157
+ nodesToSetSourceId.set(id, pastedNodes);
158
+ }
159
+ }
160
+ else {
161
+ // No original in oldState - all are new (e.g., paste multiple new smart tags)
162
+ // Pick first as "original", rest get sourceId
163
+ const [_first, ...rest] = positions;
164
+ if (rest.length > 0) {
165
+ nodesToSetSourceId.set(id, rest);
166
+ }
167
+ }
168
+ }
169
+ });
170
+ // If no duplicates found, no action needed
171
+ if (nodesToSetSourceId.size === 0) {
172
+ return null;
173
+ }
174
+ // Set sourceId for pasted nodes (do NOT generate new IDs)
175
+ // External component will handle ID generation and attrs cloning
176
+ const { tr } = newState;
177
+ let hasChanges = false;
178
+ nodesToSetSourceId.forEach((positions, sourceId) => {
179
+ positions.forEach(({ pos, node }) => {
180
+ // Only set sourceId, keep original ID
181
+ tr.setNodeMarkup(pos, undefined, {
182
+ ...node.attrs,
183
+ sourceId,
184
+ id: this.options.generateSmartTagId?.() || uniqid(),
185
+ });
186
+ hasChanges = true;
187
+ });
188
+ });
189
+ return hasChanges ? tr : null;
190
+ },
191
+ });
192
+ return [deduplicationPlugin];
193
+ },
102
194
  });
@@ -2,6 +2,7 @@ import { useBroadcastedLocalStorage, useDeepCompareEffect } from '@antscorp/ants
2
2
  import { useTextEditorStore } from '../provider';
3
3
  import { ANTSOMI_COMPONENT_PREFIX_CLS } from '@antscorp/antsomi-ui/es/constants';
4
4
  import { useCallback } from 'react';
5
+ import isEqual from 'react-fast-compare';
5
6
  export const useColorSet = () => {
6
7
  const colors = useTextEditorStore(s => s.colors);
7
8
  const setColors = useTextEditorStore(s => s.setColors);
@@ -9,9 +10,15 @@ export const useColorSet = () => {
9
10
  useDeepCompareEffect(() => {
10
11
  if (storageColors === undefined && colors !== undefined) {
11
12
  setStorageColors(colors);
13
+ return;
12
14
  }
13
15
  if (colors === undefined && storageColors !== undefined) {
14
16
  setColors(storageColors);
17
+ return;
18
+ }
19
+ // Sync store → localStorage when both defined but different
20
+ if (colors !== undefined && storageColors !== undefined && !isEqual(colors, storageColors)) {
21
+ setStorageColors(colors);
15
22
  }
16
23
  }, [colors, storageColors, setColors, setStorageColors]);
17
24
  const composeSetColors = useCallback((newColors) => {
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useState } from 'react';
2
+ import { textBetween } from '../utils';
2
3
  export function useMarkTracking(editor, options = {}) {
3
4
  const { maxSnapshots = 10, excludeMarks = ['link', 'smartTag'], debounceDelay = 500, autoCapture = true, } = options;
4
5
  const [snapshots, setSnapshots] = useState([]);
@@ -14,7 +15,7 @@ export function useMarkTracking(editor, options = {}) {
14
15
  marks,
15
16
  position: $from.pos,
16
17
  timestamp: Date.now(),
17
- textContent: editor.state.doc.textBetween(Math.max(0, $from.pos - 50), Math.min(editor.state.doc.content.size, $from.pos + 50)),
18
+ textContent: textBetween(editor.state, Math.max(0, $from.pos - 50), Math.min(editor.state.doc.content.size, $from.pos + 50)),
18
19
  };
19
20
  setSnapshots(prev => {
20
21
  const updated = [snapshot, ...prev];
@@ -1,14 +1,16 @@
1
1
  /// <reference types="react" />
2
2
  import { JSONContent } from '@tiptap/core';
3
- export type { TextEditorProps, TextEditorRef, TextEditorComponentsRender, LinkAttrs, FontConfig, } from './types';
3
+ export type { TextEditorProps, TextEditorAllProps, TextEditorRef, TextEditorWithProviderRef, TextEditorComponentsRender, LinkAttrs, FontConfig, } from './types';
4
4
  export { isLinkMark, isLinkMarkRange } from './types';
5
5
  export { CUSTOM_LINK_EXTENSION_NAME } from './constants';
6
6
  export type { JSONContent as TextEditorJSONContent };
7
7
  export { TextEditorProvider, type TextEditorProviderProps, type TextEditorProviderRefHandler, } from './provider';
8
- export declare const TextEditor: import("react").NamedExoticComponent<Omit<import("./types").TextEditorProps & import("react").RefAttributes<import("./types").TextEditorRef>, "ref"> & {
9
- ref?: ((instance: import("./types").TextEditorRef | null) => void) | import("react").RefObject<import("./types").TextEditorRef> | null | undefined;
10
- }> & {
11
- readonly type: import("react").ForwardRefExoticComponent<import("./types").TextEditorProps & import("react").RefAttributes<import("./types").TextEditorRef>>;
8
+ export declare const TextEditor: import("react").ForwardRefExoticComponent<import("./types").TextEditorProps & {
9
+ colors?: string[] | undefined;
10
+ onChangeColors?: ((colors: string[]) => void) | undefined;
11
+ } & import("react").RefAttributes<import("./types").TextEditorWithProviderRef>> & {
12
+ Provider: import("react").ForwardRefExoticComponent<import("./provider").TextEditorProviderProps & import("react").RefAttributes<import("./provider").TextEditorProviderRefHandler>>;
13
+ Internal: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<import("./types").TextEditorProps & import("react").RefAttributes<import("./types").TextEditorRef>>>;
12
14
  } & {
13
15
  Utils: {
14
16
  htmlMinifyForEmail: (htmlEditorContent: string) => string;
@@ -3,6 +3,10 @@
3
3
  position: absolute;
4
4
  pointer-events: auto;
5
5
  z-index: 999;
6
+ background-color: white;
7
+ border-radius: 4px;
8
+ padding: 15px;
9
+ box-shadow: var(--antsomi-box-shadow-secondary);
6
10
  }
7
11
 
8
12
  .antsomi-text-editor-emoji-popover {
@@ -59,10 +63,3 @@
59
63
  }
60
64
  }
61
65
  }
62
-
63
- .antsomi-text-editor-bubble-menu {
64
- background-color: white;
65
- border-radius: 4px;
66
- padding: 15px;
67
- box-shadow: var(--antsomi-box-shadow-secondary);
68
- }
@@ -8,6 +8,7 @@ export interface TextEditorProviderRefHandler {
8
8
  }
9
9
  export interface TextEditorProviderProps {
10
10
  children: React.ReactNode | ((props: TextEditorStoreApi) => React.ReactNode);
11
+ colors?: string[];
11
12
  onChangeColors?: (colors: string[]) => void;
12
13
  }
13
14
  export declare const TextEditorProvider: import("react").ForwardRefExoticComponent<TextEditorProviderProps & import("react").RefAttributes<TextEditorProviderRefHandler>>;
@@ -5,10 +5,12 @@ import { useStore } from 'zustand';
5
5
  // Store
6
6
  import { createTextEditorStore } from './store';
7
7
  export const TextEditorStoreContext = createContext(null);
8
- export const TextEditorProvider = forwardRef(({ children, onChangeColors }, ref) => {
8
+ export const TextEditorProvider = forwardRef(({ children, colors, onChangeColors }, ref) => {
9
9
  const storeRef = useRef();
10
10
  if (!storeRef.current) {
11
- storeRef.current = createTextEditorStore();
11
+ storeRef.current = createTextEditorStore({
12
+ colors,
13
+ });
12
14
  }
13
15
  useEffect(() => {
14
16
  storeRef.current?.setState({
@@ -20,7 +22,8 @@ export const TextEditorProvider = forwardRef(({ children, onChangeColors }, ref)
20
22
  }, [onChangeColors]);
21
23
  useImperativeHandle(ref, () => ({
22
24
  updateColors: colors => {
23
- storeRef.current?.getState().setColors(colors);
25
+ // Use getInitialState to bypass the wrapped setColors and avoid triggering onChangeColors
26
+ storeRef.current?.getInitialState().setColors(colors);
24
27
  },
25
28
  }));
26
29
  return (_jsx(TextEditorStoreContext.Provider, { value: storeRef.current, children: typeof children === 'function' ? children(storeRef.current) : children }));
@@ -1,12 +1,19 @@
1
+ import type { LinkFormData, LinkFormState } from './types';
1
2
  export type TextEditorState = {
2
- bubbleMenuContainer: HTMLDivElement | null;
3
- isShowBubbleMenu: boolean;
3
+ bubbleMenuContainer: HTMLElement | null;
4
4
  colors?: string[];
5
+ /** Link form state for default link insert/edit UI */
6
+ linkFormState: LinkFormState;
5
7
  };
6
8
  export type TextEditorActions = {
7
- setBubbleMenuContainer: (container: HTMLDivElement | null) => void;
8
- setIsShowBubbleMenu: (isShow: boolean) => void;
9
+ setBubbleMenuContainer: (container: HTMLElement | null) => void;
9
10
  setColors: (newColorSet: string[]) => void;
11
+ /** Show link form with initial data */
12
+ showLinkForm: (mode: 'insert' | 'edit', initialData: LinkFormData) => void;
13
+ /** Hide link form */
14
+ hideLinkForm: () => void;
15
+ /** Update link form data (partial update) */
16
+ updateLinkFormData: (data: Partial<LinkFormData>) => void;
10
17
  };
11
18
  export type TextEditorStore = TextEditorState & TextEditorActions;
12
19
  export declare const defaultTextEditorState: TextEditorState;
@@ -3,13 +3,12 @@ import isEqual from 'react-fast-compare';
3
3
  import { createStore } from 'zustand';
4
4
  export const defaultTextEditorState = {
5
5
  bubbleMenuContainer: null,
6
- isShowBubbleMenu: false,
6
+ linkFormState: null,
7
7
  };
8
8
  export const createTextEditorStore = (initState = {}) => createStore()((set, get) => ({
9
9
  ...defaultTextEditorState,
10
10
  ...initState,
11
11
  setBubbleMenuContainer: container => set(() => ({ bubbleMenuContainer: container })),
12
- setIsShowBubbleMenu: isShow => set(() => ({ isShowBubbleMenu: isShow })),
13
12
  setColors: newColors => {
14
13
  const currentColors = get().colors;
15
14
  if (isEqual(currentColors, newColors)) {
@@ -17,4 +16,25 @@ export const createTextEditorStore = (initState = {}) => createStore()((set, get
17
16
  }
18
17
  set(() => ({ colors: newColors }));
19
18
  },
19
+ showLinkForm: (mode, initialData) => set(() => ({
20
+ linkFormState: {
21
+ isVisible: true,
22
+ mode,
23
+ data: initialData,
24
+ },
25
+ })),
26
+ hideLinkForm: () => set(() => ({ linkFormState: null })),
27
+ updateLinkFormData: data => set(state => {
28
+ if (!state.linkFormState)
29
+ return state;
30
+ return {
31
+ linkFormState: {
32
+ ...state.linkFormState,
33
+ data: {
34
+ ...state.linkFormState.data,
35
+ ...data,
36
+ },
37
+ },
38
+ };
39
+ }),
20
40
  }));
@@ -0,0 +1,95 @@
1
+ {
2
+ "rawHTML": "<div class=\"fr-element fr-view\" dir=\"auto\" contenteditable=\"true\" aria-disabled=\"false\" spellcheck=\"true\"><p><span style=\"font-family: Montserrat; font-size: 22px; letter-spacing: 0px; color: #000000;\"><a data-link-id=\"j24jwuk2\" target=\"_blank\" href=\"https://ant.design/components/modal\"><span data-dynamic=\"true\" data-dynamic-id=\"i6fapgk6\" style=\"direction: unset; unicode-bidi: bidi-override; background-color: rgba(0, 199, 97, 0.2);\">Last modified by</span></a> Special <a data-link-id=\"rrd48wh5\" target=\"_blank\" href=\"https://ant.design\">Bonus <span data-dynamic=\"true\" data-dynamic-id=\"4ip5g3w4\" style=\"direction: unset; unicode-bidi: bidi-override; background-color: rgba(0, 199, 97, 0.2);\">Created date</span> Has</a> Been <span data-dynamic=\"true\" data-dynamic-id=\"vd3tmfc3\" style=\"direction: unset; unicode-bidi: bidi-override; background-color: rgba(0, 199, 97, 0.2);\"><a data-link-id=\"ir1h7e67\" target=\"_blank\" href=\"https://fireup.pro/news/goodbye-useeffect-exploring-use-in-react-19?ref=dailydev\">Name</a></span>&nbsp;Special <a data-link-id=\"rrd48wh5\" href=\"https://ant.design\" target=\"_blank\" id=\"isPasted\">Bonus</a>&nbsp;fasdfas&nbsp; sdfadf</span></p></div>",
3
+ "dynamic": {
4
+ "data": {
5
+ "4ip5g3w4": {
6
+ "type": "visitor-attribute",
7
+ "attribute": {
8
+ "label": "Created date",
9
+ "value": "date_created",
10
+ "status": 1,
11
+ "dataType": "datetime",
12
+ "disabled": false,
13
+ "datetimeFormatSettings": {
14
+ "type": "datetime",
15
+ "language": "en",
16
+ "hasDateFormat": true,
17
+ "hasTimeFormat": true,
18
+ "dateParseFormat": "MM/DD/YYYY",
19
+ "dateParseOption": "medium",
20
+ "timeParseFormat": "12hour",
21
+ "timeParseOption": "medium",
22
+ "dateFormatString": "MMM DD, YYYY h:mm:ss A"
23
+ }
24
+ },
25
+ "mappingKey": "lg5pk39o2qmxyc7oano5-4ip5g3w4",
26
+ "mappingFields": "visitor.date_created"
27
+ },
28
+ "i6fapgk6": {
29
+ "type": "event-attribute",
30
+ "event": "226539:17",
31
+ "source": [556657814],
32
+ "attribute": {
33
+ "type": 1,
34
+ "label": "Last modified by",
35
+ "value": "ad_zone.u_user_id",
36
+ "status": 1,
37
+ "children": [],
38
+ "dataType": "number",
39
+ "itemTypeId": -1013,
40
+ "itemTypeName": "ad_zone",
41
+ "propertyName": "u_user_id",
42
+ "eventPropertySyntax": "dims.ad_zone_id",
43
+ "numberFormatSettings": {
44
+ "type": "number",
45
+ "decimal": ".",
46
+ "grouping": ",",
47
+ "isCompact": false,
48
+ "prefixType": "code",
49
+ "currencyCode": "USD",
50
+ "decimalPlaces": 2
51
+ }
52
+ },
53
+ "mappingKey": "lg5pk39o2qmxyc7oano5-i6fapgk6",
54
+ "mappingFields": "event.ad_zone.u_user_id"
55
+ },
56
+ "vd3tmfc3": {
57
+ "type": "customer-attribute",
58
+ "attribute": {
59
+ "label": "Name",
60
+ "value": "name",
61
+ "status": 1,
62
+ "dataType": "string",
63
+ "disabled": false
64
+ },
65
+ "mappingKey": "lg5pk39o2qmxyc7oano5-vd3tmfc3",
66
+ "mappingFields": "customer.name"
67
+ }
68
+ },
69
+ "highlight": true,
70
+ "selectedId": ""
71
+ },
72
+ "link": {
73
+ "data": {
74
+ "ir1h7e67": {
75
+ "url": "https://fireup.pro/news/goodbye-useeffect-exploring-use-in-react-19?ref=dailydev",
76
+ "text": "Name",
77
+ "linkType": "static",
78
+ "openNewTab": true
79
+ },
80
+ "j24jwuk2": {
81
+ "url": "https://ant.design/components/modal",
82
+ "text": "Your",
83
+ "linkType": "static",
84
+ "openNewTab": true
85
+ },
86
+ "rrd48wh5": {
87
+ "url": "https://ant.design",
88
+ "text": "Bonus Offer Has",
89
+ "linkType": "static",
90
+ "openNewTab": true
91
+ }
92
+ },
93
+ "selectedId": ""
94
+ }
95
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * ⚠️ LEGACY FORMAT TYPES
3
+ * These types represent the OLD format from Froala editor
4
+ * DO NOT modify unless you understand the legacy compatibility requirements
5
+ */
6
+ /**
7
+ * Dynamic tag data structure from Froala editor
8
+ * Used for backwards compatibility with old HTML format
9
+ */
10
+ export type DynamicData = {
11
+ type: string;
12
+ attribute?: {
13
+ label: string;
14
+ value: string;
15
+ dataType?: string;
16
+ [key: string]: any;
17
+ };
18
+ event?: string;
19
+ source?: number[];
20
+ mappingKey: string;
21
+ mappingFields: string;
22
+ };
23
+ /**
24
+ * Link data structure from Froala editor
25
+ * Used for backwards compatibility with old HTML format
26
+ */
27
+ export type LinkData = {
28
+ url: string;
29
+ text: string;
30
+ linkType: string;
31
+ openNewTab: boolean;
32
+ };
33
+ /**
34
+ * State management for old format demo
35
+ */
36
+ export type State = {
37
+ link: {
38
+ data: Record<string, LinkData>;
39
+ selectedId: string;
40
+ };
41
+ dynamic: {
42
+ data: Record<string, DynamicData>;
43
+ selectedId: string;
44
+ highlight: boolean;
45
+ };
46
+ };
47
+ /**
48
+ * Actions for state reducer
49
+ */
50
+ export type Action = {
51
+ type: 'add_smart_tag';
52
+ payload: {
53
+ selectionText: string;
54
+ };
55
+ } | {
56
+ type: 'edit_smart_tag';
57
+ payload: {
58
+ id: string;
59
+ };
60
+ } | {
61
+ type: 'delete_smart_tag';
62
+ payload: {
63
+ id: string;
64
+ };
65
+ } | {
66
+ type: 'save_smart_tag';
67
+ payload: {
68
+ id: string;
69
+ data: DynamicData;
70
+ };
71
+ } | {
72
+ type: 'add_link';
73
+ payload: {
74
+ selectionText: string;
75
+ };
76
+ } | {
77
+ type: 'edit_link';
78
+ payload: {
79
+ id: string;
80
+ data?: Partial<LinkData>;
81
+ };
82
+ } | {
83
+ type: 'delete_link';
84
+ payload: {
85
+ id: string;
86
+ };
87
+ } | {
88
+ type: 'close_smart_tag_modal';
89
+ } | {
90
+ type: 'close_link_modal';
91
+ } | {
92
+ type: 'toggle_highlight';
93
+ };
94
+ /**
95
+ * Reducer for managing old format demo state
96
+ */
97
+ export declare const reducer: (state: State, action: Action) => State;
98
+ /**
99
+ * Constants for dynamic tag types
100
+ */
101
+ export declare const DYNAMIC_TAG_TYPES: {
102
+ label: string;
103
+ value: string;
104
+ }[];
105
+ /**
106
+ * Constants for data types
107
+ */
108
+ export declare const DATA_TYPES: {
109
+ label: string;
110
+ value: string;
111
+ }[];
@@ -0,0 +1,82 @@
1
+ import { produce } from 'immer';
2
+ import uniqid from 'uniqid';
3
+ /**
4
+ * Reducer for managing old format demo state
5
+ */
6
+ export const reducer = (state, action) => produce(state, draft => {
7
+ switch (action.type) {
8
+ case 'add_smart_tag': {
9
+ draft.dynamic.selectedId = uniqid();
10
+ break;
11
+ }
12
+ case 'edit_smart_tag': {
13
+ draft.dynamic.selectedId = action.payload.id;
14
+ break;
15
+ }
16
+ case 'delete_smart_tag': {
17
+ delete draft.dynamic.data[action.payload.id];
18
+ break;
19
+ }
20
+ case 'save_smart_tag': {
21
+ draft.dynamic.data[action.payload.id] = action.payload.data;
22
+ draft.dynamic.selectedId = '';
23
+ break;
24
+ }
25
+ case 'close_smart_tag_modal': {
26
+ draft.dynamic.selectedId = '';
27
+ break;
28
+ }
29
+ case 'add_link': {
30
+ const newLinkId = uniqid();
31
+ draft.link.selectedId = newLinkId;
32
+ draft.link.data[newLinkId] = {
33
+ url: '',
34
+ text: action.payload.selectionText,
35
+ linkType: 'static',
36
+ openNewTab: true,
37
+ };
38
+ break;
39
+ }
40
+ case 'edit_link': {
41
+ draft.link.selectedId = action.payload.id;
42
+ if (action.payload.data) {
43
+ draft.link.data[action.payload.id] = {
44
+ ...state.link.data[action.payload.id],
45
+ ...action.payload.data,
46
+ };
47
+ }
48
+ break;
49
+ }
50
+ case 'delete_link': {
51
+ delete draft.link.data[action.payload.id];
52
+ break;
53
+ }
54
+ case 'close_link_modal': {
55
+ draft.link.selectedId = '';
56
+ break;
57
+ }
58
+ case 'toggle_highlight': {
59
+ draft.dynamic.highlight = !draft.dynamic.highlight;
60
+ break;
61
+ }
62
+ default:
63
+ break;
64
+ }
65
+ });
66
+ /**
67
+ * Constants for dynamic tag types
68
+ */
69
+ export const DYNAMIC_TAG_TYPES = [
70
+ { label: 'Customer Attribute', value: 'customer-attribute' },
71
+ { label: 'Visitor Attribute', value: 'visitor-attribute' },
72
+ { label: 'Event Attribute', value: 'event-attribute' },
73
+ ];
74
+ /**
75
+ * Constants for data types
76
+ */
77
+ export const DATA_TYPES = [
78
+ { label: 'String', value: 'string' },
79
+ { label: 'Number', value: 'number' },
80
+ { label: 'Datetime', value: 'datetime' },
81
+ { label: 'Boolean', value: 'boolean' },
82
+ ];
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Parse HTML content from mock data
3
+ */
4
+ export declare const htmlContent: string;
5
+ /**
6
+ * State type for TextBlock component
7
+ */
8
+ export type State = {
9
+ link: {
10
+ data: Record<string, {
11
+ url: string;
12
+ text: string;
13
+ title: string;
14
+ }>;
15
+ selectedId: string;
16
+ };
17
+ dynamic: {
18
+ data: Record<string, unknown>;
19
+ selectedId: string;
20
+ };
21
+ };
22
+ /**
23
+ * Action types for TextBlock reducer
24
+ */
25
+ export type Action = {
26
+ type: 'add_smart_tag';
27
+ payload: {
28
+ selectionText: string;
29
+ };
30
+ } | {
31
+ type: 'edit_smart_tag';
32
+ payload: {
33
+ id: string;
34
+ };
35
+ } | {
36
+ type: 'add_link';
37
+ payload: {
38
+ selectionText: string;
39
+ };
40
+ } | {
41
+ type: 'edit_link';
42
+ payload: {
43
+ id: string;
44
+ data?: Partial<{
45
+ url: string;
46
+ text: string;
47
+ title: string;
48
+ }>;
49
+ };
50
+ } | {
51
+ type: 'close_smart_tag_modal';
52
+ } | {
53
+ type: 'close_link_modal';
54
+ };
55
+ /**
56
+ * Reducer for TextBlock component state management
57
+ */
58
+ export declare const reducer: (state: State, action: Action) => State;
59
+ /**
60
+ * Props for TextBlock component
61
+ */
62
+ export type TextBlockProps = {
63
+ defaultValue?: string;
64
+ };