payload-richtext-tiptap 0.0.158 → 0.0.160
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/dist/src/fields/TiptapEditor/extensions/BlockquoteFigure/BlockquoteFigure.d.ts +1 -1
- package/dist/src/fields/TiptapEditor/extensions/BlockquoteFigure/BlockquoteFigure.d.ts.map +1 -1
- package/dist/src/fields/TiptapEditor/extensions/BlockquoteFigure/BlockquoteFigure.js +61 -15
- package/dist/src/fields/TiptapEditor/extensions/BlockquoteFigure/BlockquoteFigure.js.map +1 -1
- package/dist/src/fields/TiptapEditor/extensions/BlockquoteFigure/Quote/Quote.d.ts.map +1 -1
- package/dist/src/fields/TiptapEditor/extensions/BlockquoteFigure/Quote/Quote.js +1 -3
- package/dist/src/fields/TiptapEditor/extensions/BlockquoteFigure/Quote/Quote.js.map +1 -1
- package/dist/src/fields/TiptapEditor/extensions/BlockquoteFigure/QuoteCaption/QuoteCaption.js.map +1 -1
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/GifPlayer.d.ts +12 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/GifPlayer.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/GifPlayer.js +152 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/GifPlayer.js.map +1 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/SmartImage.d.ts +12 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/SmartImage.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/SmartImage.js +55 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/SmartImage.js.map +1 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/TestImage.d.ts +4 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/TestImage.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/TestImage.js +26 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/components/TestImage.js.map +1 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/utils/gifUtils.d.ts +31 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/utils/gifUtils.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/utils/gifUtils.js +62 -0
- package/dist/src/fields/TiptapEditor/extensions/ImageBlock/utils/gifUtils.js.map +1 -0
- package/dist/src/fields/TiptapEditor/extensions/Paragraph/Paragraph.d.ts.map +1 -1
- package/dist/src/fields/TiptapEditor/extensions/Paragraph/Paragraph.js +27 -4
- package/dist/src/fields/TiptapEditor/extensions/Paragraph/Paragraph.js.map +1 -1
- package/dist/src/fields/TiptapEditor/extensions/extension-kit.d.ts +3 -3
- package/dist/src/fields/TiptapEditor/extensions/extension-kit.d.ts.map +1 -1
- package/dist/src/fields/TiptapEditor/extensions/extension-kit.js +23 -21
- package/dist/src/fields/TiptapEditor/extensions/extension-kit.js.map +1 -1
- package/dist/src/fields/TiptapEditor/features/menus/TextMenu/TextMenu.d.ts +2 -2
- package/dist/src/fields/TiptapEditor/features/menus/TextMenu/TextMenu.d.ts.map +1 -1
- package/dist/src/fields/TiptapEditor/features/menus/TextMenu/TextMenu.js +55 -61
- package/dist/src/fields/TiptapEditor/features/menus/TextMenu/TextMenu.js.map +1 -1
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashCommandTriggerButton.d.ts +4 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashCommandTriggerButton.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashCommandTriggerButton.js +32 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashCommandTriggerButton.js.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashDropdownMenu.d.ts +4 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashDropdownMenu.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashDropdownMenu.js +70 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashDropdownMenu.js.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashMenuList.d.ts +10 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashMenuList.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashMenuList.js +249 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SlashMenuList.js.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SuggestionMenu.d.ts +15 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SuggestionMenu.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SuggestionMenu.js +291 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/SuggestionMenu.js.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/index.d.ts +7 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/index.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/index.js +7 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/index.js.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/types.d.ts +28 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/types.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/types.js +3 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/types.js.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/utils.d.ts +6 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/utils.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/utils.js +222 -0
- package/dist/src/fields/TiptapEditor/features/ui/SlashCommand/utils.js.map +1 -0
- package/dist/src/fields/TiptapEditor/lib/utils/updateImageUrl.d.ts +2 -0
- package/dist/src/fields/TiptapEditor/lib/utils/updateImageUrl.d.ts.map +1 -0
- package/dist/src/fields/TiptapEditor/lib/utils/updateImageUrl.js +17 -0
- package/dist/src/fields/TiptapEditor/lib/utils/updateImageUrl.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { ReactRenderer } from '@tiptap/react';
|
|
3
|
+
import Suggestion from '@tiptap/suggestion';
|
|
4
|
+
import { PluginKey } from '@tiptap/pm/state';
|
|
5
|
+
import { computePosition, offset, autoUpdate } from '@floating-ui/dom';
|
|
6
|
+
let popupElement = null;
|
|
7
|
+
let cleanupAutoUpdate = null;
|
|
8
|
+
let isPopupVisible = false;
|
|
9
|
+
// Helper functions to manage Floating UI popup
|
|
10
|
+
const initPopup = ()=>{
|
|
11
|
+
if (!popupElement && typeof document !== 'undefined') {
|
|
12
|
+
popupElement = document.createElement('div');
|
|
13
|
+
popupElement.style.position = 'fixed';
|
|
14
|
+
popupElement.style.zIndex = '10000';
|
|
15
|
+
popupElement.style.maxWidth = '16rem';
|
|
16
|
+
popupElement.style.visibility = 'hidden';
|
|
17
|
+
popupElement.style.opacity = '0';
|
|
18
|
+
popupElement.style.transition = 'opacity 200ms';
|
|
19
|
+
popupElement.className = 'slash-command-menu';
|
|
20
|
+
document.body.appendChild(popupElement);
|
|
21
|
+
}
|
|
22
|
+
return popupElement;
|
|
23
|
+
};
|
|
24
|
+
const showPopup = ()=>{
|
|
25
|
+
if (popupElement && !isPopupVisible) {
|
|
26
|
+
isPopupVisible = true;
|
|
27
|
+
popupElement.style.display = 'block';
|
|
28
|
+
popupElement.style.visibility = 'visible';
|
|
29
|
+
popupElement.style.opacity = '1';
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const hidePopup = ()=>{
|
|
33
|
+
if (popupElement) {
|
|
34
|
+
isPopupVisible = false;
|
|
35
|
+
popupElement.style.visibility = 'hidden';
|
|
36
|
+
popupElement.style.opacity = '0';
|
|
37
|
+
// Use setTimeout to wait for transition before hiding completely
|
|
38
|
+
setTimeout(()=>{
|
|
39
|
+
if (popupElement && !isPopupVisible) {
|
|
40
|
+
popupElement.style.display = 'none';
|
|
41
|
+
}
|
|
42
|
+
}, 200);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const updatePopupPosition = async (getReferenceRect)=>{
|
|
46
|
+
if (!popupElement || !isPopupVisible) return;
|
|
47
|
+
try {
|
|
48
|
+
const rect = getReferenceRect();
|
|
49
|
+
const virtualElement = {
|
|
50
|
+
getBoundingClientRect: ()=>rect
|
|
51
|
+
};
|
|
52
|
+
const { x, y } = await computePosition(virtualElement, popupElement, {
|
|
53
|
+
placement: 'bottom-start',
|
|
54
|
+
strategy: 'fixed',
|
|
55
|
+
middleware: [
|
|
56
|
+
offset({
|
|
57
|
+
mainAxis: 8,
|
|
58
|
+
crossAxis: 16
|
|
59
|
+
})
|
|
60
|
+
]
|
|
61
|
+
});
|
|
62
|
+
popupElement.style.left = `${x}px`;
|
|
63
|
+
popupElement.style.top = `${y}px`;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Error updating popup position:', error);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
export const SuggestionMenu = ({ editor, char, items, children })=>{
|
|
69
|
+
const [suggestionProps, setSuggestionProps] = useState(null);
|
|
70
|
+
const suggestionRef = useRef(null);
|
|
71
|
+
useEffect(()=>{
|
|
72
|
+
if (!editor) return;
|
|
73
|
+
// Clean up existing suggestion if it exists
|
|
74
|
+
if (suggestionRef.current) {
|
|
75
|
+
editor.unregisterPlugin(suggestionRef.current);
|
|
76
|
+
}
|
|
77
|
+
const suggestion = Suggestion({
|
|
78
|
+
editor,
|
|
79
|
+
char,
|
|
80
|
+
allowSpaces: true,
|
|
81
|
+
startOfLine: true,
|
|
82
|
+
pluginKey: new PluginKey('slash-suggestion'),
|
|
83
|
+
allow: ({ state, range })=>{
|
|
84
|
+
const $from = state.doc.resolve(range.from);
|
|
85
|
+
const isRootDepth = $from.depth === 1;
|
|
86
|
+
const isParagraph = $from.parent.type.name === 'paragraph';
|
|
87
|
+
const isStartOfNode = $from.parent.textContent?.charAt(0) === char;
|
|
88
|
+
const isInColumn = editor.isActive('column');
|
|
89
|
+
const afterContent = $from.parent.textContent?.substring($from.parent.textContent?.indexOf(char));
|
|
90
|
+
const isValidAfterContent = !afterContent?.endsWith(' ');
|
|
91
|
+
// Check if drag handle is locked (ContentItemMenu is open)
|
|
92
|
+
const isDragHandleLocked = editor.storage.dragHandle?.lockDragHandle;
|
|
93
|
+
const shouldAllow = (isRootDepth && isParagraph && isStartOfNode || isInColumn && isParagraph && isStartOfNode) && isValidAfterContent && !isDragHandleLocked;
|
|
94
|
+
return shouldAllow;
|
|
95
|
+
},
|
|
96
|
+
command: ({ editor, props })=>{
|
|
97
|
+
const { view, state } = editor;
|
|
98
|
+
const { $head, $from } = view.state.selection;
|
|
99
|
+
const end = $from.pos;
|
|
100
|
+
const from = $head?.nodeBefore ? end - ($head.nodeBefore.text?.substring($head.nodeBefore.text?.indexOf(char)).length ?? 0) : $from.start();
|
|
101
|
+
const tr = state.tr.deleteRange(from, end);
|
|
102
|
+
view.dispatch(tr);
|
|
103
|
+
props.onSelect({
|
|
104
|
+
editor
|
|
105
|
+
});
|
|
106
|
+
view.focus();
|
|
107
|
+
},
|
|
108
|
+
items: ({ query })=>{
|
|
109
|
+
return items({
|
|
110
|
+
query,
|
|
111
|
+
editor
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
render: ()=>{
|
|
115
|
+
let component;
|
|
116
|
+
let scrollHandler = null;
|
|
117
|
+
return {
|
|
118
|
+
onStart: (props)=>{
|
|
119
|
+
setSuggestionProps(props);
|
|
120
|
+
props.editor.storage.slashSuggestion = {
|
|
121
|
+
...props.editor.storage.slashSuggestion,
|
|
122
|
+
isActive: true
|
|
123
|
+
};
|
|
124
|
+
// Temporarily disable drag handle to prevent interference
|
|
125
|
+
props.editor.commands.setMeta('hideDragHandle', true);
|
|
126
|
+
component = new ReactRenderer(children, {
|
|
127
|
+
props,
|
|
128
|
+
editor: props.editor
|
|
129
|
+
});
|
|
130
|
+
const { view } = props.editor;
|
|
131
|
+
// Initialize the popup if needed
|
|
132
|
+
initPopup();
|
|
133
|
+
if (popupElement) {
|
|
134
|
+
// Clear previous content
|
|
135
|
+
popupElement.innerHTML = '';
|
|
136
|
+
// Append the component
|
|
137
|
+
popupElement.appendChild(component.element);
|
|
138
|
+
// Set up positioning
|
|
139
|
+
const getReferenceRect = ()=>{
|
|
140
|
+
const { state } = view;
|
|
141
|
+
const { selection } = state;
|
|
142
|
+
const coords = view.coordsAtPos(selection.from);
|
|
143
|
+
const node = view.nodeDOM(selection.from);
|
|
144
|
+
if (node && node instanceof Element) {
|
|
145
|
+
const rect = node.getBoundingClientRect();
|
|
146
|
+
return new DOMRect(coords.left, coords.top, 0, 0);
|
|
147
|
+
}
|
|
148
|
+
return new DOMRect(coords.left, coords.top, 0, 0);
|
|
149
|
+
};
|
|
150
|
+
// Set up auto-update for positioning
|
|
151
|
+
if (cleanupAutoUpdate) cleanupAutoUpdate();
|
|
152
|
+
cleanupAutoUpdate = autoUpdate({
|
|
153
|
+
getBoundingClientRect: getReferenceRect
|
|
154
|
+
}, popupElement, async ()=>{
|
|
155
|
+
await updatePopupPosition(getReferenceRect);
|
|
156
|
+
});
|
|
157
|
+
scrollHandler = ()=>{
|
|
158
|
+
updatePopupPosition(getReferenceRect);
|
|
159
|
+
};
|
|
160
|
+
view.dom.parentElement?.addEventListener('scroll', scrollHandler);
|
|
161
|
+
// Show the popup
|
|
162
|
+
showPopup();
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
onUpdate (props) {
|
|
166
|
+
setSuggestionProps(props);
|
|
167
|
+
component.updateProps(props);
|
|
168
|
+
// Update positioning for the popup
|
|
169
|
+
const { view } = props.editor;
|
|
170
|
+
const { state } = view;
|
|
171
|
+
const { selection } = state;
|
|
172
|
+
const coords = view.coordsAtPos(selection.from);
|
|
173
|
+
const getReferenceRect = ()=>{
|
|
174
|
+
return new DOMRect(coords.left, coords.top, 0, 0);
|
|
175
|
+
};
|
|
176
|
+
updatePopupPosition(getReferenceRect);
|
|
177
|
+
},
|
|
178
|
+
onKeyDown (props) {
|
|
179
|
+
if (props.event.key === 'Escape') {
|
|
180
|
+
hidePopup();
|
|
181
|
+
// Return focus to the editor
|
|
182
|
+
setTimeout(()=>{
|
|
183
|
+
editor.view.focus();
|
|
184
|
+
}, 0);
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
// Only move focus to menu for arrow keys
|
|
188
|
+
if (props.event.key === 'ArrowDown' || props.event.key === 'ArrowUp') {
|
|
189
|
+
if (!isPopupVisible) {
|
|
190
|
+
showPopup();
|
|
191
|
+
}
|
|
192
|
+
// Focus the menu element for navigation
|
|
193
|
+
setTimeout(()=>{
|
|
194
|
+
const menuElement = component.element.querySelector('[tabindex="0"]');
|
|
195
|
+
if (menuElement) {
|
|
196
|
+
menuElement.focus();
|
|
197
|
+
}
|
|
198
|
+
}, 0);
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
// Handle Enter key when menu is focused
|
|
202
|
+
if (props.event.key === 'Enter') {
|
|
203
|
+
if (!isPopupVisible) {
|
|
204
|
+
showPopup();
|
|
205
|
+
}
|
|
206
|
+
// Focus the menu element and let it handle Enter
|
|
207
|
+
setTimeout(()=>{
|
|
208
|
+
const menuElement = component.element.querySelector('[tabindex="0"]');
|
|
209
|
+
if (menuElement) {
|
|
210
|
+
menuElement.focus();
|
|
211
|
+
// Trigger Enter key on the menu
|
|
212
|
+
const enterEvent = new KeyboardEvent('keydown', {
|
|
213
|
+
key: 'Enter',
|
|
214
|
+
code: 'Enter',
|
|
215
|
+
keyCode: 13,
|
|
216
|
+
which: 13,
|
|
217
|
+
bubbles: true,
|
|
218
|
+
cancelable: true
|
|
219
|
+
});
|
|
220
|
+
menuElement.dispatchEvent(enterEvent);
|
|
221
|
+
}
|
|
222
|
+
}, 0);
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
// For any other key (letters, numbers, etc.), ensure editor has focus
|
|
226
|
+
// This handles the case where user types after opening the menu
|
|
227
|
+
if (!isPopupVisible) {
|
|
228
|
+
showPopup();
|
|
229
|
+
}
|
|
230
|
+
// Ensure editor has focus for typing
|
|
231
|
+
setTimeout(()=>{
|
|
232
|
+
editor.view.focus();
|
|
233
|
+
}, 0);
|
|
234
|
+
// Let the editor handle the key normally
|
|
235
|
+
return false;
|
|
236
|
+
},
|
|
237
|
+
onExit (props) {
|
|
238
|
+
// Set slash command as inactive in storage
|
|
239
|
+
;
|
|
240
|
+
props.editor.storage.slashSuggestion = {
|
|
241
|
+
...props.editor.storage.slashSuggestion,
|
|
242
|
+
isActive: false
|
|
243
|
+
};
|
|
244
|
+
// Re-enable drag handle
|
|
245
|
+
props.editor.commands.setMeta('hideDragHandle', false);
|
|
246
|
+
// Clean up scroll handler first
|
|
247
|
+
if (scrollHandler) {
|
|
248
|
+
const { view } = props.editor;
|
|
249
|
+
view.dom.parentElement?.removeEventListener('scroll', scrollHandler);
|
|
250
|
+
scrollHandler = null;
|
|
251
|
+
}
|
|
252
|
+
// Clean up auto-update listener
|
|
253
|
+
if (cleanupAutoUpdate) {
|
|
254
|
+
cleanupAutoUpdate();
|
|
255
|
+
cleanupAutoUpdate = null;
|
|
256
|
+
}
|
|
257
|
+
// Hide the popup
|
|
258
|
+
hidePopup();
|
|
259
|
+
// Clean up component
|
|
260
|
+
component?.destroy?.();
|
|
261
|
+
component = null;
|
|
262
|
+
setSuggestionProps(null);
|
|
263
|
+
// Clear popup content
|
|
264
|
+
if (popupElement) {
|
|
265
|
+
popupElement.innerHTML = '';
|
|
266
|
+
}
|
|
267
|
+
// Return focus to the editor
|
|
268
|
+
setTimeout(()=>{
|
|
269
|
+
props.editor.view.focus();
|
|
270
|
+
}, 0);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
editor.registerPlugin(suggestion);
|
|
276
|
+
suggestionRef.current = suggestion;
|
|
277
|
+
return ()=>{
|
|
278
|
+
if (suggestionRef.current) {
|
|
279
|
+
editor.unregisterPlugin(suggestionRef.current);
|
|
280
|
+
suggestionRef.current = null;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}, [
|
|
284
|
+
editor,
|
|
285
|
+
char,
|
|
286
|
+
items
|
|
287
|
+
]);
|
|
288
|
+
return null;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
//# sourceMappingURL=SuggestionMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/fields/TiptapEditor/features/ui/SlashCommand/SuggestionMenu.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react'\r\nimport { Editor } from '@tiptap/core'\r\nimport { ReactRenderer } from '@tiptap/react'\r\nimport Suggestion, { SuggestionProps, SuggestionKeyDownProps } from '@tiptap/suggestion'\r\nimport { PluginKey } from '@tiptap/pm/state'\r\nimport { SuggestionItem } from './types.js'\r\nimport { computePosition, offset, autoUpdate } from '@floating-ui/dom'\r\n\r\ninterface SuggestionMenuProps {\r\n editor: Editor\r\n char: string\r\n items: (props: { query: string; editor: Editor }) => SuggestionItem[]\r\n children: (props: any) => React.ReactElement\r\n}\r\n\r\nlet popupElement: HTMLElement | null = null\r\nlet cleanupAutoUpdate: (() => void) | null = null\r\nlet isPopupVisible = false\r\n\r\n// Helper functions to manage Floating UI popup\r\nconst initPopup = () => {\r\n if (!popupElement && typeof document !== 'undefined') {\r\n popupElement = document.createElement('div')\r\n popupElement.style.position = 'fixed'\r\n popupElement.style.zIndex = '10000'\r\n popupElement.style.maxWidth = '16rem'\r\n popupElement.style.visibility = 'hidden'\r\n popupElement.style.opacity = '0'\r\n popupElement.style.transition = 'opacity 200ms'\r\n popupElement.className = 'slash-command-menu'\r\n document.body.appendChild(popupElement)\r\n }\r\n return popupElement\r\n}\r\n\r\nconst showPopup = () => {\r\n if (popupElement && !isPopupVisible) {\r\n isPopupVisible = true\r\n popupElement.style.display = 'block'\r\n popupElement.style.visibility = 'visible'\r\n popupElement.style.opacity = '1'\r\n }\r\n}\r\n\r\nconst hidePopup = () => {\r\n if (popupElement) {\r\n isPopupVisible = false\r\n popupElement.style.visibility = 'hidden'\r\n popupElement.style.opacity = '0'\r\n // Use setTimeout to wait for transition before hiding completely\r\n setTimeout(() => {\r\n if (popupElement && !isPopupVisible) {\r\n popupElement.style.display = 'none'\r\n }\r\n }, 200)\r\n }\r\n}\r\n\r\nconst updatePopupPosition = async (getReferenceRect: () => DOMRect) => {\r\n if (!popupElement || !isPopupVisible) return\r\n\r\n try {\r\n const rect = getReferenceRect()\r\n\r\n const virtualElement = {\r\n getBoundingClientRect: () => rect,\r\n }\r\n\r\n const { x, y } = await computePosition(virtualElement, popupElement, {\r\n placement: 'bottom-start',\r\n strategy: 'fixed',\r\n middleware: [offset({ mainAxis: 8, crossAxis: 16 })],\r\n })\r\n\r\n popupElement.style.left = `${x}px`\r\n popupElement.style.top = `${y}px`\r\n } catch (error) {\r\n console.error('Error updating popup position:', error)\r\n }\r\n}\r\n\r\nexport const SuggestionMenu = ({ editor, char, items, children }: SuggestionMenuProps) => {\r\n const [suggestionProps, setSuggestionProps] = useState<SuggestionProps | null>(null)\r\n const suggestionRef = useRef<any>(null)\r\n\r\n useEffect(() => {\r\n if (!editor) return\r\n\r\n // Clean up existing suggestion if it exists\r\n if (suggestionRef.current) {\r\n editor.unregisterPlugin(suggestionRef.current)\r\n }\r\n\r\n const suggestion = Suggestion({\r\n editor,\r\n char,\r\n allowSpaces: true,\r\n startOfLine: true,\r\n pluginKey: new PluginKey('slash-suggestion'),\r\n allow: ({ state, range }) => {\r\n const $from = state.doc.resolve(range.from)\r\n const isRootDepth = $from.depth === 1\r\n const isParagraph = $from.parent.type.name === 'paragraph'\r\n const isStartOfNode = $from.parent.textContent?.charAt(0) === char\r\n const isInColumn = editor.isActive('column')\r\n\r\n const afterContent = $from.parent.textContent?.substring(\r\n $from.parent.textContent?.indexOf(char),\r\n )\r\n const isValidAfterContent = !afterContent?.endsWith(' ')\r\n\r\n // Check if drag handle is locked (ContentItemMenu is open)\r\n const isDragHandleLocked = (editor.storage as any).dragHandle?.lockDragHandle\r\n\r\n const shouldAllow =\r\n ((isRootDepth && isParagraph && isStartOfNode) ||\r\n (isInColumn && isParagraph && isStartOfNode)) &&\r\n isValidAfterContent &&\r\n !isDragHandleLocked\r\n\r\n return shouldAllow\r\n },\r\n command: ({ editor, props }: { editor: Editor; props: any }) => {\r\n const { view, state } = editor\r\n const { $head, $from } = view.state.selection\r\n\r\n const end = $from.pos\r\n const from = $head?.nodeBefore\r\n ? end -\r\n ($head.nodeBefore.text?.substring($head.nodeBefore.text?.indexOf(char)).length ?? 0)\r\n : $from.start()\r\n\r\n const tr = state.tr.deleteRange(from, end)\r\n view.dispatch(tr)\r\n\r\n props.onSelect({ editor })\r\n view.focus()\r\n },\r\n items: ({ query }: { query: string }) => {\r\n return items({ query, editor })\r\n },\r\n render: () => {\r\n let component: any\r\n let scrollHandler: (() => void) | null = null\r\n\r\n return {\r\n onStart: (props: SuggestionProps) => {\r\n setSuggestionProps(props)\r\n\r\n // Set slash command as active in storage\r\n ;(props.editor.storage as any).slashSuggestion = {\r\n ...(props.editor.storage as any).slashSuggestion,\r\n isActive: true,\r\n }\r\n\r\n // Temporarily disable drag handle to prevent interference\r\n props.editor.commands.setMeta('hideDragHandle', true)\r\n\r\n component = new ReactRenderer(children, {\r\n props,\r\n editor: props.editor,\r\n })\r\n\r\n const { view } = props.editor\r\n\r\n // Initialize the popup if needed\r\n initPopup()\r\n\r\n if (popupElement) {\r\n // Clear previous content\r\n popupElement.innerHTML = ''\r\n // Append the component\r\n popupElement.appendChild(component.element)\r\n\r\n // Set up positioning\r\n const getReferenceRect = () => {\r\n const { state } = view\r\n const { selection } = state\r\n const coords = view.coordsAtPos(selection.from)\r\n const node = view.nodeDOM(selection.from)\r\n\r\n if (node && node instanceof Element) {\r\n const rect = node.getBoundingClientRect()\r\n return new DOMRect(coords.left, coords.top, 0, 0)\r\n }\r\n\r\n return new DOMRect(coords.left, coords.top, 0, 0)\r\n }\r\n\r\n // Set up auto-update for positioning\r\n if (cleanupAutoUpdate) cleanupAutoUpdate()\r\n cleanupAutoUpdate = autoUpdate(\r\n { getBoundingClientRect: getReferenceRect },\r\n popupElement,\r\n async () => {\r\n await updatePopupPosition(getReferenceRect)\r\n },\r\n )\r\n\r\n scrollHandler = () => {\r\n updatePopupPosition(getReferenceRect)\r\n }\r\n\r\n view.dom.parentElement?.addEventListener('scroll', scrollHandler)\r\n\r\n // Show the popup\r\n showPopup()\r\n }\r\n },\r\n\r\n onUpdate(props: SuggestionProps) {\r\n setSuggestionProps(props)\r\n component.updateProps(props)\r\n\r\n // Update positioning for the popup\r\n const { view } = props.editor\r\n const { state } = view\r\n const { selection } = state\r\n const coords = view.coordsAtPos(selection.from)\r\n\r\n const getReferenceRect = () => {\r\n return new DOMRect(coords.left, coords.top, 0, 0)\r\n }\r\n\r\n updatePopupPosition(getReferenceRect)\r\n },\r\n\r\n onKeyDown(props: SuggestionKeyDownProps) {\r\n if (props.event.key === 'Escape') {\r\n hidePopup()\r\n // Return focus to the editor\r\n setTimeout(() => {\r\n editor.view.focus()\r\n }, 0)\r\n return true\r\n }\r\n\r\n // Only move focus to menu for arrow keys\r\n if (props.event.key === 'ArrowDown' || props.event.key === 'ArrowUp') {\r\n if (!isPopupVisible) {\r\n showPopup()\r\n }\r\n\r\n // Focus the menu element for navigation\r\n setTimeout(() => {\r\n const menuElement = component.element.querySelector('[tabindex=\"0\"]')\r\n if (menuElement) {\r\n menuElement.focus()\r\n }\r\n }, 0)\r\n\r\n return false\r\n }\r\n\r\n // Handle Enter key when menu is focused\r\n if (props.event.key === 'Enter') {\r\n if (!isPopupVisible) {\r\n showPopup()\r\n }\r\n\r\n // Focus the menu element and let it handle Enter\r\n setTimeout(() => {\r\n const menuElement = component.element.querySelector('[tabindex=\"0\"]')\r\n if (menuElement) {\r\n menuElement.focus()\r\n // Trigger Enter key on the menu\r\n const enterEvent = new KeyboardEvent('keydown', {\r\n key: 'Enter',\r\n code: 'Enter',\r\n keyCode: 13,\r\n which: 13,\r\n bubbles: true,\r\n cancelable: true,\r\n })\r\n menuElement.dispatchEvent(enterEvent)\r\n }\r\n }, 0)\r\n\r\n return false\r\n }\r\n\r\n // For any other key (letters, numbers, etc.), ensure editor has focus\r\n // This handles the case where user types after opening the menu\r\n if (!isPopupVisible) {\r\n showPopup()\r\n }\r\n\r\n // Ensure editor has focus for typing\r\n setTimeout(() => {\r\n editor.view.focus()\r\n }, 0)\r\n\r\n // Let the editor handle the key normally\r\n return false\r\n },\r\n\r\n onExit(props) {\r\n // Set slash command as inactive in storage\r\n ;(props.editor.storage as any).slashSuggestion = {\r\n ...(props.editor.storage as any).slashSuggestion,\r\n isActive: false,\r\n }\r\n\r\n // Re-enable drag handle\r\n props.editor.commands.setMeta('hideDragHandle', false)\r\n\r\n // Clean up scroll handler first\r\n if (scrollHandler) {\r\n const { view } = props.editor\r\n view.dom.parentElement?.removeEventListener('scroll', scrollHandler)\r\n scrollHandler = null\r\n }\r\n\r\n // Clean up auto-update listener\r\n if (cleanupAutoUpdate) {\r\n cleanupAutoUpdate()\r\n cleanupAutoUpdate = null\r\n }\r\n\r\n // Hide the popup\r\n hidePopup()\r\n\r\n // Clean up component\r\n component?.destroy?.()\r\n component = null\r\n setSuggestionProps(null)\r\n\r\n // Clear popup content\r\n if (popupElement) {\r\n popupElement.innerHTML = ''\r\n }\r\n\r\n // Return focus to the editor\r\n setTimeout(() => {\r\n props.editor.view.focus()\r\n }, 0)\r\n },\r\n }\r\n },\r\n })\r\n\r\n editor.registerPlugin(suggestion)\r\n suggestionRef.current = suggestion\r\n\r\n return () => {\r\n if (suggestionRef.current) {\r\n editor.unregisterPlugin(suggestionRef.current)\r\n suggestionRef.current = null\r\n }\r\n }\r\n }, [editor, char, items])\r\n\r\n return null\r\n}\r\n"],"names":["React","useEffect","useRef","useState","ReactRenderer","Suggestion","PluginKey","computePosition","offset","autoUpdate","popupElement","cleanupAutoUpdate","isPopupVisible","initPopup","document","createElement","style","position","zIndex","maxWidth","visibility","opacity","transition","className","body","appendChild","showPopup","display","hidePopup","setTimeout","updatePopupPosition","getReferenceRect","rect","virtualElement","getBoundingClientRect","x","y","placement","strategy","middleware","mainAxis","crossAxis","left","top","error","console","SuggestionMenu","editor","char","items","children","suggestionProps","setSuggestionProps","suggestionRef","current","unregisterPlugin","suggestion","allowSpaces","startOfLine","pluginKey","allow","state","range","$from","doc","resolve","from","isRootDepth","depth","isParagraph","parent","type","name","isStartOfNode","textContent","charAt","isInColumn","isActive","afterContent","substring","indexOf","isValidAfterContent","endsWith","isDragHandleLocked","storage","dragHandle","lockDragHandle","shouldAllow","command","props","view","$head","selection","end","pos","nodeBefore","text","length","start","tr","deleteRange","dispatch","onSelect","focus","query","render","component","scrollHandler","onStart","slashSuggestion","commands","setMeta","innerHTML","element","coords","coordsAtPos","node","nodeDOM","Element","DOMRect","dom","parentElement","addEventListener","onUpdate","updateProps","onKeyDown","event","key","menuElement","querySelector","enterEvent","KeyboardEvent","code","keyCode","which","bubbles","cancelable","dispatchEvent","onExit","removeEventListener","destroy","registerPlugin"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { SlashDropdownMenu } from './SlashDropdownMenu.js';
|
|
2
|
+
export { SlashCommandTriggerButton } from './SlashCommandTriggerButton.js';
|
|
3
|
+
export { SuggestionMenu } from './SuggestionMenu.js';
|
|
4
|
+
export { SlashMenuList } from './SlashMenuList.js';
|
|
5
|
+
export type { SlashMenuItemType, SuggestionItem, SlashMenuConfig, SlashDropdownMenuProps, SlashCommandTriggerButtonProps, } from './types.js';
|
|
6
|
+
export { getSlashMenuItems, filterSuggestionItems, getDefaultSlashMenuItems } from './utils.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/fields/TiptapEditor/features/ui/SlashCommand/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAA;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,8BAA8B,GAC/B,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { SlashDropdownMenu } from './SlashDropdownMenu.js';
|
|
2
|
+
export { SlashCommandTriggerButton } from './SlashCommandTriggerButton.js';
|
|
3
|
+
export { SuggestionMenu } from './SuggestionMenu.js';
|
|
4
|
+
export { SlashMenuList } from './SlashMenuList.js';
|
|
5
|
+
export { getSlashMenuItems, filterSuggestionItems, getDefaultSlashMenuItems } from './utils.js';
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/fields/TiptapEditor/features/ui/SlashCommand/index.ts"],"sourcesContent":["export { SlashDropdownMenu } from './SlashDropdownMenu.js'\r\nexport { SlashCommandTriggerButton } from './SlashCommandTriggerButton.js'\r\nexport { SuggestionMenu } from './SuggestionMenu.js'\r\nexport { SlashMenuList } from './SlashMenuList.js'\r\nexport type {\r\n SlashMenuItemType,\r\n SuggestionItem,\r\n SlashMenuConfig,\r\n SlashDropdownMenuProps,\r\n SlashCommandTriggerButtonProps,\r\n} from './types.js'\r\nexport { getSlashMenuItems, filterSuggestionItems, getDefaultSlashMenuItems } from './utils.js'\r\n"],"names":["SlashDropdownMenu","SlashCommandTriggerButton","SuggestionMenu","SlashMenuList","getSlashMenuItems","filterSuggestionItems","getDefaultSlashMenuItems"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,yBAAwB;AAC1D,SAASC,yBAAyB,QAAQ,iCAAgC;AAC1E,SAASC,cAAc,QAAQ,sBAAqB;AACpD,SAASC,aAAa,QAAQ,qBAAoB;AAQlD,SAASC,iBAAiB,EAAEC,qBAAqB,EAAEC,wBAAwB,QAAQ,aAAY"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/core';
|
|
2
|
+
import { LucideIcon } from 'lucide-react';
|
|
3
|
+
export type SlashMenuItemType = 'text' | 'heading_1' | 'heading_2' | 'heading_3' | 'bullet_list' | 'ordered_list' | 'quote' | 'code_block' | 'image' | 'horizontal_rule' | 'button' | 'custom_html' | 'social' | 'columns' | 'unsubscribe_footer' | 'twitter' | 'ai_writer';
|
|
4
|
+
export interface SuggestionItem {
|
|
5
|
+
title: string;
|
|
6
|
+
subtext: string;
|
|
7
|
+
aliases: string[];
|
|
8
|
+
badge: LucideIcon;
|
|
9
|
+
group: string;
|
|
10
|
+
onSelect: (props: {
|
|
11
|
+
editor: Editor;
|
|
12
|
+
}) => void;
|
|
13
|
+
}
|
|
14
|
+
export interface SlashMenuConfig {
|
|
15
|
+
enabledItems: SlashMenuItemType[];
|
|
16
|
+
customItems?: SuggestionItem[];
|
|
17
|
+
itemGroups?: Record<string, string>;
|
|
18
|
+
showGroups?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface SlashDropdownMenuProps {
|
|
21
|
+
editor: Editor | null;
|
|
22
|
+
config?: SlashMenuConfig;
|
|
23
|
+
}
|
|
24
|
+
export interface SlashCommandTriggerButtonProps {
|
|
25
|
+
editor: Editor | null;
|
|
26
|
+
config?: SlashMenuConfig;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../../src/fields/TiptapEditor/features/ui/SlashCommand/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAS,UAAU,EAAE,MAAM,cAAc,CAAA;AAEhD,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,WAAW,GACX,WAAW,GACX,WAAW,GACX,aAAa,GACb,cAAc,GACd,OAAO,GACP,YAAY,GACZ,OAAO,GACP,iBAAiB,GACjB,QAAQ,GACR,aAAa,GACb,QAAQ,GACR,SAAS,GACT,oBAAoB,GACpB,SAAS,GACT,WAAW,CAAA;AAEf,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,KAAK,EAAE,UAAU,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CAC9C;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,iBAAiB,EAAE,CAAA;IACjC,WAAW,CAAC,EAAE,cAAc,EAAE,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,CAAC,EAAE,eAAe,CAAA;CACzB;AAED,MAAM,WAAW,8BAA8B;IAC7C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,CAAC,EAAE,eAAe,CAAA;CACzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/fields/TiptapEditor/features/ui/SlashCommand/types.ts"],"sourcesContent":["import { Editor } from '@tiptap/core'\r\nimport { icons, LucideIcon } from 'lucide-react'\r\n\r\nexport type SlashMenuItemType =\r\n | 'text'\r\n | 'heading_1'\r\n | 'heading_2'\r\n | 'heading_3'\r\n | 'bullet_list'\r\n | 'ordered_list'\r\n | 'quote'\r\n | 'code_block'\r\n | 'image'\r\n | 'horizontal_rule'\r\n | 'button'\r\n | 'custom_html'\r\n | 'social'\r\n | 'columns'\r\n | 'unsubscribe_footer'\r\n | 'twitter'\r\n | 'ai_writer'\r\n\r\nexport interface SuggestionItem {\r\n title: string\r\n subtext: string\r\n aliases: string[]\r\n badge: LucideIcon\r\n group: string\r\n onSelect: (props: { editor: Editor }) => void\r\n}\r\n\r\nexport interface SlashMenuConfig {\r\n enabledItems: SlashMenuItemType[]\r\n customItems?: SuggestionItem[]\r\n itemGroups?: Record<string, string>\r\n showGroups?: boolean\r\n}\r\n\r\nexport interface SlashDropdownMenuProps {\r\n editor: Editor | null\r\n config?: SlashMenuConfig\r\n}\r\n\r\nexport interface SlashCommandTriggerButtonProps {\r\n editor: Editor | null\r\n config?: SlashMenuConfig\r\n}\r\n"],"names":[],"mappings":"AA2CA,WAGC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/core';
|
|
2
|
+
import { SlashMenuConfig, SuggestionItem } from './types.js';
|
|
3
|
+
export declare const getDefaultSlashMenuItems: (editor: Editor) => SuggestionItem[];
|
|
4
|
+
export declare const filterSuggestionItems: (items: SuggestionItem[], query: string) => SuggestionItem[];
|
|
5
|
+
export declare const getSlashMenuItems: (editor: Editor, config: SlashMenuConfig) => SuggestionItem[];
|
|
6
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../../../src/fields/TiptapEditor/features/ui/SlashCommand/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,eAAe,EAAqB,cAAc,EAAE,MAAM,YAAY,CAAA;AAmB/E,eAAO,MAAM,wBAAwB,WAAY,MAAM,KAAG,cAAc,EA0JvE,CAAA;AAED,eAAO,MAAM,qBAAqB,UAAW,cAAc,EAAE,SAAS,MAAM,KAAG,cAAc,EAY5F,CAAA;AAED,eAAO,MAAM,iBAAiB,WAAY,MAAM,UAAU,eAAe,KAAG,cAAc,EAmBzF,CAAA"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import { Facebook, Heading1, Heading2, Heading3, Image, Instagram, Link, List, Minus, Music, Quote, Twitter, Video, Youtube } from 'lucide-react';
|
|
3
|
+
export const getDefaultSlashMenuItems = (editor)=>{
|
|
4
|
+
return [
|
|
5
|
+
{
|
|
6
|
+
title: i18next.t('heading1') || 'Heading 1',
|
|
7
|
+
subtext: i18next.t('heading1Desc') || 'High priority section title',
|
|
8
|
+
aliases: [
|
|
9
|
+
'h1'
|
|
10
|
+
],
|
|
11
|
+
badge: Heading1,
|
|
12
|
+
group: 'format',
|
|
13
|
+
onSelect: ({ editor })=>{
|
|
14
|
+
editor.chain().focus().setHeading({
|
|
15
|
+
level: 1
|
|
16
|
+
}).run();
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
title: i18next.t('heading2') || 'Heading 2',
|
|
21
|
+
subtext: i18next.t('heading2Desc') || 'Medium priority section title',
|
|
22
|
+
aliases: [
|
|
23
|
+
'h2'
|
|
24
|
+
],
|
|
25
|
+
badge: Heading2,
|
|
26
|
+
group: 'format',
|
|
27
|
+
onSelect: ({ editor })=>{
|
|
28
|
+
editor.chain().focus().setHeading({
|
|
29
|
+
level: 2
|
|
30
|
+
}).run();
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
title: i18next.t('heading3') || 'Heading 3',
|
|
35
|
+
subtext: i18next.t('heading3Desc') || 'Low priority section title',
|
|
36
|
+
aliases: [
|
|
37
|
+
'h3'
|
|
38
|
+
],
|
|
39
|
+
badge: Heading3,
|
|
40
|
+
group: 'format',
|
|
41
|
+
onSelect: ({ editor })=>{
|
|
42
|
+
editor.chain().focus().setHeading({
|
|
43
|
+
level: 3
|
|
44
|
+
}).run();
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
title: i18next.t('bulletList') || 'Bullet List',
|
|
49
|
+
subtext: i18next.t('bulletListDesc') || 'Unordered list of items',
|
|
50
|
+
aliases: [
|
|
51
|
+
'ul'
|
|
52
|
+
],
|
|
53
|
+
badge: List,
|
|
54
|
+
group: 'format',
|
|
55
|
+
onSelect: ({ editor })=>{
|
|
56
|
+
editor.chain().focus().toggleBulletList().run();
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
// Uncomment if you want a numbered list option
|
|
60
|
+
// {
|
|
61
|
+
// title: i18next.t("numberedList") || 'Numbered List',
|
|
62
|
+
// subtext: i18next.t("numberedListDesc") || "Ordered list of items",
|
|
63
|
+
// aliases: ["ol"],
|
|
64
|
+
// badge: ListOrdered,
|
|
65
|
+
// group: "format",
|
|
66
|
+
// onSelect: ({ editor }) => {
|
|
67
|
+
// editor.chain().focus().toggleOrderedList().run();
|
|
68
|
+
// },
|
|
69
|
+
// },
|
|
70
|
+
{
|
|
71
|
+
title: i18next.t('blockquote') || 'Blockquote',
|
|
72
|
+
subtext: i18next.t('blockquoteDesc') || 'Element for quoting',
|
|
73
|
+
aliases: [],
|
|
74
|
+
badge: Quote,
|
|
75
|
+
group: 'format',
|
|
76
|
+
onSelect: ({ editor })=>{
|
|
77
|
+
editor.chain().focus().setBlockquote().run();
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
title: i18next.t('image') || 'Image',
|
|
82
|
+
subtext: i18next.t('imageDesc') || 'Insert an image',
|
|
83
|
+
aliases: [
|
|
84
|
+
'img'
|
|
85
|
+
],
|
|
86
|
+
badge: Image,
|
|
87
|
+
group: 'insert',
|
|
88
|
+
onSelect: ({ editor })=>{
|
|
89
|
+
editor.chain().focus().setImageUpload('image').run();
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
title: i18next.t('video') || 'Video',
|
|
94
|
+
subtext: i18next.t('videoDesc') || 'Insert a video',
|
|
95
|
+
aliases: [
|
|
96
|
+
'video'
|
|
97
|
+
],
|
|
98
|
+
badge: Video,
|
|
99
|
+
group: 'insert',
|
|
100
|
+
onSelect: ({ editor })=>{
|
|
101
|
+
editor.chain().focus().setImageUpload('video').run();
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
title: i18next.t('horizontalRule') || 'HorizontalRule',
|
|
106
|
+
subtext: i18next.t('horizontalRuleDesc') || 'Insert a horizontal divider',
|
|
107
|
+
aliases: [
|
|
108
|
+
'hr'
|
|
109
|
+
],
|
|
110
|
+
badge: Minus,
|
|
111
|
+
group: 'insert',
|
|
112
|
+
onSelect: ({ editor })=>{
|
|
113
|
+
editor.chain().focus().setHorizontalRule().run();
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
title: i18next.t('embed') || 'Embed Link',
|
|
118
|
+
subtext: i18next.t('iframeDesc') || 'Insert an Embed link',
|
|
119
|
+
aliases: [
|
|
120
|
+
'linkPreview'
|
|
121
|
+
],
|
|
122
|
+
badge: Link,
|
|
123
|
+
group: 'embed',
|
|
124
|
+
onSelect: ({ editor })=>{
|
|
125
|
+
editor.chain().focus().setLinkPreview().run();
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
title: i18next.t('twitter') || 'Twitter',
|
|
130
|
+
subtext: i18next.t('twitterDesc') || 'Insert a Twitter embed',
|
|
131
|
+
aliases: [
|
|
132
|
+
'x',
|
|
133
|
+
'twitter'
|
|
134
|
+
],
|
|
135
|
+
badge: Twitter,
|
|
136
|
+
group: 'embed',
|
|
137
|
+
onSelect: ({ editor })=>{
|
|
138
|
+
editor.chain().focus().insertTwitter().run();
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
title: i18next.t('facebook') || 'Facebook',
|
|
143
|
+
subtext: i18next.t('facebookDesc') || 'Insert a Facebook embed',
|
|
144
|
+
aliases: [
|
|
145
|
+
'meta',
|
|
146
|
+
'facebook'
|
|
147
|
+
],
|
|
148
|
+
badge: Facebook,
|
|
149
|
+
group: 'embed',
|
|
150
|
+
onSelect: ({ editor })=>{
|
|
151
|
+
editor.chain().focus().insertFacebook().run();
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
title: i18next.t('instagram') || 'Instagram',
|
|
156
|
+
subtext: i18next.t('instagramDesc') || 'Insert a Instagram embed',
|
|
157
|
+
aliases: [
|
|
158
|
+
'instagram'
|
|
159
|
+
],
|
|
160
|
+
badge: Instagram,
|
|
161
|
+
group: 'embed',
|
|
162
|
+
onSelect: ({ editor })=>{
|
|
163
|
+
editor.chain().focus().insertInstagram().run();
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
title: i18next.t('youtube') || 'Youtube',
|
|
168
|
+
subtext: i18next.t('youtubeDesc') || 'Insert a Youtube embed',
|
|
169
|
+
aliases: [
|
|
170
|
+
'youtube'
|
|
171
|
+
],
|
|
172
|
+
badge: Youtube,
|
|
173
|
+
group: 'embed',
|
|
174
|
+
onSelect: ({ editor })=>{
|
|
175
|
+
editor.chain().focus().insertYoutube().run();
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
title: i18next.t('tiktok') || 'Tiktok',
|
|
180
|
+
subtext: i18next.t('tiktokDesc') || 'Insert a Tiktok embed',
|
|
181
|
+
aliases: [
|
|
182
|
+
'tiktok'
|
|
183
|
+
],
|
|
184
|
+
badge: Music,
|
|
185
|
+
group: 'embed',
|
|
186
|
+
onSelect: ({ editor })=>{
|
|
187
|
+
editor.chain().focus().insertTiktok().run();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
];
|
|
191
|
+
};
|
|
192
|
+
export const filterSuggestionItems = (items, query)=>{
|
|
193
|
+
if (!query) return items;
|
|
194
|
+
const normalizedQuery = query.toLowerCase().trim();
|
|
195
|
+
return items.filter((item)=>{
|
|
196
|
+
const titleMatch = item.title.toLowerCase().includes(normalizedQuery);
|
|
197
|
+
const subtextMatch = item.subtext.toLowerCase().includes(normalizedQuery);
|
|
198
|
+
const aliasesMatch = item.aliases.some((alias)=>alias.toLowerCase().includes(normalizedQuery));
|
|
199
|
+
return titleMatch || subtextMatch || aliasesMatch;
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
export const getSlashMenuItems = (editor, config)=>{
|
|
203
|
+
const defaultItems = getDefaultSlashMenuItems(editor);
|
|
204
|
+
// Filter by enabled items
|
|
205
|
+
const enabledItems = defaultItems.filter((item)=>{
|
|
206
|
+
const itemType = item.title.toLowerCase().replace(/\s+/g, '_');
|
|
207
|
+
return config.enabledItems.includes(itemType) || config.enabledItems.includes('text');
|
|
208
|
+
});
|
|
209
|
+
// Add custom items
|
|
210
|
+
const customItems = config.customItems || [];
|
|
211
|
+
// Apply group assignments
|
|
212
|
+
const itemsWithGroups = [
|
|
213
|
+
...enabledItems,
|
|
214
|
+
...customItems
|
|
215
|
+
].map((item)=>({
|
|
216
|
+
...item,
|
|
217
|
+
group: config.itemGroups?.[item.title.toLowerCase().replace(/\s+/g, '_')] || item.group
|
|
218
|
+
}));
|
|
219
|
+
return itemsWithGroups;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
//# sourceMappingURL=utils.js.map
|