@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.
- package/es/components/atoms/InputDynamic/InputDynamic.js +3 -3
- package/es/components/atoms/index.d.ts +0 -2
- package/es/components/atoms/index.js +0 -2
- package/es/components/molecules/AddDynamicContent/AddDynamicContent.js +28 -2
- package/es/components/molecules/AddDynamicContent/constants.d.ts +2 -0
- package/es/components/molecules/AddDynamicContent/constants.js +2 -0
- package/es/components/molecules/EmojiPopover/EmojiPopover.d.ts +1 -0
- package/es/components/molecules/EmojiPopover/EmojiPopover.js +2 -2
- package/es/components/molecules/EmojiPopover/styled.d.ts +2 -2
- package/es/components/molecules/EmojiPopover/styled.js +1 -1
- package/es/components/molecules/SearchPopover/SearchPopover.js +2 -2
- package/es/components/molecules/SearchPopover/styled.d.ts +12 -1
- package/es/components/molecules/SearchPopover/styled.js +1 -2
- package/es/components/molecules/SearchPopover/types.d.ts +4 -3
- package/es/components/molecules/TagifyInput/TagifyInput.js +55 -19
- package/es/components/molecules/TagifyInput/types.d.ts +20 -1
- package/es/components/molecules/TagifyInput/utils.d.ts +6 -1
- package/es/components/molecules/TagifyInput/utils.js +19 -0
- package/es/components/molecules/VirtualizedMenu/VirtualizedMenu.d.ts +1 -0
- package/es/components/molecules/VirtualizedMenu/components/Item/Item.js +3 -7
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.d.ts +10 -10
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/MenuInline.js +46 -327
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/index.d.ts +9 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/index.js +5 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useFocusManagement.d.ts +23 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useFocusManagement.js +81 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useItemInteraction.d.ts +24 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useItemInteraction.js +32 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useKeyboardNavigation.d.ts +26 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useKeyboardNavigation.js +93 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useTreeState.d.ts +24 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useTreeState.js +94 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useVisibleItems.d.ts +7 -0
- package/es/components/molecules/VirtualizedMenu/components/MenuInline/hooks/useVisibleItems.js +132 -0
- package/es/components/molecules/VirtualizedMenu/styled.js +14 -3
- package/es/components/molecules/VirtualizedMenu/types.d.ts +1 -0
- package/es/components/molecules/VirtualizedMenu/utils.d.ts +1 -0
- package/es/components/molecules/VirtualizedMenu/utils.js +1 -0
- package/es/components/organism/ActivityTimeline/ActivityTimeline.js +3 -3
- package/es/components/organism/ActivityTimeline/constants.d.ts +9 -9
- package/es/components/organism/ActivityTimeline/constants.js +3 -3
- package/es/components/organism/ActivityTimeline/index.d.ts +530 -1
- package/es/components/organism/ActivityTimeline/index.js +9 -1
- package/es/components/organism/ActivityTimeline/utils.js +7 -6
- package/es/components/organism/TextEditor/TextEditor.d.ts +9 -2
- package/es/components/organism/TextEditor/TextEditor.js +181 -40
- package/es/components/organism/TextEditor/constants.d.ts +9 -0
- package/es/components/organism/TextEditor/constants.js +66 -0
- package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu-plugin.d.ts +6 -128
- package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu-plugin.js +7 -292
- package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu.d.ts +1 -1
- package/es/components/organism/TextEditor/extensions/BubbleMenu/bubble-menu.js +4 -0
- package/es/components/organism/TextEditor/extensions/Link.js +3 -3
- package/es/components/organism/TextEditor/extensions/ListItem.d.ts +10 -0
- package/es/components/organism/TextEditor/extensions/ListItem.js +93 -0
- package/es/components/organism/TextEditor/extensions/SmartTag.d.ts +5 -6
- package/es/components/organism/TextEditor/extensions/SmartTag.js +94 -2
- package/es/components/organism/TextEditor/hooks/useColorSet.js +7 -0
- package/es/components/organism/TextEditor/hooks/useMarkTracking.js +2 -1
- package/es/components/organism/TextEditor/index.d.ts +7 -5
- package/es/components/organism/TextEditor/index.scss +4 -7
- package/es/components/organism/TextEditor/provider.d.ts +1 -0
- package/es/components/organism/TextEditor/provider.js +6 -3
- package/es/components/organism/TextEditor/store.d.ts +11 -4
- package/es/components/organism/TextEditor/store.js +22 -2
- package/es/components/organism/TextEditor/stories/WithOldDynAndLink/froala-legacy-format.settings.json +95 -0
- package/es/components/organism/TextEditor/stories/WithOldDynAndLink/shared.d.ts +111 -0
- package/es/components/organism/TextEditor/stories/WithOldDynAndLink/shared.js +82 -0
- package/es/components/organism/TextEditor/stories/shared.d.ts +64 -0
- package/es/components/organism/TextEditor/stories/shared.js +57 -0
- package/es/components/organism/TextEditor/styled.d.ts +1 -1
- package/es/components/organism/TextEditor/styled.js +1 -0
- package/es/components/organism/TextEditor/types.d.ts +153 -9
- package/es/components/organism/TextEditor/types.js +1 -0
- package/es/components/organism/TextEditor/ui/BubbleMenu/BubbleMenu.d.ts +2 -1
- package/es/components/organism/TextEditor/ui/BubbleMenu/BubbleMenu.js +80 -51
- package/es/components/organism/TextEditor/ui/Emoji/EmojiList.js +1 -1
- package/es/components/organism/TextEditor/ui/Emoji/suggestion.d.ts +1 -1
- package/es/components/organism/TextEditor/ui/Emoji/suggestion.js +2 -2
- package/es/components/organism/TextEditor/ui/FontPopover/FontPopover.js +12 -2
- package/es/components/organism/TextEditor/ui/LinkInsertForm/LinkInsertForm.d.ts +16 -0
- package/es/components/organism/TextEditor/ui/LinkInsertForm/LinkInsertForm.js +61 -0
- package/es/components/organism/TextEditor/ui/LinkInsertForm/index.d.ts +2 -0
- package/es/components/organism/TextEditor/ui/LinkInsertForm/index.js +1 -0
- package/es/components/organism/TextEditor/ui/LinkPopover/LinkPopover.d.ts +9 -0
- package/es/components/organism/TextEditor/ui/LinkPopover/LinkPopover.js +126 -0
- package/es/components/organism/TextEditor/ui/LinkPopover/index.d.ts +2 -0
- package/es/components/organism/TextEditor/ui/LinkPopover/index.js +1 -0
- package/es/components/organism/TextEditor/ui/Popover/Popover.js +1 -1
- package/es/components/organism/TextEditor/ui/TextAlignSelect/TextAlignSelect.js +1 -3
- package/es/components/organism/TextEditor/ui/Toolbar/{Toolbar.d.ts → FormattingToolbar.d.ts} +3 -4
- package/es/components/organism/TextEditor/ui/Toolbar/FormattingToolbar.js +85 -0
- package/es/components/organism/TextEditor/ui/Toolbar/LinkPreviewToolbar.d.ts +10 -0
- package/es/components/organism/TextEditor/ui/Toolbar/LinkPreviewToolbar.js +39 -0
- package/es/components/organism/TextEditor/ui/Toolbar/actions/EmojiAction.js +4 -3
- package/es/components/organism/TextEditor/ui/Toolbar/actions/FontFamilyAction.d.ts +1 -0
- package/es/components/organism/TextEditor/ui/Toolbar/actions/FontFamilyAction.js +2 -2
- package/es/components/organism/TextEditor/ui/Toolbar/actions/FontSizeAction.js +6 -1
- package/es/components/organism/TextEditor/ui/Toolbar/index.d.ts +2 -1
- package/es/components/organism/TextEditor/ui/Toolbar/index.js +2 -1
- package/es/components/organism/TextEditor/utils/documentState.d.ts +14 -0
- package/es/components/organism/TextEditor/utils/documentState.js +25 -0
- package/es/components/organism/TextEditor/utils/htmlProcessing.js +60 -0
- package/es/components/organism/TextEditor/utils/link.d.ts +10 -1
- package/es/components/organism/TextEditor/utils/link.js +161 -7
- package/es/components/organism/TextEditor/utils/menu.js +2 -1
- package/es/components/organism/TextEditor/utils/selection.js +3 -2
- package/es/components/organism/TextEditor/utils/smartTag.js +2 -1
- package/es/components/organism/index.d.ts +1 -1
- package/es/hooks/index.d.ts +1 -1
- package/es/hooks/index.js +1 -1
- package/es/services/MediaTemplateDesign/UploadFile/index.js +4 -4
- package/es/types/index.d.ts +1 -1
- package/es/types/index.js +1 -1
- package/es/utils/common.d.ts +1 -1
- package/es/utils/common.js +3 -3
- package/package.json +23 -35
- package/es/components/organism/TextEditor/ui/BubbleToolbar/BubbleToolbar.d.ts +0 -1
- package/es/components/organism/TextEditor/ui/BubbleToolbar/BubbleToolbar.js +0 -1
- package/es/components/organism/TextEditor/ui/BubbleToolbar/index.d.ts +0 -0
- package/es/components/organism/TextEditor/ui/BubbleToolbar/index.js +0 -1
- package/es/components/organism/TextEditor/ui/Toolbar/Toolbar.js +0 -39
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import type { JSONContent, Editor, MarkRange } from '@tiptap/core';
|
|
3
|
-
import
|
|
4
|
-
import type {
|
|
3
|
+
import { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
|
|
4
|
+
import type { Mark, Node } from '@tiptap/pm/model';
|
|
5
5
|
import { ORDERED_LIST_STYLE_TYPE, UNORDERED_LIST_STYLE_TYPE } from './constants';
|
|
6
6
|
import type { OverrideProperties } from 'type-fest';
|
|
7
|
-
import { BubbleMenuPluginProps } from './extensions/BubbleMenu';
|
|
8
7
|
export type HandleSmartTagRef = {
|
|
9
8
|
setSmartTag: (attrs: SmartTagAttrs) => void;
|
|
10
9
|
deleteSmartTag: (id: string) => void;
|
|
11
|
-
updateSmartTagAttrs: (id: string, updatedAttrs:
|
|
10
|
+
updateSmartTagAttrs: (id: string, updatedAttrs: Partial<SmartTagAttrs>) => void;
|
|
12
11
|
};
|
|
13
12
|
export type HandleLinkRef = {
|
|
14
13
|
setLink: (params: {
|
|
@@ -22,6 +21,11 @@ export type HandleLinkRef = {
|
|
|
22
21
|
updated: (currentText: string) => string;
|
|
23
22
|
mergeAdjacentEqualFn?: (currentAttrs: LinkAttrs, candidateAttrs: LinkAttrs) => boolean;
|
|
24
23
|
}) => void;
|
|
24
|
+
eachLinkGroup: (callback: (params: {
|
|
25
|
+
attrs: LinkAttrs;
|
|
26
|
+
content: string;
|
|
27
|
+
}) => void) => void;
|
|
28
|
+
eachSmartag: (callback: (attrs: SmartTagAttrs) => void) => void;
|
|
25
29
|
};
|
|
26
30
|
export type TextEditorRef = HandleSmartTagRef & HandleLinkRef & {
|
|
27
31
|
blur: (perserveSelection?: boolean) => void;
|
|
@@ -43,6 +47,28 @@ export type LinkHandler = Partial<{
|
|
|
43
47
|
selectionText: string;
|
|
44
48
|
}) => void;
|
|
45
49
|
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Link form data structure
|
|
52
|
+
*/
|
|
53
|
+
export type LinkFormData = {
|
|
54
|
+
/** The URL/href of the link */
|
|
55
|
+
url: string;
|
|
56
|
+
/** The text content to display */
|
|
57
|
+
text: string;
|
|
58
|
+
/** Whether to open link in new tab */
|
|
59
|
+
openInNewTab: boolean;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Link form state in store
|
|
63
|
+
*/
|
|
64
|
+
export type LinkFormState = {
|
|
65
|
+
/** Whether the link form is visible */
|
|
66
|
+
isVisible: boolean;
|
|
67
|
+
/** Mode: insert new link or edit existing link */
|
|
68
|
+
mode: 'insert' | 'edit';
|
|
69
|
+
/** Form data */
|
|
70
|
+
data: LinkFormData;
|
|
71
|
+
} | null;
|
|
46
72
|
export type FontConfig = {
|
|
47
73
|
fontFamily: {
|
|
48
74
|
name: string;
|
|
@@ -73,6 +99,29 @@ export type FontGroupResult = {
|
|
|
73
99
|
* @returns Array of group keys in desired display order
|
|
74
100
|
*/
|
|
75
101
|
export type GroupOrderFunction = (groups: string[]) => string[];
|
|
102
|
+
/**
|
|
103
|
+
* Toolbar action names - union type for type safety
|
|
104
|
+
*/
|
|
105
|
+
export type ToolbarActionName = 'bold' | 'italic' | 'underline' | 'strike' | 'superscript' | 'subscript' | 'fontFamily' | 'fontSize' | 'textColor' | 'backgroundColor' | 'textTransform' | 'bulletList' | 'orderedList' | 'indent' | 'outdent' | 'textAlign' | 'lineSpacing' | 'letterSpacing' | 'link' | 'smartTag' | 'emoji' | 'history' | 'clearFormatting';
|
|
106
|
+
/**
|
|
107
|
+
* Toolbar configuration options
|
|
108
|
+
*/
|
|
109
|
+
export type ToolbarConfig = {
|
|
110
|
+
/**
|
|
111
|
+
* Which toolbar actions to show
|
|
112
|
+
* - Array: Show only specified actions in given order
|
|
113
|
+
* - 'all': Show all available actions (default)
|
|
114
|
+
* - false: Hide toolbar entirely
|
|
115
|
+
*/
|
|
116
|
+
items?: ToolbarActionName[] | 'all' | false;
|
|
117
|
+
/**
|
|
118
|
+
* Which toolbar actions to hide (ignored if items is an array)
|
|
119
|
+
* Useful when you want to show most items but exclude a few
|
|
120
|
+
* @example
|
|
121
|
+
* config={{ Toolbar: { exclude: ['smartTag', 'emoji'] } }}
|
|
122
|
+
*/
|
|
123
|
+
exclude?: ToolbarActionName[];
|
|
124
|
+
};
|
|
76
125
|
/**
|
|
77
126
|
* Configuration options for TextEditor components
|
|
78
127
|
*/
|
|
@@ -91,6 +140,11 @@ export type Config = Partial<{
|
|
|
91
140
|
/** Whether to use custom bullet styles */
|
|
92
141
|
useCustomBullet?: boolean;
|
|
93
142
|
};
|
|
143
|
+
SmartTag: {
|
|
144
|
+
generateId?: () => string;
|
|
145
|
+
};
|
|
146
|
+
/** Toolbar configuration */
|
|
147
|
+
Toolbar: ToolbarConfig;
|
|
94
148
|
}>;
|
|
95
149
|
export type TextStyle = {
|
|
96
150
|
fontFamily: string;
|
|
@@ -109,8 +163,6 @@ export type TextEditorComponentsRender = {
|
|
|
109
163
|
unlink: React.ReactNode;
|
|
110
164
|
}) => React.ReactNode;
|
|
111
165
|
};
|
|
112
|
-
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
113
|
-
export type BubbleMenuProps = Optional<Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> & React.HTMLAttributes<HTMLDivElement>;
|
|
114
166
|
export type TextEditorProps = {
|
|
115
167
|
id?: string;
|
|
116
168
|
className?: string;
|
|
@@ -120,9 +172,9 @@ export type TextEditorProps = {
|
|
|
120
172
|
smartTagHandler?: SmartTagHandler;
|
|
121
173
|
linkHandler?: LinkHandler;
|
|
122
174
|
editable?: boolean;
|
|
123
|
-
bubbleMenuProps?: {
|
|
124
|
-
|
|
125
|
-
}
|
|
175
|
+
bubbleMenuProps?: Pick<BubbleMenuPluginProps, 'options' | 'appendTo'> & {
|
|
176
|
+
enabled?: boolean;
|
|
177
|
+
};
|
|
126
178
|
defaultTextStyle?: Partial<TextStyle>;
|
|
127
179
|
dataAttributes?: Record<`data-${string}`, unknown>;
|
|
128
180
|
onUpdateDebounced?: number;
|
|
@@ -133,6 +185,11 @@ export type TextEditorProps = {
|
|
|
133
185
|
text: string;
|
|
134
186
|
json: JSONContent;
|
|
135
187
|
}) => void;
|
|
188
|
+
onCreate?: (args: {
|
|
189
|
+
html: string;
|
|
190
|
+
text: string;
|
|
191
|
+
json: JSONContent;
|
|
192
|
+
}) => void;
|
|
136
193
|
onChangeFont?: (changeInfo: {
|
|
137
194
|
font: FontConfig;
|
|
138
195
|
weight: number;
|
|
@@ -142,6 +199,13 @@ export type TextEditorProps = {
|
|
|
142
199
|
};
|
|
143
200
|
}) => void;
|
|
144
201
|
};
|
|
202
|
+
export type TextEditorAllProps = TextEditorProps & {
|
|
203
|
+
colors?: string[];
|
|
204
|
+
onChangeColors?: (colors: string[]) => void;
|
|
205
|
+
};
|
|
206
|
+
export type TextEditorWithProviderRef = TextEditorRef & {
|
|
207
|
+
updateColors?: (colors: string[]) => void;
|
|
208
|
+
};
|
|
145
209
|
export type OrderedListStyleType = (typeof ORDERED_LIST_STYLE_TYPE)[keyof typeof ORDERED_LIST_STYLE_TYPE];
|
|
146
210
|
export type UnorderedListStyleType = (typeof UNORDERED_LIST_STYLE_TYPE)[keyof typeof UNORDERED_LIST_STYLE_TYPE];
|
|
147
211
|
export declare const isOrderedListStyleType: (value: unknown) => value is OrderedListStyleType;
|
|
@@ -155,9 +219,19 @@ export type LinkAttrs = {
|
|
|
155
219
|
title: string | null;
|
|
156
220
|
data: Record<string, string>;
|
|
157
221
|
}>;
|
|
222
|
+
export type SmartTagAttrs = {
|
|
223
|
+
id: string;
|
|
224
|
+
sourceId?: string;
|
|
225
|
+
} & Partial<{
|
|
226
|
+
content: string;
|
|
227
|
+
tag: string;
|
|
228
|
+
}>;
|
|
158
229
|
export type LinkMark = OverrideProperties<Mark, {
|
|
159
230
|
attrs: LinkAttrs;
|
|
160
231
|
}>;
|
|
232
|
+
export type SmartTagNode = OverrideProperties<Node, {
|
|
233
|
+
attrs: SmartTagAttrs;
|
|
234
|
+
}>;
|
|
161
235
|
export type LinkMarkRange = OverrideProperties<MarkRange, {
|
|
162
236
|
mark: LinkMark;
|
|
163
237
|
}>;
|
|
@@ -170,6 +244,76 @@ export declare const isLinkMark: (mark: Mark) => mark is {
|
|
|
170
244
|
toJSON: () => any;
|
|
171
245
|
attrs: LinkAttrs;
|
|
172
246
|
};
|
|
247
|
+
type ProtocolOptions = {
|
|
248
|
+
/**
|
|
249
|
+
* The protocol scheme to be registered.
|
|
250
|
+
* @default '''
|
|
251
|
+
* @example 'ftp'
|
|
252
|
+
* @example 'git'
|
|
253
|
+
*/
|
|
254
|
+
scheme: string;
|
|
255
|
+
/**
|
|
256
|
+
* If enabled, it allows optional slashes after the protocol.
|
|
257
|
+
* @default false
|
|
258
|
+
* @example true
|
|
259
|
+
*/
|
|
260
|
+
optionalSlashes?: boolean;
|
|
261
|
+
};
|
|
262
|
+
export type ProtocolConfig = Array<ProtocolOptions | string>;
|
|
263
|
+
export declare const isSmartTagNode: (node: Node) => node is {
|
|
264
|
+
toString: () => string;
|
|
265
|
+
readonly type: import("prosemirror-model").NodeType;
|
|
266
|
+
readonly marks: readonly Mark[];
|
|
267
|
+
readonly content: import("prosemirror-model").Fragment;
|
|
268
|
+
readonly children: readonly Node[];
|
|
269
|
+
readonly text: string | undefined;
|
|
270
|
+
readonly nodeSize: number;
|
|
271
|
+
readonly childCount: number;
|
|
272
|
+
child: (index: number) => Node;
|
|
273
|
+
maybeChild: (index: number) => Node | null;
|
|
274
|
+
forEach: (f: (node: Node, offset: number, index: number) => void) => void;
|
|
275
|
+
nodesBetween: (from: number, to: number, f: (node: Node, pos: number, parent: Node | null, index: number) => boolean | void, startPos?: number | undefined) => void;
|
|
276
|
+
descendants: (f: (node: Node, pos: number, parent: Node | null, index: number) => boolean | void) => void;
|
|
277
|
+
readonly textContent: string;
|
|
278
|
+
textBetween: (from: number, to: number, blockSeparator?: string | null | undefined, leafText?: string | ((leafNode: Node) => string) | null | undefined) => string;
|
|
279
|
+
readonly firstChild: Node | null;
|
|
280
|
+
readonly lastChild: Node | null;
|
|
281
|
+
eq: (other: Node) => boolean;
|
|
282
|
+
sameMarkup: (other: Node) => boolean;
|
|
283
|
+
hasMarkup: (type: import("prosemirror-model").NodeType, attrs?: import("prosemirror-model").Attrs | null | undefined, marks?: readonly Mark[] | undefined) => boolean;
|
|
284
|
+
copy: (content?: import("prosemirror-model").Fragment | null | undefined) => Node;
|
|
285
|
+
mark: (marks: readonly Mark[]) => Node;
|
|
286
|
+
cut: (from: number, to?: number | undefined) => Node;
|
|
287
|
+
slice: (from: number, to?: number | undefined, includeParents?: boolean | undefined) => import("prosemirror-model").Slice;
|
|
288
|
+
replace: (from: number, to: number, slice: import("prosemirror-model").Slice) => Node;
|
|
289
|
+
nodeAt: (pos: number) => Node | null;
|
|
290
|
+
childAfter: (pos: number) => {
|
|
291
|
+
node: Node | null;
|
|
292
|
+
index: number;
|
|
293
|
+
offset: number;
|
|
294
|
+
};
|
|
295
|
+
childBefore: (pos: number) => {
|
|
296
|
+
node: Node | null;
|
|
297
|
+
index: number;
|
|
298
|
+
offset: number;
|
|
299
|
+
};
|
|
300
|
+
resolve: (pos: number) => import("prosemirror-model").ResolvedPos;
|
|
301
|
+
rangeHasMark: (from: number, to: number, type: import("prosemirror-model").MarkType | Mark) => boolean;
|
|
302
|
+
readonly isBlock: boolean;
|
|
303
|
+
readonly isTextblock: boolean;
|
|
304
|
+
readonly inlineContent: boolean;
|
|
305
|
+
readonly isInline: boolean;
|
|
306
|
+
readonly isText: boolean;
|
|
307
|
+
readonly isLeaf: boolean;
|
|
308
|
+
readonly isAtom: boolean;
|
|
309
|
+
contentMatchAt: (index: number) => import("prosemirror-model").ContentMatch;
|
|
310
|
+
canReplace: (from: number, to: number, replacement?: import("prosemirror-model").Fragment | undefined, start?: number | undefined, end?: number | undefined) => boolean;
|
|
311
|
+
canReplaceWith: (from: number, to: number, type: import("prosemirror-model").NodeType, marks?: readonly Mark[] | undefined) => boolean;
|
|
312
|
+
canAppend: (other: Node) => boolean;
|
|
313
|
+
check: () => void;
|
|
314
|
+
toJSON: () => any;
|
|
315
|
+
attrs: SmartTagAttrs;
|
|
316
|
+
};
|
|
173
317
|
export declare const isLinkMarkRange: (markRange: MarkRange) => markRange is {
|
|
174
318
|
from: number;
|
|
175
319
|
to: number;
|
|
@@ -2,4 +2,5 @@ import { CUSTOM_LINK_EXTENSION_NAME, ORDERED_LIST_STYLE_TYPE, UNORDERED_LIST_STY
|
|
|
2
2
|
export const isOrderedListStyleType = (value) => typeof value === 'string' && Object.values(ORDERED_LIST_STYLE_TYPE).some(v => v === value);
|
|
3
3
|
export const isUnorderedListStyleType = (value) => typeof value === 'string' && Object.values(UNORDERED_LIST_STYLE_TYPE).some(v => v === value);
|
|
4
4
|
export const isLinkMark = (mark) => mark.type.name === CUSTOM_LINK_EXTENSION_NAME;
|
|
5
|
+
export const isSmartTagNode = (node) => node.type.name === 'smartTag';
|
|
5
6
|
export const isLinkMarkRange = (markRange) => isLinkMark(markRange.mark);
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
|
|
2
|
+
import { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
|
|
3
|
+
export declare const BubbleMenu: import("react").ForwardRefExoticComponent<Pick<Partial<Omit<Pick<Partial<BubbleMenuPluginProps>, "pluginKey"> & Omit<BubbleMenuPluginProps, "pluginKey">, "element">>, "editor"> & Omit<Omit<Pick<Partial<BubbleMenuPluginProps>, "pluginKey"> & Omit<BubbleMenuPluginProps, "pluginKey">, "element">, "editor"> & import("react").HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCurrentEditor } from '@tiptap/react';
|
|
3
|
-
import { forwardRef, useEffect, useRef } from 'react';
|
|
3
|
+
import { forwardRef, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { BubbleMenuPlugin } from '../../extensions/BubbleMenu';
|
|
6
|
-
import { useDeepCompareEffect
|
|
7
|
-
import { useTextEditorStore } from '../../provider';
|
|
8
|
-
import clsx from 'clsx';
|
|
6
|
+
import { useDeepCompareEffect } from '@antscorp/antsomi-ui/es/hooks';
|
|
9
7
|
import { ANTSOMI_COMPONENT_PREFIX_CLS } from '@antscorp/antsomi-ui/es/constants';
|
|
10
|
-
export const BubbleMenu = forwardRef(({ pluginKey = 'bubbleMenu', editor, updateDelay, resizeDelay, shouldShow = null,
|
|
8
|
+
export const BubbleMenu = forwardRef(({ pluginKey = 'bubbleMenu', editor, updateDelay, resizeDelay, shouldShow = null, getReferencedVirtualElement, appendTo, options, children, ...restProps }, ref) => {
|
|
11
9
|
const menuEl = useRef(document.createElement('div'));
|
|
12
|
-
|
|
13
|
-
const setIsShowBubbleMenu = useTextEditorStore(state => state.setIsShowBubbleMenu);
|
|
10
|
+
menuEl.current.classList.add(`${ANTSOMI_COMPONENT_PREFIX_CLS}-text-editor-bubble-menu`);
|
|
14
11
|
const { editor: currentEditor } = useCurrentEditor();
|
|
12
|
+
/**
|
|
13
|
+
* Tracks whether the bubble menu has been shown at least once.
|
|
14
|
+
* Used for lazy mounting optimization - children are only mounted after first show.
|
|
15
|
+
*/
|
|
16
|
+
const [hasShown, setHasShown] = useState(false);
|
|
17
|
+
/**
|
|
18
|
+
* Ref to track hasShown synchronously (since state updates are async).
|
|
19
|
+
*/
|
|
20
|
+
const hasShownRef = useRef(false);
|
|
21
|
+
/**
|
|
22
|
+
* Tracks whether we're in the first show process (to prevent empty flash).
|
|
23
|
+
* After first show completes, this flag is no longer used.
|
|
24
|
+
*/
|
|
25
|
+
const isFirstShowRef = useRef(false);
|
|
26
|
+
/**
|
|
27
|
+
* The editor instance where the bubble menu plugin will be registered.
|
|
28
|
+
*/
|
|
29
|
+
const pluginEditor = editor || currentEditor;
|
|
15
30
|
useEffect(() => {
|
|
16
31
|
if (typeof ref === 'function') {
|
|
17
32
|
ref(menuEl.current);
|
|
@@ -20,66 +35,80 @@ export const BubbleMenu = forwardRef(({ pluginKey = 'bubbleMenu', editor, update
|
|
|
20
35
|
ref.current = menuEl.current;
|
|
21
36
|
}
|
|
22
37
|
}, [ref]);
|
|
38
|
+
const bubbleMenuPluginProps = {
|
|
39
|
+
updateDelay,
|
|
40
|
+
resizeDelay,
|
|
41
|
+
pluginKey,
|
|
42
|
+
shouldShow,
|
|
43
|
+
getReferencedVirtualElement,
|
|
44
|
+
options: {
|
|
45
|
+
...options,
|
|
46
|
+
strategy: options?.strategy || 'absolute',
|
|
47
|
+
placement: options?.placement || 'bottom',
|
|
48
|
+
offset: options?.offset || 10,
|
|
49
|
+
onHide: () => {
|
|
50
|
+
options?.onHide?.();
|
|
51
|
+
},
|
|
52
|
+
onShow: () => {
|
|
53
|
+
if (!hasShownRef.current) {
|
|
54
|
+
// First show: mark as first show and force hide until children mount
|
|
55
|
+
hasShownRef.current = true;
|
|
56
|
+
isFirstShowRef.current = true;
|
|
57
|
+
menuEl.current.style.visibility = 'hidden';
|
|
58
|
+
setHasShown(true);
|
|
59
|
+
}
|
|
60
|
+
options?.onShow?.();
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
const bubbleMenuPluginPropsRef = useRef(bubbleMenuPluginProps);
|
|
65
|
+
bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps;
|
|
66
|
+
useLayoutEffect(() => {
|
|
67
|
+
// After children mount in first show, make menuEl visible
|
|
68
|
+
if (isFirstShowRef.current) {
|
|
69
|
+
menuEl.current.style.visibility = 'visible';
|
|
70
|
+
isFirstShowRef.current = false;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
23
73
|
useDeepCompareEffect(() => {
|
|
24
|
-
|
|
25
|
-
bubbleMenuElement.className = clsx(className, `${ANTSOMI_COMPONENT_PREFIX_CLS}-text-editor-bubble-menu`);
|
|
26
|
-
if (editor?.isDestroyed || currentEditor?.isDestroyed) {
|
|
74
|
+
if (pluginEditor?.isDestroyed) {
|
|
27
75
|
return;
|
|
28
76
|
}
|
|
29
|
-
|
|
30
|
-
if (!attachToEditor) {
|
|
77
|
+
if (!pluginEditor) {
|
|
31
78
|
// eslint-disable-next-line no-console
|
|
32
79
|
console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.');
|
|
33
80
|
return;
|
|
34
81
|
}
|
|
82
|
+
const bubbleMenuElement = menuEl.current;
|
|
83
|
+
bubbleMenuElement.style.visibility = 'hidden';
|
|
84
|
+
bubbleMenuElement.style.position = 'absolute';
|
|
35
85
|
const plugin = BubbleMenuPlugin({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
editor:
|
|
86
|
+
...bubbleMenuPluginPropsRef.current,
|
|
87
|
+
appendTo,
|
|
88
|
+
editor: pluginEditor,
|
|
39
89
|
element: bubbleMenuElement,
|
|
40
|
-
container,
|
|
41
|
-
pluginKey,
|
|
42
|
-
options: {
|
|
43
|
-
...options,
|
|
44
|
-
strategy: options?.strategy || 'absolute',
|
|
45
|
-
placement: options?.placement || 'bottom',
|
|
46
|
-
offset: options?.offset,
|
|
47
|
-
onShow: () => {
|
|
48
|
-
if (isMounted()) {
|
|
49
|
-
setIsShowBubbleMenu(true);
|
|
50
|
-
}
|
|
51
|
-
options?.onShow?.();
|
|
52
|
-
},
|
|
53
|
-
onHide: () => {
|
|
54
|
-
if (isMounted()) {
|
|
55
|
-
setIsShowBubbleMenu(false);
|
|
56
|
-
}
|
|
57
|
-
options?.onHide?.();
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
shouldShow,
|
|
61
90
|
});
|
|
62
|
-
|
|
91
|
+
pluginEditor.registerPlugin(plugin);
|
|
92
|
+
const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey;
|
|
63
93
|
return () => {
|
|
64
|
-
|
|
94
|
+
pluginEditor.unregisterPlugin(createdPluginKey);
|
|
65
95
|
window.requestAnimationFrame(() => {
|
|
66
96
|
if (bubbleMenuElement.parentNode) {
|
|
67
97
|
bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
|
|
68
98
|
}
|
|
69
99
|
});
|
|
70
100
|
};
|
|
71
|
-
}, [
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
]);
|
|
101
|
+
}, [appendTo, pluginEditor]);
|
|
102
|
+
/**
|
|
103
|
+
* Lazy mounting optimization: only mount children after the bubble menu
|
|
104
|
+
* has been shown at least once. This significantly improves performance
|
|
105
|
+
* when many TextEditor instances exist (e.g., in lists).
|
|
106
|
+
*
|
|
107
|
+
* After first show, children remain mounted and the tiptap plugin
|
|
108
|
+
* handles visibility automatically via CSS, avoiding flash effects.
|
|
109
|
+
*/
|
|
110
|
+
if (!hasShown) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
84
113
|
return createPortal(_jsx("div", { ...restProps, children: children }), menuEl.current);
|
|
85
114
|
});
|
|
@@ -44,7 +44,7 @@ export const EmojiList = forwardRef((props, ref) => {
|
|
|
44
44
|
if (items.length <= 0) {
|
|
45
45
|
return null;
|
|
46
46
|
}
|
|
47
|
-
return (_jsx(VirtualizedMenu, { className: `${COMPONENT_CLS}-emoji-list`, itemSize: ITEM_SIZE, selected: items.at(selectedIndex) ? [items[selectedIndex].slug] : [], onClick: item => {
|
|
47
|
+
return (_jsx(VirtualizedMenu, { className: `${COMPONENT_CLS}-emoji-list`, itemSize: ITEM_SIZE, selected: items.at(selectedIndex) ? [items[selectedIndex].slug] : [], focusedKey: items[selectedIndex]?.slug, onClick: item => {
|
|
48
48
|
const idx = items.findIndex(i => i.slug === item.item.key);
|
|
49
49
|
if (idx !== -1) {
|
|
50
50
|
selectItem(idx);
|
|
@@ -10,7 +10,7 @@ const updatePosition = (editor, element, itemLength) => {
|
|
|
10
10
|
getBoundingClientRect: () => posToDOMRect(editor.view, editor.state.selection.from, editor.state.selection.to),
|
|
11
11
|
};
|
|
12
12
|
computePosition(virtualElement, element, {
|
|
13
|
-
placement: '
|
|
13
|
+
placement: 'bottom-start',
|
|
14
14
|
strategy: 'absolute',
|
|
15
15
|
middleware: [shift(), flip()],
|
|
16
16
|
}).then(({ x, y, strategy }) => {
|
|
@@ -45,7 +45,7 @@ export const suggestion = ({ container }) => ({
|
|
|
45
45
|
}
|
|
46
46
|
const rendererEl = safeGetRenndererEl();
|
|
47
47
|
rendererEl.classList.add(`${COMPONENT_CLS}-emoji-popover`);
|
|
48
|
-
(container || document.body).appendChild(rendererEl);
|
|
48
|
+
((typeof container === 'function' ? container() : container) || document.body).appendChild(rendererEl);
|
|
49
49
|
updatePosition(props.editor, rendererEl, props.items.length);
|
|
50
50
|
},
|
|
51
51
|
onUpdate(props) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Typography } from 'antd';
|
|
4
4
|
import { SearchPopover, VirtualizedMenu } from '@antscorp/antsomi-ui/es/components/molecules';
|
|
5
5
|
import { FontGroupTitle, PopoverTrigger } from './styled';
|
|
@@ -52,6 +52,7 @@ export const FontPopover = memo((props) => {
|
|
|
52
52
|
const itemSize = 30;
|
|
53
53
|
const itemSpacing = 4;
|
|
54
54
|
const activeFontName = fontValue?.font.fontFamily.name;
|
|
55
|
+
const inputSearchRef = useRef(null);
|
|
55
56
|
const [search, setSearch] = useState('');
|
|
56
57
|
const [open, setOpen] = useState(false);
|
|
57
58
|
const [fontItemsSize, setFontItemSize] = useDebounce(0, 200);
|
|
@@ -83,10 +84,19 @@ export const FontPopover = memo((props) => {
|
|
|
83
84
|
const listSize = calInlineListSize(Math.min(items.length, 6), itemSize, itemSpacing);
|
|
84
85
|
setFontItemSize(listSize);
|
|
85
86
|
}, [items.length, setFontItemSize]);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (open) {
|
|
89
|
+
// Use setTimeout to ensure the popover content is fully mounted in the DOM
|
|
90
|
+
const timeoutId = setTimeout(() => {
|
|
91
|
+
inputSearchRef.current?.focus();
|
|
92
|
+
}, 0);
|
|
93
|
+
return () => clearTimeout(timeoutId);
|
|
94
|
+
}
|
|
95
|
+
}, [open]);
|
|
86
96
|
return (_jsx(SearchPopover, { open: open, placement: "bottomLeft", onOpenChange: handleOpenChange, getPopupContainer: () => bubbleMenuContainer || document.body, inputSearchProps: {
|
|
87
97
|
value: search,
|
|
88
98
|
onAfterChange: setSearch,
|
|
89
|
-
}, overlayClassName: "font-popover-overlay", content: _jsx("div", { className: "list-font-root", style: {
|
|
99
|
+
}, inputSearchRef: inputSearchRef, overlayClassName: "font-popover-overlay", content: _jsx("div", { className: "list-font-root", style: {
|
|
90
100
|
height: `${fontItemsSize}px`,
|
|
91
101
|
}, children: _jsx(VirtualizedMenu, { itemSize: itemSize, inlinePadding: 0, itemSpacing: itemSpacing, selectable: false, items: items }) }), children: _jsx(PopoverTrigger, { className: "font-popover-trigger", children: _jsx(Typography.Text, { ellipsis: { tooltip: true }, children: activeFontName }) }) }));
|
|
92
102
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/core';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { LinkFormData } from '../../types';
|
|
4
|
+
export type LinkInsertFormProps = {
|
|
5
|
+
/** TipTap editor instance */
|
|
6
|
+
editor: Editor;
|
|
7
|
+
/** Form mode: insert or edit */
|
|
8
|
+
mode: 'insert' | 'edit';
|
|
9
|
+
/** Initial form data */
|
|
10
|
+
initialData: LinkFormData;
|
|
11
|
+
/** Callback when form is submitted */
|
|
12
|
+
onSubmit: () => void;
|
|
13
|
+
/** Callback when form is cancelled */
|
|
14
|
+
onCancel: () => void;
|
|
15
|
+
};
|
|
16
|
+
export declare const LinkInsertForm: React.FC<LinkInsertFormProps>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Checkbox, Flex, Input } from 'antd';
|
|
3
|
+
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
4
|
+
export const LinkInsertForm = props => {
|
|
5
|
+
const { editor, mode, initialData, onSubmit, onCancel } = props;
|
|
6
|
+
const [url, setUrl] = useState(initialData.url);
|
|
7
|
+
const [text, setText] = useState(initialData.text);
|
|
8
|
+
const [openInNewTab, setOpenInNewTab] = useState(initialData.openInNewTab);
|
|
9
|
+
const urlInputRef = useRef(null);
|
|
10
|
+
// Auto-focus the URL input without scrolling
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
urlInputRef.current?.focus({
|
|
13
|
+
preventScroll: true,
|
|
14
|
+
});
|
|
15
|
+
}, []);
|
|
16
|
+
const handleSubmit = useCallback(() => {
|
|
17
|
+
if (!url.trim()) {
|
|
18
|
+
// Don't submit if URL is empty
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// If text is different from initial, update the content
|
|
22
|
+
if (text !== initialData.text && text.trim()) {
|
|
23
|
+
editor
|
|
24
|
+
.chain()
|
|
25
|
+
.focus()
|
|
26
|
+
.setCustomLink({
|
|
27
|
+
attrs: {
|
|
28
|
+
href: url,
|
|
29
|
+
target: openInNewTab ? '_blank' : null,
|
|
30
|
+
},
|
|
31
|
+
content: text,
|
|
32
|
+
})
|
|
33
|
+
.run();
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Just update the link attributes
|
|
37
|
+
editor
|
|
38
|
+
.chain()
|
|
39
|
+
.focus()
|
|
40
|
+
.setCustomLink({
|
|
41
|
+
attrs: {
|
|
42
|
+
href: url,
|
|
43
|
+
target: openInNewTab ? '_blank' : null,
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
.run();
|
|
47
|
+
}
|
|
48
|
+
onSubmit();
|
|
49
|
+
}, [editor, url, text, initialData.text, openInNewTab, onSubmit]);
|
|
50
|
+
const handleKeyDown = useCallback((e) => {
|
|
51
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
handleSubmit();
|
|
54
|
+
}
|
|
55
|
+
else if (e.key === 'Escape') {
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
onCancel();
|
|
58
|
+
}
|
|
59
|
+
}, [handleSubmit, onCancel]);
|
|
60
|
+
return (_jsxs(Flex, { vertical: true, gap: 8, children: [_jsxs(Flex, { gap: 8, vertical: true, children: [_jsx(Input, { ref: urlInputRef, placeholder: "URL", value: url, onChange: e => setUrl(e.target.value), onKeyDown: handleKeyDown }), _jsx(Input, { placeholder: "Text", value: text, onChange: e => setText(e.target.value), onKeyDown: handleKeyDown })] }), _jsx(Checkbox, { checked: openInNewTab, onChange: e => setOpenInNewTab(e.target.checked), children: "Open in new tab" }), _jsxs(Flex, { justify: "flex-end", gap: 8, children: [_jsx(Button, { onClick: onCancel, children: "Cancel" }), _jsx(Button, { type: "primary", onClick: handleSubmit, disabled: !url.trim(), children: mode === 'insert' ? 'Insert' : 'Update' })] })] }));
|
|
61
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LinkInsertForm } from './LinkInsertForm';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export type LinkPopoverProps = {
|
|
4
|
+
/** TipTap editor instance */
|
|
5
|
+
editor: Editor;
|
|
6
|
+
/** Optional container to append the popover to. Defaults to document.body */
|
|
7
|
+
container?: HTMLElement | (() => HTMLElement);
|
|
8
|
+
};
|
|
9
|
+
export declare const LinkPopover: React.FC<LinkPopoverProps>;
|