@underverse-ui/underverse 1.0.202 → 1.0.204
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/README.md +8 -2
- package/api-reference.json +1 -1
- package/dist/EmojiPicker-UN6N7YVB.js +14 -0
- package/dist/EmojiPicker-UN6N7YVB.js.map +1 -0
- package/dist/chunk-4TYW5K4J.js +769 -0
- package/dist/chunk-4TYW5K4J.js.map +1 -0
- package/dist/chunk-CC3QO2YM.js +14506 -0
- package/dist/chunk-CC3QO2YM.js.map +1 -0
- package/dist/chunk-CKKLFVOY.js +206 -0
- package/dist/chunk-CKKLFVOY.js.map +1 -0
- package/dist/chunk-GSBRTFP2.js +2464 -0
- package/dist/chunk-GSBRTFP2.js.map +1 -0
- package/dist/chunk-KFTYWTJR.js +4481 -0
- package/dist/chunk-KFTYWTJR.js.map +1 -0
- package/dist/chunk-NSBPE2FW.js +17 -0
- package/dist/chunk-NSBPE2FW.js.map +1 -0
- package/dist/chunk-QVQTWZ24.js +199 -0
- package/dist/chunk-QVQTWZ24.js.map +1 -0
- package/dist/chunk-ZUAIBY2Z.js +74 -0
- package/dist/chunk-ZUAIBY2Z.js.map +1 -0
- package/dist/emojis-I5AMZ3EE.js +8 -0
- package/dist/emojis-I5AMZ3EE.js.map +1 -0
- package/dist/index.cjs +36680 -35147
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -134
- package/dist/index.d.ts +19 -134
- package/dist/index.js +2801 -25223
- package/dist/index.js.map +1 -1
- package/dist/lowlight-runtime-Y43Y7YTW.js +11 -0
- package/dist/lowlight-runtime-Y43Y7YTW.js.map +1 -0
- package/dist/menu-bar-6U5S4PS7.js +947 -0
- package/dist/menu-bar-6U5S4PS7.js.map +1 -0
- package/dist/ueditor.cjs +23503 -0
- package/dist/ueditor.cjs.map +1 -0
- package/dist/ueditor.d.cts +136 -0
- package/dist/ueditor.d.ts +136 -0
- package/dist/ueditor.js +20 -0
- package/dist/ueditor.js.map +1 -0
- package/package.json +54 -46
|
@@ -0,0 +1,4481 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Tooltip,
|
|
3
|
+
chainEventHandlers,
|
|
4
|
+
cn,
|
|
5
|
+
mergeRefs,
|
|
6
|
+
setRefValue,
|
|
7
|
+
useSmartTranslations
|
|
8
|
+
} from "./chunk-GSBRTFP2.js";
|
|
9
|
+
|
|
10
|
+
// src/contexts/UnderverseConfigContext.tsx
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
13
|
+
var UnderverseUIConfigContext = React.createContext(null);
|
|
14
|
+
function useUnderverseUIConfig() {
|
|
15
|
+
return React.useContext(UnderverseUIConfigContext) ?? {};
|
|
16
|
+
}
|
|
17
|
+
function UnderverseConfigProvider({ children, config }) {
|
|
18
|
+
if (!config) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
19
|
+
return /* @__PURE__ */ jsx(UnderverseUIConfigContext.Provider, { value: config, children });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/utils/animations.ts
|
|
23
|
+
import { useEffect } from "react";
|
|
24
|
+
var shadcnAnimationStyles = `
|
|
25
|
+
/* ============================================
|
|
26
|
+
* DROPDOWN / POPOVER ANIMATIONS
|
|
27
|
+
* Uses spring-like cubic-bezier for natural feel
|
|
28
|
+
* ============================================ */
|
|
29
|
+
|
|
30
|
+
/* Native-like Combobox Animation - Mimics browser default select */
|
|
31
|
+
[data-state="open"][data-combobox-dropdown] {
|
|
32
|
+
animation: comboboxOpen 150ms cubic-bezier(0.2, 0, 0, 1);
|
|
33
|
+
transform-origin: top center;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[data-state="closed"][data-combobox-dropdown] {
|
|
37
|
+
animation: comboboxClose 120ms cubic-bezier(0.4, 0, 1, 1);
|
|
38
|
+
transform-origin: top center;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@keyframes comboboxOpen {
|
|
42
|
+
0% {
|
|
43
|
+
opacity: 0;
|
|
44
|
+
transform: translateY(-4px) scaleY(0.9);
|
|
45
|
+
}
|
|
46
|
+
100% {
|
|
47
|
+
opacity: 1;
|
|
48
|
+
transform: translateY(0) scaleY(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@keyframes comboboxClose {
|
|
53
|
+
0% {
|
|
54
|
+
opacity: 1;
|
|
55
|
+
transform: translateY(0) scaleY(1);
|
|
56
|
+
}
|
|
57
|
+
100% {
|
|
58
|
+
opacity: 0;
|
|
59
|
+
transform: translateY(-4px) scaleY(0.9);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Generic dropdown open/close */
|
|
64
|
+
[data-state="open"] {
|
|
65
|
+
animation: slideDownAndFade 220ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[data-state="closed"] {
|
|
69
|
+
animation: slideUpAndFade 180ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@keyframes slideDownAndFade {
|
|
73
|
+
from {
|
|
74
|
+
opacity: 0;
|
|
75
|
+
transform: translateY(-4px) scale(0.98);
|
|
76
|
+
}
|
|
77
|
+
to {
|
|
78
|
+
opacity: 1;
|
|
79
|
+
transform: translateY(0) scale(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@keyframes slideUpAndFade {
|
|
84
|
+
from {
|
|
85
|
+
opacity: 1;
|
|
86
|
+
transform: translateY(0) scale(1);
|
|
87
|
+
}
|
|
88
|
+
to {
|
|
89
|
+
opacity: 0;
|
|
90
|
+
transform: translateY(-4px) scale(0.98);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ============================================
|
|
95
|
+
* DROPDOWN ITEMS - Native-like instant appearance
|
|
96
|
+
* ============================================ */
|
|
97
|
+
|
|
98
|
+
/* Fast staggered animation for native feel */
|
|
99
|
+
.dropdown-item {
|
|
100
|
+
opacity: 0;
|
|
101
|
+
animation: itemFadeIn 120ms cubic-bezier(0.2, 0, 0, 1) forwards;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@keyframes itemFadeIn {
|
|
105
|
+
from {
|
|
106
|
+
opacity: 0;
|
|
107
|
+
transform: translateX(-4px);
|
|
108
|
+
}
|
|
109
|
+
to {
|
|
110
|
+
opacity: 1;
|
|
111
|
+
transform: translateX(0);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Item stagger delays - minimal for speed */
|
|
116
|
+
.dropdown-item:nth-child(1) { animation-delay: 0ms; }
|
|
117
|
+
.dropdown-item:nth-child(2) { animation-delay: 15ms; }
|
|
118
|
+
.dropdown-item:nth-child(3) { animation-delay: 30ms; }
|
|
119
|
+
.dropdown-item:nth-child(4) { animation-delay: 45ms; }
|
|
120
|
+
.dropdown-item:nth-child(5) { animation-delay: 60ms; }
|
|
121
|
+
.dropdown-item:nth-child(6) { animation-delay: 75ms; }
|
|
122
|
+
.dropdown-item:nth-child(7) { animation-delay: 90ms; }
|
|
123
|
+
.dropdown-item:nth-child(8) { animation-delay: 105ms; }
|
|
124
|
+
.dropdown-item:nth-child(n+9) { animation-delay: 120ms; }
|
|
125
|
+
|
|
126
|
+
/* ============================================
|
|
127
|
+
* DATEPICKER ANIMATIONS
|
|
128
|
+
* ============================================ */
|
|
129
|
+
|
|
130
|
+
.datepicker-day {
|
|
131
|
+
opacity: 0;
|
|
132
|
+
animation: dayFadeIn 200ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@keyframes dayFadeIn {
|
|
136
|
+
from {
|
|
137
|
+
opacity: 0;
|
|
138
|
+
transform: scale(0.8);
|
|
139
|
+
}
|
|
140
|
+
to {
|
|
141
|
+
opacity: 1;
|
|
142
|
+
transform: scale(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* ============================================
|
|
147
|
+
* TOOLTIP ANIMATIONS
|
|
148
|
+
* ============================================ */
|
|
149
|
+
|
|
150
|
+
[data-tooltip] {
|
|
151
|
+
animation: tooltipIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@keyframes tooltipIn {
|
|
155
|
+
from {
|
|
156
|
+
opacity: 0;
|
|
157
|
+
transform: scale(0.95);
|
|
158
|
+
}
|
|
159
|
+
to {
|
|
160
|
+
opacity: 1;
|
|
161
|
+
transform: scale(1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* ============================================
|
|
166
|
+
* MODAL / DIALOG ANIMATIONS
|
|
167
|
+
* ============================================ */
|
|
168
|
+
|
|
169
|
+
.modal-content {
|
|
170
|
+
animation: scaleIn 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@keyframes scaleIn {
|
|
174
|
+
from {
|
|
175
|
+
opacity: 0;
|
|
176
|
+
transform: scale(0.9);
|
|
177
|
+
}
|
|
178
|
+
to {
|
|
179
|
+
opacity: 1;
|
|
180
|
+
transform: scale(1);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Smooth backdrop blur transition */
|
|
185
|
+
.backdrop-animate {
|
|
186
|
+
transition: backdrop-filter 200ms ease, background-color 200ms ease;
|
|
187
|
+
}
|
|
188
|
+
`;
|
|
189
|
+
function ensureAnimationStylesInjected() {
|
|
190
|
+
if (typeof document !== "undefined") {
|
|
191
|
+
const styleId = "shadcn-animations";
|
|
192
|
+
if (!document.getElementById(styleId)) {
|
|
193
|
+
const styleElement = document.createElement("style");
|
|
194
|
+
styleElement.id = styleId;
|
|
195
|
+
styleElement.textContent = shadcnAnimationStyles;
|
|
196
|
+
document.head.appendChild(styleElement);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function useShadCNAnimations() {
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
ensureAnimationStylesInjected();
|
|
203
|
+
}, []);
|
|
204
|
+
}
|
|
205
|
+
function injectAnimationStyles() {
|
|
206
|
+
ensureAnimationStylesInjected();
|
|
207
|
+
}
|
|
208
|
+
function getAnimationStyles() {
|
|
209
|
+
return shadcnAnimationStyles;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/components/Popover.tsx
|
|
213
|
+
import * as React2 from "react";
|
|
214
|
+
import { createPortal } from "react-dom";
|
|
215
|
+
|
|
216
|
+
// src/utils/radius.ts
|
|
217
|
+
var STANDARD_BORDER_MODES = ["none", "sm", "md", "lg", "xl", "2xl", "3xl", "full"];
|
|
218
|
+
var CUSTOM_BORDER_MAP = {
|
|
219
|
+
daewoo: "rounded",
|
|
220
|
+
infiniq: "rounded-full"
|
|
221
|
+
};
|
|
222
|
+
var BORDER_MODE_DOCS_TYPE = `"${STANDARD_BORDER_MODES.join('" | "')}" | "${Object.keys(CUSTOM_BORDER_MAP).join('" | "')}"`;
|
|
223
|
+
function getBorderRadiusClass(borderMode = "full") {
|
|
224
|
+
if (!borderMode) return "rounded-full";
|
|
225
|
+
if (borderMode in CUSTOM_BORDER_MAP) {
|
|
226
|
+
return CUSTOM_BORDER_MAP[borderMode];
|
|
227
|
+
}
|
|
228
|
+
if (STANDARD_BORDER_MODES.includes(borderMode)) {
|
|
229
|
+
return `rounded-${borderMode}`;
|
|
230
|
+
}
|
|
231
|
+
return borderMode;
|
|
232
|
+
}
|
|
233
|
+
function getPanelBorderRadiusClass(borderMode = "lg") {
|
|
234
|
+
const resolved = borderMode ?? "lg";
|
|
235
|
+
if (resolved === "full" || resolved === "infiniq") {
|
|
236
|
+
return "rounded-2xl";
|
|
237
|
+
}
|
|
238
|
+
return getBorderRadiusClass(resolved);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/components/Popover.tsx
|
|
242
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
243
|
+
function getTransformOrigin(side, align) {
|
|
244
|
+
if (side === "top") return `${align === "end" ? "right" : "left"} bottom`;
|
|
245
|
+
if (side === "bottom") return `${align === "end" ? "right" : "left"} top`;
|
|
246
|
+
if (side === "left") return "right top";
|
|
247
|
+
return "left top";
|
|
248
|
+
}
|
|
249
|
+
function normalizePlacement(placement) {
|
|
250
|
+
switch (placement) {
|
|
251
|
+
case "top":
|
|
252
|
+
return { side: "top", align: "start" };
|
|
253
|
+
case "bottom":
|
|
254
|
+
return { side: "bottom", align: "start" };
|
|
255
|
+
case "left":
|
|
256
|
+
return { side: "left", align: "start" };
|
|
257
|
+
case "right":
|
|
258
|
+
return { side: "right", align: "start" };
|
|
259
|
+
default: {
|
|
260
|
+
const [side, align] = placement.split("-");
|
|
261
|
+
return { side, align };
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
266
|
+
function overflowAmount(left, width, viewportWidth, padding) {
|
|
267
|
+
const min = padding;
|
|
268
|
+
const max = viewportWidth - padding;
|
|
269
|
+
const overflowLeft = Math.max(0, min - left);
|
|
270
|
+
const overflowRight = Math.max(0, left + width - max);
|
|
271
|
+
return overflowLeft + overflowRight;
|
|
272
|
+
}
|
|
273
|
+
function computePopoverPosition(args) {
|
|
274
|
+
const { triggerRect, contentSize, placement, offset, padding, viewport } = args;
|
|
275
|
+
let { side, align } = normalizePlacement(placement);
|
|
276
|
+
if (side === "bottom") {
|
|
277
|
+
const bottomTop = triggerRect.bottom + offset;
|
|
278
|
+
const overflowsBottom = bottomTop + contentSize.height > viewport.height - padding;
|
|
279
|
+
const topTop = triggerRect.top - offset - contentSize.height;
|
|
280
|
+
const fitsTop = topTop >= padding;
|
|
281
|
+
if (overflowsBottom && fitsTop) side = "top";
|
|
282
|
+
} else if (side === "top") {
|
|
283
|
+
const topTop = triggerRect.top - offset - contentSize.height;
|
|
284
|
+
const overflowsTop = topTop < padding;
|
|
285
|
+
const bottomTop = triggerRect.bottom + offset;
|
|
286
|
+
const fitsBottom = bottomTop + contentSize.height <= viewport.height - padding;
|
|
287
|
+
if (overflowsTop && fitsBottom) side = "bottom";
|
|
288
|
+
} else if (side === "right") {
|
|
289
|
+
const rightLeft = triggerRect.right + offset;
|
|
290
|
+
const overflowsRight = rightLeft + contentSize.width > viewport.width - padding;
|
|
291
|
+
const leftLeft = triggerRect.left - offset - contentSize.width;
|
|
292
|
+
const fitsLeft = leftLeft >= padding;
|
|
293
|
+
if (overflowsRight && fitsLeft) side = "left";
|
|
294
|
+
} else if (side === "left") {
|
|
295
|
+
const leftLeft = triggerRect.left - offset - contentSize.width;
|
|
296
|
+
const overflowsLeft = leftLeft < padding;
|
|
297
|
+
const rightLeft = triggerRect.right + offset;
|
|
298
|
+
const fitsRight = rightLeft + contentSize.width <= viewport.width - padding;
|
|
299
|
+
if (overflowsLeft && fitsRight) side = "right";
|
|
300
|
+
}
|
|
301
|
+
let top = 0;
|
|
302
|
+
let left = 0;
|
|
303
|
+
if (side === "top" || side === "bottom") {
|
|
304
|
+
const leftStart = triggerRect.left;
|
|
305
|
+
const leftEnd = triggerRect.right - contentSize.width;
|
|
306
|
+
const startOverflow = overflowAmount(leftStart, contentSize.width, viewport.width, padding);
|
|
307
|
+
const endOverflow = overflowAmount(leftEnd, contentSize.width, viewport.width, padding);
|
|
308
|
+
if (align === "start" && startOverflow > 0 && endOverflow < startOverflow) align = "end";
|
|
309
|
+
if (align === "end" && endOverflow > 0 && startOverflow < endOverflow) align = "start";
|
|
310
|
+
left = align === "end" ? leftEnd : leftStart;
|
|
311
|
+
top = side === "top" ? triggerRect.top - offset - contentSize.height : triggerRect.bottom + offset;
|
|
312
|
+
left = clamp(left, padding, viewport.width - contentSize.width - padding);
|
|
313
|
+
top = clamp(top, padding, viewport.height - contentSize.height - padding);
|
|
314
|
+
return { top, left, side, align };
|
|
315
|
+
}
|
|
316
|
+
top = triggerRect.top;
|
|
317
|
+
left = side === "left" ? triggerRect.left - offset - contentSize.width : triggerRect.right + offset;
|
|
318
|
+
left = clamp(left, padding, viewport.width - contentSize.width - padding);
|
|
319
|
+
top = clamp(top, padding, viewport.height - contentSize.height - padding);
|
|
320
|
+
return { top, left, side, align };
|
|
321
|
+
}
|
|
322
|
+
var Popover = ({
|
|
323
|
+
trigger,
|
|
324
|
+
children,
|
|
325
|
+
className,
|
|
326
|
+
contentClassName,
|
|
327
|
+
contentProps,
|
|
328
|
+
contentScrollable = false,
|
|
329
|
+
placement = "bottom",
|
|
330
|
+
modal = false,
|
|
331
|
+
disabled = false,
|
|
332
|
+
open,
|
|
333
|
+
onOpenChange,
|
|
334
|
+
matchTriggerWidth = false,
|
|
335
|
+
contentWidth,
|
|
336
|
+
borderMode
|
|
337
|
+
}) => {
|
|
338
|
+
const isControlled = open !== void 0;
|
|
339
|
+
const [internalOpen, setInternalOpen] = React2.useState(false);
|
|
340
|
+
const triggerRef = React2.useRef(null);
|
|
341
|
+
const positionerRef = React2.useRef(null);
|
|
342
|
+
const panelRef = React2.useRef(null);
|
|
343
|
+
const lastAppliedRef = React2.useRef(null);
|
|
344
|
+
useShadCNAnimations();
|
|
345
|
+
const globalConfig = useUnderverseUIConfig();
|
|
346
|
+
const resolvedBorderMode = borderMode ?? globalConfig.popover?.borderMode ?? globalConfig.borderMode;
|
|
347
|
+
const isOpen = isControlled ? open : internalOpen;
|
|
348
|
+
const setIsOpen = React2.useCallback(
|
|
349
|
+
(next) => {
|
|
350
|
+
if (!isControlled) setInternalOpen(next);
|
|
351
|
+
onOpenChange?.(next);
|
|
352
|
+
},
|
|
353
|
+
[isControlled, onOpenChange]
|
|
354
|
+
);
|
|
355
|
+
const offset = 4;
|
|
356
|
+
const padding = 8;
|
|
357
|
+
const triggerSelector = React2.useId();
|
|
358
|
+
const initialPlacement = React2.useMemo(() => normalizePlacement(placement), [placement]);
|
|
359
|
+
React2.useLayoutEffect(() => {
|
|
360
|
+
if (typeof document === "undefined") return;
|
|
361
|
+
const triggerEl = document.querySelector(`[data-underverse-popover-trigger="${triggerSelector}"]`);
|
|
362
|
+
if (triggerEl) {
|
|
363
|
+
triggerRef.current = triggerEl;
|
|
364
|
+
}
|
|
365
|
+
}, [triggerSelector, trigger]);
|
|
366
|
+
const updatePosition = React2.useCallback(() => {
|
|
367
|
+
const triggerEl = triggerRef.current;
|
|
368
|
+
const positionerEl = positionerRef.current;
|
|
369
|
+
const panelEl = panelRef.current;
|
|
370
|
+
if (!triggerEl || !positionerEl || !panelEl) return;
|
|
371
|
+
const triggerRect = triggerEl.getBoundingClientRect();
|
|
372
|
+
const widthWanted = matchTriggerWidth ? triggerRect.width : contentWidth;
|
|
373
|
+
const widthPx = widthWanted == null ? void 0 : Math.max(0, Math.round(widthWanted));
|
|
374
|
+
if (widthPx == null) {
|
|
375
|
+
if (positionerEl.style.width) positionerEl.style.width = "";
|
|
376
|
+
} else if (positionerEl.style.width !== `${widthPx}px`) {
|
|
377
|
+
positionerEl.style.width = `${widthPx}px`;
|
|
378
|
+
}
|
|
379
|
+
const prevApplied = lastAppliedRef.current;
|
|
380
|
+
const measuredWidth = positionerEl.offsetWidth;
|
|
381
|
+
const measuredHeight = positionerEl.offsetHeight;
|
|
382
|
+
const contentRect = positionerEl.getBoundingClientRect();
|
|
383
|
+
const contentBoxWidth = measuredWidth || contentRect.width;
|
|
384
|
+
const contentBoxHeight = measuredHeight || contentRect.height;
|
|
385
|
+
const next = computePopoverPosition({
|
|
386
|
+
triggerRect,
|
|
387
|
+
contentSize: { width: contentBoxWidth, height: contentBoxHeight },
|
|
388
|
+
placement,
|
|
389
|
+
offset,
|
|
390
|
+
padding,
|
|
391
|
+
viewport: { width: window.innerWidth, height: window.innerHeight }
|
|
392
|
+
});
|
|
393
|
+
const top = Math.round(next.top);
|
|
394
|
+
const left = Math.round(next.left);
|
|
395
|
+
const applied = prevApplied && Math.abs(prevApplied.top - top) < 0.5 && Math.abs(prevApplied.left - left) < 0.5 && prevApplied.side === next.side && prevApplied.align === next.align && prevApplied.width === widthPx;
|
|
396
|
+
if (applied) return;
|
|
397
|
+
lastAppliedRef.current = { top, left, side: next.side, align: next.align, width: widthPx };
|
|
398
|
+
positionerEl.style.transform = `translate3d(${left}px, ${top}px, 0)`;
|
|
399
|
+
panelEl.style.transformOrigin = getTransformOrigin(next.side, next.align);
|
|
400
|
+
if (positionerEl.style.visibility !== "visible") positionerEl.style.visibility = "visible";
|
|
401
|
+
if (positionerEl.style.pointerEvents !== "auto") positionerEl.style.pointerEvents = "auto";
|
|
402
|
+
}, [placement, matchTriggerWidth, contentWidth]);
|
|
403
|
+
React2.useLayoutEffect(() => {
|
|
404
|
+
if (!isOpen) return;
|
|
405
|
+
updatePosition();
|
|
406
|
+
let raf1 = 0;
|
|
407
|
+
let raf2 = 0;
|
|
408
|
+
raf1 = window.requestAnimationFrame(() => {
|
|
409
|
+
updatePosition();
|
|
410
|
+
raf2 = window.requestAnimationFrame(() => updatePosition());
|
|
411
|
+
});
|
|
412
|
+
return () => {
|
|
413
|
+
cancelAnimationFrame(raf1);
|
|
414
|
+
cancelAnimationFrame(raf2);
|
|
415
|
+
};
|
|
416
|
+
}, [isOpen, updatePosition]);
|
|
417
|
+
React2.useEffect(() => {
|
|
418
|
+
if (!isOpen) return;
|
|
419
|
+
let raf = 0;
|
|
420
|
+
const tick = () => {
|
|
421
|
+
updatePosition();
|
|
422
|
+
raf = window.requestAnimationFrame(tick);
|
|
423
|
+
};
|
|
424
|
+
raf = window.requestAnimationFrame(tick);
|
|
425
|
+
return () => window.cancelAnimationFrame(raf);
|
|
426
|
+
}, [isOpen, updatePosition]);
|
|
427
|
+
React2.useEffect(() => {
|
|
428
|
+
if (!isOpen) return;
|
|
429
|
+
let raf = 0;
|
|
430
|
+
const handler = () => {
|
|
431
|
+
cancelAnimationFrame(raf);
|
|
432
|
+
raf = window.requestAnimationFrame(() => updatePosition());
|
|
433
|
+
};
|
|
434
|
+
handler();
|
|
435
|
+
window.addEventListener("resize", handler);
|
|
436
|
+
window.addEventListener("scroll", handler, true);
|
|
437
|
+
document.addEventListener("scroll", handler, true);
|
|
438
|
+
return () => {
|
|
439
|
+
cancelAnimationFrame(raf);
|
|
440
|
+
window.removeEventListener("resize", handler);
|
|
441
|
+
window.removeEventListener("scroll", handler, true);
|
|
442
|
+
document.removeEventListener("scroll", handler, true);
|
|
443
|
+
};
|
|
444
|
+
}, [isOpen, updatePosition]);
|
|
445
|
+
React2.useEffect(() => {
|
|
446
|
+
if (!isOpen) return;
|
|
447
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
448
|
+
const ro = new ResizeObserver(() => updatePosition());
|
|
449
|
+
if (positionerRef.current) ro.observe(positionerRef.current);
|
|
450
|
+
if (triggerRef.current) ro.observe(triggerRef.current);
|
|
451
|
+
return () => ro.disconnect();
|
|
452
|
+
}, [isOpen, updatePosition]);
|
|
453
|
+
React2.useLayoutEffect(() => {
|
|
454
|
+
if (!isOpen) {
|
|
455
|
+
lastAppliedRef.current = null;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
}, [isOpen]);
|
|
459
|
+
React2.useEffect(() => {
|
|
460
|
+
if (!isOpen) return;
|
|
461
|
+
const handleClickOutside = (event) => {
|
|
462
|
+
const target = event.target;
|
|
463
|
+
const triggerEl = triggerRef.current;
|
|
464
|
+
const popoverEl = positionerRef.current;
|
|
465
|
+
if (!triggerEl || !popoverEl) return;
|
|
466
|
+
if (triggerEl.contains(target)) return;
|
|
467
|
+
if (popoverEl.contains(target)) return;
|
|
468
|
+
if (target instanceof Element && target.closest("[data-popover]")) return;
|
|
469
|
+
setIsOpen(false);
|
|
470
|
+
};
|
|
471
|
+
const handleEscape = (event) => {
|
|
472
|
+
if (event.key === "Escape") {
|
|
473
|
+
setIsOpen(false);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
if (typeof document !== "undefined") {
|
|
477
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
478
|
+
document.addEventListener("keydown", handleEscape);
|
|
479
|
+
return () => {
|
|
480
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
481
|
+
document.removeEventListener("keydown", handleEscape);
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
}, [isOpen, setIsOpen]);
|
|
485
|
+
const handleTriggerClick = () => {
|
|
486
|
+
if (!disabled) {
|
|
487
|
+
setIsOpen(!isOpen);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
const popoverContent = isOpen && typeof window !== "undefined" ? createPortal(
|
|
491
|
+
/* @__PURE__ */ jsx2(
|
|
492
|
+
"div",
|
|
493
|
+
{
|
|
494
|
+
ref: positionerRef,
|
|
495
|
+
"data-popover": true,
|
|
496
|
+
style: {
|
|
497
|
+
position: "fixed",
|
|
498
|
+
top: 0,
|
|
499
|
+
left: 0,
|
|
500
|
+
transform: "translate3d(0, 0, 0)",
|
|
501
|
+
zIndex: 99999,
|
|
502
|
+
visibility: "hidden",
|
|
503
|
+
pointerEvents: "none",
|
|
504
|
+
willChange: "transform"
|
|
505
|
+
},
|
|
506
|
+
className: "z-[99999]",
|
|
507
|
+
children: /* @__PURE__ */ jsx2(
|
|
508
|
+
"div",
|
|
509
|
+
{
|
|
510
|
+
ref: panelRef,
|
|
511
|
+
"data-state": "open",
|
|
512
|
+
role: modal ? "dialog" : void 0,
|
|
513
|
+
"aria-modal": modal || void 0,
|
|
514
|
+
style: {
|
|
515
|
+
transformOrigin: getTransformOrigin(initialPlacement.side, initialPlacement.align)
|
|
516
|
+
},
|
|
517
|
+
className: cn(
|
|
518
|
+
// shadcn-like enter animation
|
|
519
|
+
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
520
|
+
className
|
|
521
|
+
),
|
|
522
|
+
children: /* @__PURE__ */ jsx2(
|
|
523
|
+
"div",
|
|
524
|
+
{
|
|
525
|
+
...contentProps,
|
|
526
|
+
className: cn(
|
|
527
|
+
resolvedBorderMode ? getPanelBorderRadiusClass(resolvedBorderMode) : "rounded-2xl md:rounded-3xl",
|
|
528
|
+
"border bg-popover text-popover-foreground shadow-md",
|
|
529
|
+
"backdrop-blur-sm bg-popover/95 border-border/60 p-4",
|
|
530
|
+
contentProps?.className,
|
|
531
|
+
contentClassName
|
|
532
|
+
),
|
|
533
|
+
tabIndex: contentProps?.tabIndex ?? -1,
|
|
534
|
+
children
|
|
535
|
+
}
|
|
536
|
+
)
|
|
537
|
+
}
|
|
538
|
+
)
|
|
539
|
+
}
|
|
540
|
+
),
|
|
541
|
+
document.body
|
|
542
|
+
) : null;
|
|
543
|
+
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
544
|
+
(() => {
|
|
545
|
+
const triggerProps = trigger.props;
|
|
546
|
+
const childRef = triggerProps.ref;
|
|
547
|
+
return React2.cloneElement(trigger, {
|
|
548
|
+
...triggerProps,
|
|
549
|
+
ref: mergeRefs(childRef, (node) => {
|
|
550
|
+
triggerRef.current = node;
|
|
551
|
+
}),
|
|
552
|
+
"data-underverse-popover-trigger": triggerSelector,
|
|
553
|
+
onClick: chainEventHandlers(
|
|
554
|
+
(e) => {
|
|
555
|
+
triggerRef.current = e.currentTarget;
|
|
556
|
+
e.preventDefault();
|
|
557
|
+
e.stopPropagation();
|
|
558
|
+
handleTriggerClick();
|
|
559
|
+
},
|
|
560
|
+
triggerProps.onClick
|
|
561
|
+
),
|
|
562
|
+
onFocus: chainEventHandlers(
|
|
563
|
+
(e) => {
|
|
564
|
+
triggerRef.current = e.currentTarget;
|
|
565
|
+
},
|
|
566
|
+
triggerProps.onFocus
|
|
567
|
+
),
|
|
568
|
+
"aria-expanded": isOpen,
|
|
569
|
+
"aria-haspopup": triggerProps["aria-haspopup"] ?? "dialog"
|
|
570
|
+
});
|
|
571
|
+
})(),
|
|
572
|
+
popoverContent
|
|
573
|
+
] });
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// src/components/DropdownMenu.tsx
|
|
577
|
+
import React3, { useState as useState2 } from "react";
|
|
578
|
+
import { ChevronRight } from "lucide-react";
|
|
579
|
+
|
|
580
|
+
// src/constants/form-control-size.ts
|
|
581
|
+
var formControlSizeStyles = {
|
|
582
|
+
sm: {
|
|
583
|
+
control: "h-8 px-3 text-sm leading-none md:h-7 md:text-xs",
|
|
584
|
+
compactControl: "h-8 px-2.5 text-sm leading-none md:h-7 md:text-xs",
|
|
585
|
+
input: "h-8 px-3 text-sm leading-none md:h-7 md:text-xs",
|
|
586
|
+
label: "text-xs",
|
|
587
|
+
icon: "h-4 w-4 md:h-3.5 md:w-3.5",
|
|
588
|
+
iconButton: "h-7 w-7 md:h-6 md:w-6",
|
|
589
|
+
tag: "h-5 max-w-24 px-2 text-[10px] leading-none"
|
|
590
|
+
},
|
|
591
|
+
md: {
|
|
592
|
+
control: "h-10 px-3 text-sm leading-none",
|
|
593
|
+
compactControl: "h-10 px-3 text-sm leading-none",
|
|
594
|
+
input: "h-10 px-4 text-sm leading-none",
|
|
595
|
+
label: "text-sm",
|
|
596
|
+
icon: "h-4 w-4",
|
|
597
|
+
iconButton: "h-8 w-8",
|
|
598
|
+
tag: "h-6 max-w-28 px-2 text-xs leading-none"
|
|
599
|
+
},
|
|
600
|
+
lg: {
|
|
601
|
+
control: "h-12 px-4 text-base leading-none",
|
|
602
|
+
compactControl: "h-12 px-4 text-base leading-none",
|
|
603
|
+
input: "h-12 px-5 text-base leading-none",
|
|
604
|
+
label: "text-base",
|
|
605
|
+
icon: "h-5 w-5",
|
|
606
|
+
iconButton: "h-10 w-10",
|
|
607
|
+
tag: "h-7 max-w-32 px-2.5 text-sm leading-none"
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
var formControlFixedClass = "min-h-0 overflow-hidden";
|
|
611
|
+
var formControlValueClass = "max-h-full min-w-0 flex-1 truncate whitespace-nowrap";
|
|
612
|
+
|
|
613
|
+
// src/components/DropdownMenu.tsx
|
|
614
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
615
|
+
var DropdownMenuContext = React3.createContext(null);
|
|
616
|
+
function useDropdownMenuClose() {
|
|
617
|
+
return React3.useContext(DropdownMenuContext)?.closeMenu ?? (() => {
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
function useResettingIndex(resetToken) {
|
|
621
|
+
const [state, setState] = React3.useState({ resetToken, index: -1 });
|
|
622
|
+
const activeIndex = Object.is(state.resetToken, resetToken) ? state.index : -1;
|
|
623
|
+
const setActiveIndex = React3.useCallback((nextIndex) => {
|
|
624
|
+
setState((prev) => {
|
|
625
|
+
const prevIndex = Object.is(prev.resetToken, resetToken) ? prev.index : -1;
|
|
626
|
+
return {
|
|
627
|
+
resetToken,
|
|
628
|
+
index: typeof nextIndex === "function" ? nextIndex(prevIndex) : nextIndex
|
|
629
|
+
};
|
|
630
|
+
});
|
|
631
|
+
}, [resetToken]);
|
|
632
|
+
return [activeIndex, setActiveIndex];
|
|
633
|
+
}
|
|
634
|
+
var DropdownMenu = ({
|
|
635
|
+
trigger,
|
|
636
|
+
children,
|
|
637
|
+
className,
|
|
638
|
+
contentClassName,
|
|
639
|
+
placement = "bottom-start",
|
|
640
|
+
closeOnSelect = true,
|
|
641
|
+
disabled = false,
|
|
642
|
+
isOpen,
|
|
643
|
+
onOpenChange,
|
|
644
|
+
openOnHover = false,
|
|
645
|
+
hoverCloseDelay = 120,
|
|
646
|
+
items,
|
|
647
|
+
borderMode
|
|
648
|
+
}) => {
|
|
649
|
+
const [internalOpen, setInternalOpen] = useState2(false);
|
|
650
|
+
const open = isOpen !== void 0 ? isOpen : internalOpen;
|
|
651
|
+
const setOpen = React3.useCallback(
|
|
652
|
+
(nextOpen) => {
|
|
653
|
+
if (isOpen === void 0) {
|
|
654
|
+
setInternalOpen(nextOpen);
|
|
655
|
+
}
|
|
656
|
+
onOpenChange?.(nextOpen);
|
|
657
|
+
},
|
|
658
|
+
[isOpen, onOpenChange]
|
|
659
|
+
);
|
|
660
|
+
const triggerRef = React3.useRef(null);
|
|
661
|
+
const menuRef = React3.useRef(null);
|
|
662
|
+
const itemsRef = React3.useRef([]);
|
|
663
|
+
const [activeIndex, setActiveIndex] = useResettingIndex(open);
|
|
664
|
+
const parentMenu = React3.useContext(DropdownMenuContext);
|
|
665
|
+
const hoverCloseTimeoutRef = React3.useRef(null);
|
|
666
|
+
const cancelHoverClose = React3.useCallback(() => {
|
|
667
|
+
if (hoverCloseTimeoutRef.current === null) return;
|
|
668
|
+
clearTimeout(hoverCloseTimeoutRef.current);
|
|
669
|
+
hoverCloseTimeoutRef.current = null;
|
|
670
|
+
}, []);
|
|
671
|
+
const scheduleHoverClose = React3.useCallback(() => {
|
|
672
|
+
if (!openOnHover) return;
|
|
673
|
+
cancelHoverClose();
|
|
674
|
+
hoverCloseTimeoutRef.current = setTimeout(() => {
|
|
675
|
+
hoverCloseTimeoutRef.current = null;
|
|
676
|
+
setOpen(false);
|
|
677
|
+
}, hoverCloseDelay);
|
|
678
|
+
}, [cancelHoverClose, hoverCloseDelay, openOnHover, setOpen]);
|
|
679
|
+
React3.useEffect(() => () => cancelHoverClose(), [cancelHoverClose]);
|
|
680
|
+
const closeMenu = React3.useCallback(() => {
|
|
681
|
+
cancelHoverClose();
|
|
682
|
+
setOpen(false);
|
|
683
|
+
parentMenu?.closeMenu();
|
|
684
|
+
}, [cancelHoverClose, parentMenu, setOpen]);
|
|
685
|
+
const getEnabledMenuItems = React3.useCallback(() => {
|
|
686
|
+
const menuEl = menuRef.current;
|
|
687
|
+
if (!menuEl) return [];
|
|
688
|
+
return Array.from(menuEl.querySelectorAll("[data-dropdown-menu-item]")).filter((el) => !el.disabled);
|
|
689
|
+
}, []);
|
|
690
|
+
const focusMenuItem = React3.useCallback((index) => {
|
|
691
|
+
const enabled = getEnabledMenuItems();
|
|
692
|
+
const item = enabled[index];
|
|
693
|
+
if (!item) return;
|
|
694
|
+
setActiveIndex(index);
|
|
695
|
+
item.focus();
|
|
696
|
+
item.scrollIntoView({ block: "nearest" });
|
|
697
|
+
}, [getEnabledMenuItems, setActiveIndex]);
|
|
698
|
+
useShadCNAnimations();
|
|
699
|
+
const globalConfig = useUnderverseUIConfig();
|
|
700
|
+
const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
|
|
701
|
+
React3.useEffect(() => {
|
|
702
|
+
if (!open) return;
|
|
703
|
+
const handleKeyNav = (e) => {
|
|
704
|
+
const active = document.activeElement;
|
|
705
|
+
const triggerEl = triggerRef.current;
|
|
706
|
+
const menuEl = menuRef.current;
|
|
707
|
+
if (!active || !triggerEl || !menuEl) return;
|
|
708
|
+
const isInMenu = menuEl.contains(active);
|
|
709
|
+
const isOnTrigger = triggerEl.contains(active);
|
|
710
|
+
const enabled = getEnabledMenuItems();
|
|
711
|
+
if (enabled.length === 0) return;
|
|
712
|
+
const currentIndex = enabled.findIndex((el) => el === active);
|
|
713
|
+
const baseIndex = currentIndex >= 0 ? currentIndex : activeIndex;
|
|
714
|
+
if (e.key === "ArrowDown") {
|
|
715
|
+
e.preventDefault();
|
|
716
|
+
const next = (baseIndex + 1 + enabled.length) % enabled.length;
|
|
717
|
+
focusMenuItem(next);
|
|
718
|
+
} else if (e.key === "ArrowUp") {
|
|
719
|
+
e.preventDefault();
|
|
720
|
+
const prev = (baseIndex - 1 + enabled.length) % enabled.length;
|
|
721
|
+
focusMenuItem(prev);
|
|
722
|
+
} else if (e.key === "Home") {
|
|
723
|
+
e.preventDefault();
|
|
724
|
+
focusMenuItem(0);
|
|
725
|
+
} else if (e.key === "End") {
|
|
726
|
+
e.preventDefault();
|
|
727
|
+
focusMenuItem(enabled.length - 1);
|
|
728
|
+
} else if (e.key === "Escape" && (isInMenu || isOnTrigger)) {
|
|
729
|
+
e.preventDefault();
|
|
730
|
+
closeMenu();
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
document.addEventListener("keydown", handleKeyNav, true);
|
|
734
|
+
return () => {
|
|
735
|
+
document.removeEventListener("keydown", handleKeyNav, true);
|
|
736
|
+
};
|
|
737
|
+
}, [open, activeIndex, closeMenu, focusMenuItem, getEnabledMenuItems]);
|
|
738
|
+
const menuContext = React3.useMemo(
|
|
739
|
+
() => ({
|
|
740
|
+
closeMenu,
|
|
741
|
+
closeOnSelect,
|
|
742
|
+
cancelHoverClose,
|
|
743
|
+
scheduleHoverClose
|
|
744
|
+
}),
|
|
745
|
+
[cancelHoverClose, closeMenu, closeOnSelect, scheduleHoverClose]
|
|
746
|
+
);
|
|
747
|
+
const handleItemClick = (itemOnClick) => {
|
|
748
|
+
itemOnClick();
|
|
749
|
+
if (closeOnSelect) {
|
|
750
|
+
closeMenu();
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
const menuBody = /* @__PURE__ */ jsx3(DropdownMenuContext.Provider, { value: menuContext, children: /* @__PURE__ */ jsx3(
|
|
754
|
+
"div",
|
|
755
|
+
{
|
|
756
|
+
ref: menuRef,
|
|
757
|
+
"data-dropdown-menu": true,
|
|
758
|
+
"data-state": open ? "open" : "closed",
|
|
759
|
+
role: "menu",
|
|
760
|
+
className: cn("min-w-40", className),
|
|
761
|
+
onMouseEnter: openOnHover ? () => {
|
|
762
|
+
cancelHoverClose();
|
|
763
|
+
parentMenu?.cancelHoverClose();
|
|
764
|
+
} : void 0,
|
|
765
|
+
onMouseLeave: openOnHover ? () => {
|
|
766
|
+
scheduleHoverClose();
|
|
767
|
+
parentMenu?.scheduleHoverClose();
|
|
768
|
+
} : void 0,
|
|
769
|
+
children: items ? items.map((item, index) => {
|
|
770
|
+
const IconComponent = item.icon;
|
|
771
|
+
return /* @__PURE__ */ jsxs2(
|
|
772
|
+
"button",
|
|
773
|
+
{
|
|
774
|
+
ref: (el) => {
|
|
775
|
+
if (el) itemsRef.current[index] = el;
|
|
776
|
+
},
|
|
777
|
+
onClick: () => handleItemClick(item.onClick),
|
|
778
|
+
disabled: item.disabled,
|
|
779
|
+
role: "menuitem",
|
|
780
|
+
"data-dropdown-menu-item": "",
|
|
781
|
+
tabIndex: -1,
|
|
782
|
+
style: {
|
|
783
|
+
animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
|
|
784
|
+
},
|
|
785
|
+
className: cn(
|
|
786
|
+
"dropdown-item flex w-full items-center gap-2 px-2.5 py-1.5 text-sm",
|
|
787
|
+
resolvedBorderMode ? getBorderRadiusClass(resolvedBorderMode) : "rounded-lg",
|
|
788
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
789
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
790
|
+
"focus:bg-accent focus:text-accent-foreground",
|
|
791
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
792
|
+
item.destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10"
|
|
793
|
+
),
|
|
794
|
+
children: [
|
|
795
|
+
IconComponent && /* @__PURE__ */ jsx3(IconComponent, { className: "h-4 w-4" }),
|
|
796
|
+
item.label
|
|
797
|
+
]
|
|
798
|
+
},
|
|
799
|
+
index
|
|
800
|
+
);
|
|
801
|
+
}) : children
|
|
802
|
+
}
|
|
803
|
+
) });
|
|
804
|
+
const triggerProps = trigger.props;
|
|
805
|
+
const {
|
|
806
|
+
ref: childRef,
|
|
807
|
+
onKeyDown: triggerOnKeyDown,
|
|
808
|
+
onClick: triggerOnClick,
|
|
809
|
+
onMouseEnter: triggerOnMouseEnter,
|
|
810
|
+
onMouseLeave: triggerOnMouseLeave
|
|
811
|
+
} = triggerProps;
|
|
812
|
+
const setTriggerNode = React3.useCallback((node) => {
|
|
813
|
+
setRefValue(childRef, node);
|
|
814
|
+
triggerRef.current = node;
|
|
815
|
+
}, [childRef]);
|
|
816
|
+
const handleTriggerKeyDown = React3.useCallback((event) => {
|
|
817
|
+
if (!disabled) {
|
|
818
|
+
if (event.key === "ArrowDown") {
|
|
819
|
+
event.preventDefault();
|
|
820
|
+
setOpen(true);
|
|
821
|
+
requestAnimationFrame(() => focusMenuItem(0));
|
|
822
|
+
} else if (event.key === "ArrowUp") {
|
|
823
|
+
event.preventDefault();
|
|
824
|
+
setOpen(true);
|
|
825
|
+
requestAnimationFrame(() => {
|
|
826
|
+
const enabled = getEnabledMenuItems();
|
|
827
|
+
focusMenuItem(enabled.length - 1);
|
|
828
|
+
});
|
|
829
|
+
} else if (event.key === "Escape") {
|
|
830
|
+
event.preventDefault();
|
|
831
|
+
setOpen(false);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
triggerOnKeyDown?.(event);
|
|
835
|
+
}, [disabled, focusMenuItem, getEnabledMenuItems, setOpen, triggerOnKeyDown]);
|
|
836
|
+
const handleTriggerClick = React3.useCallback((event) => {
|
|
837
|
+
if (openOnHover && !disabled) {
|
|
838
|
+
cancelHoverClose();
|
|
839
|
+
setOpen(true);
|
|
840
|
+
}
|
|
841
|
+
triggerOnClick?.(event);
|
|
842
|
+
}, [cancelHoverClose, disabled, openOnHover, setOpen, triggerOnClick]);
|
|
843
|
+
const handleTriggerMouseEnter = React3.useCallback((event) => {
|
|
844
|
+
if (openOnHover && !disabled) {
|
|
845
|
+
cancelHoverClose();
|
|
846
|
+
parentMenu?.cancelHoverClose();
|
|
847
|
+
setOpen(true);
|
|
848
|
+
}
|
|
849
|
+
triggerOnMouseEnter?.(event);
|
|
850
|
+
}, [cancelHoverClose, disabled, openOnHover, parentMenu, setOpen, triggerOnMouseEnter]);
|
|
851
|
+
const handleTriggerMouseLeave = React3.useCallback((event) => {
|
|
852
|
+
scheduleHoverClose();
|
|
853
|
+
triggerOnMouseLeave?.(event);
|
|
854
|
+
}, [scheduleHoverClose, triggerOnMouseLeave]);
|
|
855
|
+
const enhancedTrigger = React3.cloneElement(trigger, {
|
|
856
|
+
...triggerProps,
|
|
857
|
+
ref: setTriggerNode,
|
|
858
|
+
"aria-haspopup": "menu",
|
|
859
|
+
"aria-expanded": open,
|
|
860
|
+
onKeyDown: handleTriggerKeyDown,
|
|
861
|
+
onClick: handleTriggerClick,
|
|
862
|
+
onMouseEnter: handleTriggerMouseEnter,
|
|
863
|
+
onMouseLeave: handleTriggerMouseLeave
|
|
864
|
+
});
|
|
865
|
+
return /* @__PURE__ */ jsx3(
|
|
866
|
+
Popover,
|
|
867
|
+
{
|
|
868
|
+
open,
|
|
869
|
+
onOpenChange: setOpen,
|
|
870
|
+
trigger: enhancedTrigger,
|
|
871
|
+
placement,
|
|
872
|
+
disabled,
|
|
873
|
+
borderMode: resolvedBorderMode,
|
|
874
|
+
contentClassName: cn("p-1", contentClassName),
|
|
875
|
+
children: menuBody
|
|
876
|
+
}
|
|
877
|
+
);
|
|
878
|
+
};
|
|
879
|
+
var DropdownMenuItem = ({
|
|
880
|
+
children,
|
|
881
|
+
label,
|
|
882
|
+
description,
|
|
883
|
+
icon: Icon,
|
|
884
|
+
onClick,
|
|
885
|
+
disabled,
|
|
886
|
+
destructive,
|
|
887
|
+
active,
|
|
888
|
+
shortcut,
|
|
889
|
+
className,
|
|
890
|
+
closeOnSelect,
|
|
891
|
+
borderMode
|
|
892
|
+
}) => {
|
|
893
|
+
const menu = React3.useContext(DropdownMenuContext);
|
|
894
|
+
const shouldCloseOnSelect = closeOnSelect ?? menu?.closeOnSelect ?? false;
|
|
895
|
+
const globalConfig = useUnderverseUIConfig();
|
|
896
|
+
const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
|
|
897
|
+
return /* @__PURE__ */ jsxs2(
|
|
898
|
+
"button",
|
|
899
|
+
{
|
|
900
|
+
onClick: () => {
|
|
901
|
+
onClick?.();
|
|
902
|
+
if (shouldCloseOnSelect) {
|
|
903
|
+
menu?.closeMenu();
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
disabled,
|
|
907
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
908
|
+
"data-dropdown-menu-item": "",
|
|
909
|
+
tabIndex: -1,
|
|
910
|
+
className: cn(
|
|
911
|
+
"flex w-full items-center gap-2 px-3 py-2 text-sm transition-colors group cursor-pointer",
|
|
912
|
+
resolvedBorderMode ? getBorderRadiusClass(resolvedBorderMode) : "rounded-lg",
|
|
913
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
914
|
+
"focus:bg-accent focus:text-accent-foreground focus:outline-none",
|
|
915
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
916
|
+
destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
|
|
917
|
+
active && "bg-primary/10 text-primary",
|
|
918
|
+
className
|
|
919
|
+
),
|
|
920
|
+
children: [
|
|
921
|
+
Icon && /* @__PURE__ */ jsx3(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
|
|
922
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 text-left", children: [
|
|
923
|
+
label && /* @__PURE__ */ jsx3("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
|
|
924
|
+
description && /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: description }),
|
|
925
|
+
children
|
|
926
|
+
] }),
|
|
927
|
+
shortcut && /* @__PURE__ */ jsx3("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
|
|
928
|
+
active && /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx3("polyline", { points: "20 6 9 17 4 12" }) })
|
|
929
|
+
]
|
|
930
|
+
}
|
|
931
|
+
);
|
|
932
|
+
};
|
|
933
|
+
var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ jsx3("div", { className: cn("h-px bg-border my-1", className) });
|
|
934
|
+
var DropdownMenuSub = ({ label, icon: Icon, disabled, borderMode, children }) => {
|
|
935
|
+
const globalConfig = useUnderverseUIConfig();
|
|
936
|
+
const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
|
|
937
|
+
return /* @__PURE__ */ jsx3(
|
|
938
|
+
DropdownMenu,
|
|
939
|
+
{
|
|
940
|
+
trigger: /* @__PURE__ */ jsxs2(
|
|
941
|
+
"button",
|
|
942
|
+
{
|
|
943
|
+
type: "button",
|
|
944
|
+
disabled,
|
|
945
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
946
|
+
className: cn(
|
|
947
|
+
"flex w-full items-center gap-2 px-3 py-2 text-sm transition-colors cursor-pointer",
|
|
948
|
+
resolvedBorderMode ? getBorderRadiusClass(resolvedBorderMode) : "rounded-lg",
|
|
949
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
950
|
+
"focus:bg-accent focus:text-accent-foreground focus:outline-none",
|
|
951
|
+
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
952
|
+
),
|
|
953
|
+
children: [
|
|
954
|
+
Icon && /* @__PURE__ */ jsx3(Icon, { className: "w-4 h-4 shrink-0 opacity-60" }),
|
|
955
|
+
/* @__PURE__ */ jsx3("span", { className: "flex-1 text-left", children: label }),
|
|
956
|
+
/* @__PURE__ */ jsx3(ChevronRight, { className: "w-3 h-3 opacity-50" })
|
|
957
|
+
]
|
|
958
|
+
}
|
|
959
|
+
),
|
|
960
|
+
placement: "right",
|
|
961
|
+
openOnHover: true,
|
|
962
|
+
children
|
|
963
|
+
}
|
|
964
|
+
);
|
|
965
|
+
};
|
|
966
|
+
var SelectDropdown = ({ options, value, onChange, placeholder = "Select...", className, borderMode, size = "md" }) => {
|
|
967
|
+
const globalConfig = useUnderverseUIConfig();
|
|
968
|
+
const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
|
|
969
|
+
return /* @__PURE__ */ jsx3(
|
|
970
|
+
DropdownMenu,
|
|
971
|
+
{
|
|
972
|
+
trigger: /* @__PURE__ */ jsxs2(
|
|
973
|
+
"button",
|
|
974
|
+
{
|
|
975
|
+
className: cn(
|
|
976
|
+
"inline-flex items-center justify-between gap-2 border bg-background border-border/60",
|
|
977
|
+
resolvedBorderMode ? getBorderRadiusClass(resolvedBorderMode) : "rounded-2xl",
|
|
978
|
+
formControlFixedClass,
|
|
979
|
+
formControlSizeStyles[size].control,
|
|
980
|
+
"hover:bg-accent/50",
|
|
981
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
982
|
+
className
|
|
983
|
+
),
|
|
984
|
+
children: [
|
|
985
|
+
/* @__PURE__ */ jsx3("span", { className: cn(formControlValueClass, "max-w-64 text-foreground/90"), children: value || placeholder }),
|
|
986
|
+
/* @__PURE__ */ jsx3("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", className: "shrink-0 opacity-70", children: /* @__PURE__ */ jsx3("path", { d: "M6 8l4 4 4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
987
|
+
]
|
|
988
|
+
}
|
|
989
|
+
),
|
|
990
|
+
items: options.map((option) => ({
|
|
991
|
+
label: option,
|
|
992
|
+
onClick: () => onChange(option)
|
|
993
|
+
})),
|
|
994
|
+
borderMode: resolvedBorderMode
|
|
995
|
+
}
|
|
996
|
+
);
|
|
997
|
+
};
|
|
998
|
+
var DropdownMenu_default = DropdownMenu;
|
|
999
|
+
|
|
1000
|
+
// src/components/UEditor/url-safety.ts
|
|
1001
|
+
var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
1002
|
+
var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
1003
|
+
var FILE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "blob:"]);
|
|
1004
|
+
function normalizeUrlInput(raw) {
|
|
1005
|
+
return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
|
|
1006
|
+
}
|
|
1007
|
+
function isProtocolRelativeUrl(value) {
|
|
1008
|
+
return value.startsWith("//");
|
|
1009
|
+
}
|
|
1010
|
+
function isRelativeUrl(value) {
|
|
1011
|
+
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
|
|
1012
|
+
}
|
|
1013
|
+
function isValidIpv4Hostname(hostname) {
|
|
1014
|
+
const parts = hostname.split(".");
|
|
1015
|
+
return parts.length === 4 && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
|
|
1016
|
+
}
|
|
1017
|
+
function isValidWebHostname(hostname) {
|
|
1018
|
+
const normalized = hostname.toLowerCase();
|
|
1019
|
+
if (normalized === "localhost" || isValidIpv4Hostname(normalized)) return true;
|
|
1020
|
+
if (normalized.startsWith("[") && normalized.endsWith("]") && normalized.includes(":")) return true;
|
|
1021
|
+
const labels = normalized.split(".");
|
|
1022
|
+
if (labels.length < 2) return false;
|
|
1023
|
+
const validLabel = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)$/;
|
|
1024
|
+
return labels.every((label) => validLabel.test(label)) && /[a-z]/.test(labels.at(-1) ?? "");
|
|
1025
|
+
}
|
|
1026
|
+
function isValidLinkUrl(parsed) {
|
|
1027
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
1028
|
+
return isValidWebHostname(parsed.hostname);
|
|
1029
|
+
}
|
|
1030
|
+
if (parsed.protocol === "mailto:") {
|
|
1031
|
+
return /^[^@]+@[^@]+\.[^@]+$/.test(decodeURIComponent(parsed.pathname));
|
|
1032
|
+
}
|
|
1033
|
+
if (parsed.protocol === "tel:") {
|
|
1034
|
+
const number = decodeURIComponent(parsed.pathname);
|
|
1035
|
+
return /^\+?[\d().-]+$/.test(number) && (number.match(/\d/g)?.length ?? 0) >= 3;
|
|
1036
|
+
}
|
|
1037
|
+
return false;
|
|
1038
|
+
}
|
|
1039
|
+
function isDataImageUrl(value) {
|
|
1040
|
+
return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
|
|
1041
|
+
}
|
|
1042
|
+
function isDataFileUrl(value) {
|
|
1043
|
+
return /^data:[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*(?:;[a-z0-9!#$&^_.+-]+=[^;,]*)*;base64,[a-z0-9+/]*={0,2}$/i.test(value);
|
|
1044
|
+
}
|
|
1045
|
+
function isSafeUEditorUrl(raw, kind) {
|
|
1046
|
+
const value = normalizeUrlInput(raw);
|
|
1047
|
+
if (!value) return false;
|
|
1048
|
+
if (kind === "image" && isDataImageUrl(value)) return true;
|
|
1049
|
+
if (kind === "file" && isDataFileUrl(value)) return true;
|
|
1050
|
+
if (isProtocolRelativeUrl(value)) return false;
|
|
1051
|
+
if (isRelativeUrl(value)) return true;
|
|
1052
|
+
try {
|
|
1053
|
+
const parsed = new URL(value);
|
|
1054
|
+
if (kind === "image") return IMAGE_PROTOCOLS.has(parsed.protocol);
|
|
1055
|
+
if (kind === "file") return FILE_PROTOCOLS.has(parsed.protocol);
|
|
1056
|
+
return LINK_PROTOCOLS.has(parsed.protocol) && isValidLinkUrl(parsed);
|
|
1057
|
+
} catch {
|
|
1058
|
+
return false;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
function sanitizeUEditorUrl(raw, kind) {
|
|
1062
|
+
const value = raw.trim();
|
|
1063
|
+
if (!value) return "";
|
|
1064
|
+
if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
|
|
1065
|
+
if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
1066
|
+
const withProtocol = `https://${value}`;
|
|
1067
|
+
return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
|
|
1068
|
+
}
|
|
1069
|
+
return "";
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// src/components/UEditor/clipboard-images.ts
|
|
1073
|
+
import { Extension } from "@tiptap/core";
|
|
1074
|
+
import { Plugin } from "@tiptap/pm/state";
|
|
1075
|
+
|
|
1076
|
+
// src/components/UEditor/clipboard-tables.ts
|
|
1077
|
+
var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
|
|
1078
|
+
var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
|
|
1079
|
+
function getClipboardData(dataTransfer, type) {
|
|
1080
|
+
try {
|
|
1081
|
+
return dataTransfer.getData(type) ?? "";
|
|
1082
|
+
} catch {
|
|
1083
|
+
return "";
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
function extractClipboardHtmlFragment(html) {
|
|
1087
|
+
const startMarker = "<!--StartFragment-->";
|
|
1088
|
+
const endMarker = "<!--EndFragment-->";
|
|
1089
|
+
const start = html.indexOf(startMarker);
|
|
1090
|
+
const end = html.indexOf(endMarker);
|
|
1091
|
+
if (start >= 0 && end > start) {
|
|
1092
|
+
return html.slice(start + startMarker.length, end);
|
|
1093
|
+
}
|
|
1094
|
+
return html;
|
|
1095
|
+
}
|
|
1096
|
+
function normalizeClipboardCellText(value) {
|
|
1097
|
+
return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " ").replace(/[ \t]+\n/g, "\n").replace(/\n[ \t]+/g, "\n").replace(/\n+$/g, "").replace(/^\n+/g, "").trim();
|
|
1098
|
+
}
|
|
1099
|
+
function parseStyleDeclarations(styleText) {
|
|
1100
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
1101
|
+
if (!styleText) return declarations;
|
|
1102
|
+
for (const declaration of styleText.split(";")) {
|
|
1103
|
+
const separatorIndex = declaration.indexOf(":");
|
|
1104
|
+
if (separatorIndex <= 0) continue;
|
|
1105
|
+
const property = declaration.slice(0, separatorIndex).trim().toLowerCase();
|
|
1106
|
+
const value = cleanStyleValue(declaration.slice(separatorIndex + 1));
|
|
1107
|
+
if (!property || !value) continue;
|
|
1108
|
+
declarations.set(property, value);
|
|
1109
|
+
}
|
|
1110
|
+
return declarations;
|
|
1111
|
+
}
|
|
1112
|
+
function mergeStyleDeclarations(...sources) {
|
|
1113
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
1114
|
+
for (const source of sources) {
|
|
1115
|
+
if (!source) continue;
|
|
1116
|
+
for (const [property, value] of source.entries()) {
|
|
1117
|
+
declarations.set(property, value);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
return declarations;
|
|
1121
|
+
}
|
|
1122
|
+
function extractCssClassNames(selectorText) {
|
|
1123
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
1124
|
+
const classNamePattern = /\.([_a-zA-Z-][\w-]*)/g;
|
|
1125
|
+
let match;
|
|
1126
|
+
while ((match = classNamePattern.exec(selectorText)) !== null) {
|
|
1127
|
+
classNames.add(match[1]);
|
|
1128
|
+
}
|
|
1129
|
+
return classNames;
|
|
1130
|
+
}
|
|
1131
|
+
function parseClipboardCssClassStyles(doc) {
|
|
1132
|
+
const styleMap = /* @__PURE__ */ new Map();
|
|
1133
|
+
for (const styleElement of Array.from(doc.querySelectorAll("style"))) {
|
|
1134
|
+
const cssText = (styleElement.textContent ?? "").replace(/<!--|-->/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
1135
|
+
const rulePattern = /([^{}]+)\{([^{}]*)\}/g;
|
|
1136
|
+
let match;
|
|
1137
|
+
while ((match = rulePattern.exec(cssText)) !== null) {
|
|
1138
|
+
const classNames = extractCssClassNames(match[1]);
|
|
1139
|
+
if (classNames.size === 0) continue;
|
|
1140
|
+
const declarations = parseStyleDeclarations(match[2]);
|
|
1141
|
+
if (declarations.size === 0) continue;
|
|
1142
|
+
for (const className of classNames) {
|
|
1143
|
+
styleMap.set(className, mergeStyleDeclarations(styleMap.get(className), declarations));
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return styleMap;
|
|
1148
|
+
}
|
|
1149
|
+
function getElementStyleDeclarations(element, styleMap) {
|
|
1150
|
+
const classDeclarations = Array.from(element.classList).map((className) => styleMap.get(className));
|
|
1151
|
+
const inlineDeclarations = parseStyleDeclarations(element.getAttribute("style"));
|
|
1152
|
+
return mergeStyleDeclarations(...classDeclarations, inlineDeclarations);
|
|
1153
|
+
}
|
|
1154
|
+
function cleanStyleValue(value) {
|
|
1155
|
+
const normalized = value?.trim();
|
|
1156
|
+
if (!normalized) return null;
|
|
1157
|
+
if (/[\0<>;{}]/.test(normalized)) return null;
|
|
1158
|
+
if (/\b(?:expression|url|(?:repeating-)?(?:linear|radial|conic)-gradient)\s*\(/i.test(normalized)) return null;
|
|
1159
|
+
return normalized;
|
|
1160
|
+
}
|
|
1161
|
+
function normalizeColorValue(value) {
|
|
1162
|
+
const normalized = cleanStyleValue(value);
|
|
1163
|
+
if (!normalized) return null;
|
|
1164
|
+
if (/^(?:auto|inherit|initial|none|transparent|unset)$/i.test(normalized)) return null;
|
|
1165
|
+
return normalized;
|
|
1166
|
+
}
|
|
1167
|
+
function normalizeTextColorValue(value) {
|
|
1168
|
+
const normalized = normalizeColorValue(value);
|
|
1169
|
+
if (!normalized) return null;
|
|
1170
|
+
if (/^(?:automatic|windowtext|black|#000|#000000|rgb\(\s*0\s*,\s*0\s*,\s*0\s*\))$/i.test(normalized)) {
|
|
1171
|
+
return DEFAULT_HTML_TABLE_TEXT_COLOR;
|
|
1172
|
+
}
|
|
1173
|
+
return normalized;
|
|
1174
|
+
}
|
|
1175
|
+
function isWhiteColor(value) {
|
|
1176
|
+
if (!value) return false;
|
|
1177
|
+
return /^(?:white|#fff|#ffffff|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))$/i.test(value.trim());
|
|
1178
|
+
}
|
|
1179
|
+
function parseCssColorRgb(value) {
|
|
1180
|
+
const normalized = normalizeColorValue(value);
|
|
1181
|
+
if (!normalized) return null;
|
|
1182
|
+
const lowerColor = normalized.toLowerCase();
|
|
1183
|
+
if (lowerColor === "white") return { r: 255, g: 255, b: 255 };
|
|
1184
|
+
if (lowerColor === "black") return { r: 0, g: 0, b: 0 };
|
|
1185
|
+
const hexMatch = lowerColor.match(/^#([\da-f]{3}|[\da-f]{6})$/i);
|
|
1186
|
+
if (hexMatch) {
|
|
1187
|
+
const hex = hexMatch[1];
|
|
1188
|
+
const fullHex = hex.length === 3 ? hex.split("").map((part) => part + part).join("") : hex;
|
|
1189
|
+
return {
|
|
1190
|
+
r: Number.parseInt(fullHex.slice(0, 2), 16),
|
|
1191
|
+
g: Number.parseInt(fullHex.slice(2, 4), 16),
|
|
1192
|
+
b: Number.parseInt(fullHex.slice(4, 6), 16)
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
const rgbMatch = lowerColor.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/);
|
|
1196
|
+
if (rgbMatch) {
|
|
1197
|
+
return {
|
|
1198
|
+
r: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[1]))),
|
|
1199
|
+
g: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[2]))),
|
|
1200
|
+
b: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[3])))
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
function getRelativeLuminance(value) {
|
|
1206
|
+
const rgb = parseCssColorRgb(value);
|
|
1207
|
+
if (!rgb) return null;
|
|
1208
|
+
const toLinear = (channel) => {
|
|
1209
|
+
const normalized = channel / 255;
|
|
1210
|
+
return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
|
|
1211
|
+
};
|
|
1212
|
+
return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
|
|
1213
|
+
}
|
|
1214
|
+
function isLightTextColor(value) {
|
|
1215
|
+
const luminance = getRelativeLuminance(value);
|
|
1216
|
+
return luminance !== null && luminance >= 0.72;
|
|
1217
|
+
}
|
|
1218
|
+
function isDarkReadableBackground(value) {
|
|
1219
|
+
const luminance = getRelativeLuminance(value);
|
|
1220
|
+
return luminance !== null && luminance <= 0.45;
|
|
1221
|
+
}
|
|
1222
|
+
function splitCssTokens(value) {
|
|
1223
|
+
const tokens = [];
|
|
1224
|
+
let current = "";
|
|
1225
|
+
let depth = 0;
|
|
1226
|
+
for (const char of value) {
|
|
1227
|
+
if (char === "(") depth += 1;
|
|
1228
|
+
if (char === ")") depth = Math.max(0, depth - 1);
|
|
1229
|
+
if (/\s/.test(char) && depth === 0) {
|
|
1230
|
+
if (current) {
|
|
1231
|
+
tokens.push(current);
|
|
1232
|
+
current = "";
|
|
1233
|
+
}
|
|
1234
|
+
continue;
|
|
1235
|
+
}
|
|
1236
|
+
current += char;
|
|
1237
|
+
}
|
|
1238
|
+
if (current) tokens.push(current);
|
|
1239
|
+
return tokens;
|
|
1240
|
+
}
|
|
1241
|
+
function extractColorFromShorthand(value) {
|
|
1242
|
+
const normalized = cleanStyleValue(value);
|
|
1243
|
+
if (!normalized) return null;
|
|
1244
|
+
const explicitColor = normalized.match(/#[\da-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
|
|
1245
|
+
if (explicitColor) return explicitColor[0];
|
|
1246
|
+
const ignoredKeywords = /* @__PURE__ */ new Set([
|
|
1247
|
+
"border-box",
|
|
1248
|
+
"center",
|
|
1249
|
+
"contain",
|
|
1250
|
+
"content-box",
|
|
1251
|
+
"cover",
|
|
1252
|
+
"fixed",
|
|
1253
|
+
"inherit",
|
|
1254
|
+
"initial",
|
|
1255
|
+
"left",
|
|
1256
|
+
"local",
|
|
1257
|
+
"none",
|
|
1258
|
+
"no-repeat",
|
|
1259
|
+
"padding-box",
|
|
1260
|
+
"repeat",
|
|
1261
|
+
"repeat-x",
|
|
1262
|
+
"repeat-y",
|
|
1263
|
+
"right",
|
|
1264
|
+
"scroll",
|
|
1265
|
+
"top",
|
|
1266
|
+
"transparent",
|
|
1267
|
+
"unset"
|
|
1268
|
+
]);
|
|
1269
|
+
return splitCssTokens(normalized).find((token) => !ignoredKeywords.has(token.toLowerCase())) ?? null;
|
|
1270
|
+
}
|
|
1271
|
+
function getBackgroundColor(styles) {
|
|
1272
|
+
return normalizeColorValue(styles.get("background-color")) ?? normalizeColorValue(extractColorFromShorthand(styles.get("background")));
|
|
1273
|
+
}
|
|
1274
|
+
var BORDER_STYLES = /* @__PURE__ */ new Set([
|
|
1275
|
+
"dashed",
|
|
1276
|
+
"dotted",
|
|
1277
|
+
"double",
|
|
1278
|
+
"groove",
|
|
1279
|
+
"hidden",
|
|
1280
|
+
"inset",
|
|
1281
|
+
"none",
|
|
1282
|
+
"outset",
|
|
1283
|
+
"ridge",
|
|
1284
|
+
"solid"
|
|
1285
|
+
]);
|
|
1286
|
+
var BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set(["medium", "thick", "thin"]);
|
|
1287
|
+
function normalizeBorderStyle(value) {
|
|
1288
|
+
const normalized = cleanStyleValue(value);
|
|
1289
|
+
if (!normalized) return null;
|
|
1290
|
+
const styles = splitCssTokens(normalized).filter((token) => BORDER_STYLES.has(token.toLowerCase()));
|
|
1291
|
+
const usefulStyles = styles.filter((style) => !/^(?:hidden|none)$/i.test(style));
|
|
1292
|
+
return usefulStyles.length > 0 ? usefulStyles.join(" ") : null;
|
|
1293
|
+
}
|
|
1294
|
+
function normalizeBorderWidth(value) {
|
|
1295
|
+
const normalized = cleanStyleValue(value);
|
|
1296
|
+
if (!normalized) return null;
|
|
1297
|
+
const widths = splitCssTokens(normalized).filter((token) => {
|
|
1298
|
+
const lowerToken = token.toLowerCase();
|
|
1299
|
+
return BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token);
|
|
1300
|
+
});
|
|
1301
|
+
return widths.length > 0 ? widths.join(" ") : null;
|
|
1302
|
+
}
|
|
1303
|
+
function parseBorderShorthand(value) {
|
|
1304
|
+
const normalized = cleanStyleValue(value);
|
|
1305
|
+
if (!normalized) return null;
|
|
1306
|
+
const tokens = splitCssTokens(normalized);
|
|
1307
|
+
let borderStyle = null;
|
|
1308
|
+
let borderWidth = null;
|
|
1309
|
+
const colorTokens = [];
|
|
1310
|
+
for (const token of tokens) {
|
|
1311
|
+
const lowerToken = token.toLowerCase();
|
|
1312
|
+
if (!borderStyle && BORDER_STYLES.has(lowerToken)) {
|
|
1313
|
+
borderStyle = lowerToken;
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
if (!borderWidth && (BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token))) {
|
|
1317
|
+
borderWidth = token;
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
colorTokens.push(token);
|
|
1321
|
+
}
|
|
1322
|
+
if (borderStyle && /^(?:hidden|none)$/i.test(borderStyle)) return null;
|
|
1323
|
+
return {
|
|
1324
|
+
borderColor: normalizeColorValue(colorTokens.join(" ")),
|
|
1325
|
+
borderStyle,
|
|
1326
|
+
borderWidth
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
function getFirstParsedBorder(styles) {
|
|
1330
|
+
for (const property of ["border", "border-top", "border-right", "border-bottom", "border-left"]) {
|
|
1331
|
+
const border = parseBorderShorthand(styles.get(property));
|
|
1332
|
+
if (border) return border;
|
|
1333
|
+
}
|
|
1334
|
+
return null;
|
|
1335
|
+
}
|
|
1336
|
+
function getBorderAttrs(styles) {
|
|
1337
|
+
const parsedBorder = getFirstParsedBorder(styles);
|
|
1338
|
+
return {
|
|
1339
|
+
borderColor: normalizeColorValue(styles.get("border-color")) ?? parsedBorder?.borderColor ?? void 0,
|
|
1340
|
+
borderStyle: normalizeBorderStyle(styles.get("border-style")) ?? parsedBorder?.borderStyle ?? void 0,
|
|
1341
|
+
borderWidth: normalizeBorderWidth(styles.get("border-width")) ?? parsedBorder?.borderWidth ?? void 0
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
function parsePositiveInteger(value, max = 100) {
|
|
1345
|
+
if (!value) return null;
|
|
1346
|
+
const parsed = Number.parseInt(value, 10);
|
|
1347
|
+
if (!Number.isFinite(parsed) || parsed < 1) return null;
|
|
1348
|
+
return Math.min(parsed, max);
|
|
1349
|
+
}
|
|
1350
|
+
function parseCssSize(value) {
|
|
1351
|
+
const normalized = cleanStyleValue(value);
|
|
1352
|
+
if (!normalized) return null;
|
|
1353
|
+
const match = normalized.match(/^(\d+(?:\.\d+)?)(px|pt)?$/i);
|
|
1354
|
+
if (!match) return null;
|
|
1355
|
+
const amount = Number.parseFloat(match[1]);
|
|
1356
|
+
if (!Number.isFinite(amount) || amount <= 0) return null;
|
|
1357
|
+
return Math.round(match[2]?.toLowerCase() === "pt" ? amount * (4 / 3) : amount);
|
|
1358
|
+
}
|
|
1359
|
+
function getCellWidth(cell, styles, colspan) {
|
|
1360
|
+
if (colspan !== 1) return null;
|
|
1361
|
+
const width = parseCssSize(cell.getAttribute("data-colwidth") ?? cell.getAttribute("width") ?? styles.get("width"));
|
|
1362
|
+
return width ? [width] : null;
|
|
1363
|
+
}
|
|
1364
|
+
function getTableRowAttrs(row, styles) {
|
|
1365
|
+
const rowHeight = parseCssSize(
|
|
1366
|
+
row.getAttribute("data-row-height") ?? row.getAttribute("height") ?? styles.get("height")
|
|
1367
|
+
);
|
|
1368
|
+
return rowHeight ? { rowHeight } : void 0;
|
|
1369
|
+
}
|
|
1370
|
+
function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
|
|
1371
|
+
const colspan = parsePositiveInteger(cell.getAttribute("colspan")) ?? 1;
|
|
1372
|
+
const rowspan = parsePositiveInteger(cell.getAttribute("rowspan")) ?? 1;
|
|
1373
|
+
const backgroundColor = getBackgroundColor(styles) ?? normalizeColorValue(cell.getAttribute("data-background-color")) ?? normalizeColorValue(cell.getAttribute("bgcolor")) ?? defaultBackgroundColor;
|
|
1374
|
+
const borderAttrs = getBorderAttrs(styles);
|
|
1375
|
+
const colwidth = getCellWidth(cell, styles, colspan);
|
|
1376
|
+
const attrs = {};
|
|
1377
|
+
if (backgroundColor) attrs.backgroundColor = backgroundColor;
|
|
1378
|
+
if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
|
|
1379
|
+
if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
|
|
1380
|
+
if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
|
|
1381
|
+
if (cell.getAttribute("data-cell-id")) attrs.cellId = cell.getAttribute("data-cell-id") ?? void 0;
|
|
1382
|
+
if (cell.getAttribute("data-number-format")) attrs.numberFormat = cell.getAttribute("data-number-format") ?? void 0;
|
|
1383
|
+
if (cell.getAttribute("data-formula")) attrs.formula = cell.getAttribute("data-formula") ?? void 0;
|
|
1384
|
+
if (cell.getAttribute("data-computed-value")) attrs.computedValue = cell.getAttribute("data-computed-value") ?? void 0;
|
|
1385
|
+
if (colspan > 1) attrs.colspan = colspan;
|
|
1386
|
+
if (rowspan > 1) attrs.rowspan = rowspan;
|
|
1387
|
+
if (colwidth) attrs.colwidth = colwidth;
|
|
1388
|
+
return Object.keys(attrs).length > 0 ? attrs : void 0;
|
|
1389
|
+
}
|
|
1390
|
+
function marksEqual(left, right) {
|
|
1391
|
+
return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
|
|
1392
|
+
}
|
|
1393
|
+
function mergeMarks(base, additions) {
|
|
1394
|
+
const next = [...base ?? []];
|
|
1395
|
+
for (const addition of additions ?? []) {
|
|
1396
|
+
const existingIndex = next.findIndex((mark) => mark.type === addition.type);
|
|
1397
|
+
if (existingIndex >= 0) {
|
|
1398
|
+
const existingMark = next[existingIndex];
|
|
1399
|
+
next[existingIndex] = {
|
|
1400
|
+
...existingMark,
|
|
1401
|
+
attrs: {
|
|
1402
|
+
...existingMark.attrs ?? {},
|
|
1403
|
+
...addition.attrs ?? {}
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
next.push(addition);
|
|
1409
|
+
}
|
|
1410
|
+
return next.length > 0 ? next : void 0;
|
|
1411
|
+
}
|
|
1412
|
+
function getMarkColor(marks, markType) {
|
|
1413
|
+
const mark = marks?.find((candidate) => candidate.type === markType);
|
|
1414
|
+
const color = mark?.attrs?.color;
|
|
1415
|
+
return typeof color === "string" ? color : null;
|
|
1416
|
+
}
|
|
1417
|
+
function replaceTextStyleColor(marks, color) {
|
|
1418
|
+
let replaced = false;
|
|
1419
|
+
const next = (marks ?? []).map((mark) => {
|
|
1420
|
+
if (mark.type !== "textStyle") return mark;
|
|
1421
|
+
replaced = true;
|
|
1422
|
+
return {
|
|
1423
|
+
...mark,
|
|
1424
|
+
attrs: {
|
|
1425
|
+
...mark.attrs ?? {},
|
|
1426
|
+
color
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
});
|
|
1430
|
+
if (!replaced) {
|
|
1431
|
+
next.unshift({ type: "textStyle", attrs: { color } });
|
|
1432
|
+
}
|
|
1433
|
+
return next;
|
|
1434
|
+
}
|
|
1435
|
+
function ensureReadableSpreadsheetSegments(segments, cellBackgroundColor) {
|
|
1436
|
+
return segments.map((segment) => {
|
|
1437
|
+
const textColor = getMarkColor(segment.marks, "textStyle");
|
|
1438
|
+
if (!isLightTextColor(textColor)) return segment;
|
|
1439
|
+
const inlineBackgroundColor = getMarkColor(segment.marks, "highlight");
|
|
1440
|
+
if (isDarkReadableBackground(inlineBackgroundColor) || isDarkReadableBackground(cellBackgroundColor)) {
|
|
1441
|
+
return segment;
|
|
1442
|
+
}
|
|
1443
|
+
return {
|
|
1444
|
+
...segment,
|
|
1445
|
+
marks: replaceTextStyleColor(segment.marks, DEFAULT_HTML_TABLE_TEXT_COLOR)
|
|
1446
|
+
};
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
function getElementInlineMarks(element, styles) {
|
|
1450
|
+
const marks = [];
|
|
1451
|
+
const tagName = element.tagName;
|
|
1452
|
+
const color = normalizeTextColorValue(styles.get("color") ?? element.getAttribute("color"));
|
|
1453
|
+
const backgroundColor = getBackgroundColor(styles);
|
|
1454
|
+
const fontWeight = styles.get("font-weight")?.toLowerCase();
|
|
1455
|
+
const fontStyle = styles.get("font-style")?.toLowerCase();
|
|
1456
|
+
const textDecoration = styles.get("text-decoration")?.toLowerCase();
|
|
1457
|
+
if (color) {
|
|
1458
|
+
marks.push({ type: "textStyle", attrs: { color } });
|
|
1459
|
+
}
|
|
1460
|
+
if (backgroundColor && !isWhiteColor(backgroundColor)) {
|
|
1461
|
+
marks.push({ type: "highlight", attrs: { color: backgroundColor } });
|
|
1462
|
+
}
|
|
1463
|
+
if (tagName === "B" || tagName === "STRONG" || fontWeight === "bold" || /^\d+$/.test(fontWeight ?? "") && Number(fontWeight) >= 600) {
|
|
1464
|
+
marks.push({ type: "bold" });
|
|
1465
|
+
}
|
|
1466
|
+
if (tagName === "I" || tagName === "EM" || fontStyle === "italic") {
|
|
1467
|
+
marks.push({ type: "italic" });
|
|
1468
|
+
}
|
|
1469
|
+
if (tagName === "U" || textDecoration?.includes("underline")) {
|
|
1470
|
+
marks.push({ type: "underline" });
|
|
1471
|
+
}
|
|
1472
|
+
return marks.length > 0 ? marks : void 0;
|
|
1473
|
+
}
|
|
1474
|
+
function appendTextSegment(segments, segment) {
|
|
1475
|
+
if (!segment.text) return;
|
|
1476
|
+
const lastSegment = segments[segments.length - 1];
|
|
1477
|
+
if (lastSegment && marksEqual(lastSegment.marks, segment.marks)) {
|
|
1478
|
+
lastSegment.text += segment.text;
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
segments.push(segment);
|
|
1482
|
+
}
|
|
1483
|
+
function segmentsEndWithNewline(segments) {
|
|
1484
|
+
return segments.length > 0 && segments[segments.length - 1].text.endsWith("\n");
|
|
1485
|
+
}
|
|
1486
|
+
function normalizeClipboardTextSegments(segments) {
|
|
1487
|
+
const normalizedSegments = [];
|
|
1488
|
+
for (const segment of segments) {
|
|
1489
|
+
appendTextSegment(normalizedSegments, {
|
|
1490
|
+
text: segment.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " "),
|
|
1491
|
+
marks: segment.marks
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
while (normalizedSegments.length > 0) {
|
|
1495
|
+
const firstSegment = normalizedSegments[0];
|
|
1496
|
+
firstSegment.text = firstSegment.text.replace(/^\s+/, "");
|
|
1497
|
+
if (firstSegment.text) break;
|
|
1498
|
+
normalizedSegments.shift();
|
|
1499
|
+
}
|
|
1500
|
+
while (normalizedSegments.length > 0) {
|
|
1501
|
+
const lastSegment = normalizedSegments[normalizedSegments.length - 1];
|
|
1502
|
+
lastSegment.text = lastSegment.text.replace(/\s+$/, "");
|
|
1503
|
+
if (lastSegment.text) break;
|
|
1504
|
+
normalizedSegments.pop();
|
|
1505
|
+
}
|
|
1506
|
+
return normalizedSegments;
|
|
1507
|
+
}
|
|
1508
|
+
function getClipboardCellText(node) {
|
|
1509
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
1510
|
+
return node.textContent ?? "";
|
|
1511
|
+
}
|
|
1512
|
+
if (!(node instanceof HTMLElement)) {
|
|
1513
|
+
return "";
|
|
1514
|
+
}
|
|
1515
|
+
if (node.tagName === "BR") {
|
|
1516
|
+
return "\n";
|
|
1517
|
+
}
|
|
1518
|
+
const childText = Array.from(node.childNodes).map(getClipboardCellText).join("");
|
|
1519
|
+
if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && childText && !childText.endsWith("\n")) {
|
|
1520
|
+
return `${childText}
|
|
1521
|
+
`;
|
|
1522
|
+
}
|
|
1523
|
+
return childText;
|
|
1524
|
+
}
|
|
1525
|
+
function getClipboardCellSegments(node, styleMap, inheritedMarks) {
|
|
1526
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
1527
|
+
return [{ text: node.textContent ?? "", marks: inheritedMarks }];
|
|
1528
|
+
}
|
|
1529
|
+
if (!(node instanceof HTMLElement)) {
|
|
1530
|
+
return [];
|
|
1531
|
+
}
|
|
1532
|
+
if (node.tagName === "BR") {
|
|
1533
|
+
return [{ text: "\n", marks: inheritedMarks }];
|
|
1534
|
+
}
|
|
1535
|
+
const styles = getElementStyleDeclarations(node, styleMap);
|
|
1536
|
+
const marks = mergeMarks(inheritedMarks, getElementInlineMarks(node, styles));
|
|
1537
|
+
const segments = [];
|
|
1538
|
+
for (const childNode of Array.from(node.childNodes)) {
|
|
1539
|
+
for (const segment of getClipboardCellSegments(childNode, styleMap, marks)) {
|
|
1540
|
+
appendTextSegment(segments, segment);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && segments.length > 0 && !segmentsEndWithNewline(segments)) {
|
|
1544
|
+
appendTextSegment(segments, { text: "\n" });
|
|
1545
|
+
}
|
|
1546
|
+
return segments;
|
|
1547
|
+
}
|
|
1548
|
+
function getClipboardCellChildSegments(cell, styleMap, inheritedMarks) {
|
|
1549
|
+
const segments = [];
|
|
1550
|
+
for (const childNode of Array.from(cell.childNodes)) {
|
|
1551
|
+
for (const segment of getClipboardCellSegments(childNode, styleMap, inheritedMarks)) {
|
|
1552
|
+
appendTextSegment(segments, segment);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
return normalizeClipboardTextSegments(segments);
|
|
1556
|
+
}
|
|
1557
|
+
function getHtmlTableRows(table, styleMap) {
|
|
1558
|
+
const rows = Array.from(table.querySelectorAll("tr")).map(
|
|
1559
|
+
(row) => ({
|
|
1560
|
+
attrs: getTableRowAttrs(row, getElementStyleDeclarations(row, styleMap)),
|
|
1561
|
+
cells: Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => {
|
|
1562
|
+
const styles = getElementStyleDeclarations(cell, styleMap);
|
|
1563
|
+
const textColor = normalizeTextColorValue(styles.get("color")) ?? DEFAULT_HTML_TABLE_TEXT_COLOR;
|
|
1564
|
+
const inheritedMarks = [{ type: "textStyle", attrs: { color: textColor } }];
|
|
1565
|
+
const attrs = getTableCellAttrs(cell, styles, DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR);
|
|
1566
|
+
const segments = ensureReadableSpreadsheetSegments(
|
|
1567
|
+
getClipboardCellChildSegments(cell, styleMap, inheritedMarks),
|
|
1568
|
+
attrs?.backgroundColor
|
|
1569
|
+
);
|
|
1570
|
+
return {
|
|
1571
|
+
text: normalizeClipboardCellText(getClipboardCellText(cell)),
|
|
1572
|
+
isHeader: cell.tagName === "TH",
|
|
1573
|
+
attrs,
|
|
1574
|
+
segments: segments.length > 0 ? segments : void 0,
|
|
1575
|
+
textColor
|
|
1576
|
+
};
|
|
1577
|
+
})
|
|
1578
|
+
})
|
|
1579
|
+
);
|
|
1580
|
+
return rows.filter((row) => row.cells.length > 0);
|
|
1581
|
+
}
|
|
1582
|
+
function createTextMarks(cell) {
|
|
1583
|
+
return cell.textColor ? [{ type: "textStyle", attrs: { color: cell.textColor } }] : void 0;
|
|
1584
|
+
}
|
|
1585
|
+
function createParagraphContent(text, marks) {
|
|
1586
|
+
return text ? {
|
|
1587
|
+
type: "paragraph",
|
|
1588
|
+
content: [{ type: "text", text, ...marks ? { marks } : {} }]
|
|
1589
|
+
} : { type: "paragraph" };
|
|
1590
|
+
}
|
|
1591
|
+
function createParagraphContentFromSegments(segments) {
|
|
1592
|
+
const paragraphs = [[]];
|
|
1593
|
+
for (const segment of segments) {
|
|
1594
|
+
const parts = segment.text.split("\n");
|
|
1595
|
+
parts.forEach((part, index) => {
|
|
1596
|
+
if (part) {
|
|
1597
|
+
paragraphs[paragraphs.length - 1].push({ text: part, marks: segment.marks });
|
|
1598
|
+
}
|
|
1599
|
+
if (index < parts.length - 1) {
|
|
1600
|
+
paragraphs.push([]);
|
|
1601
|
+
}
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
return paragraphs.map((paragraphSegments) => {
|
|
1605
|
+
const content = paragraphSegments.map((segment) => ({
|
|
1606
|
+
type: "text",
|
|
1607
|
+
text: segment.text,
|
|
1608
|
+
...segment.marks ? { marks: segment.marks } : {}
|
|
1609
|
+
}));
|
|
1610
|
+
return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
function createTableCellContent(cell) {
|
|
1614
|
+
const lines = cell.text.split("\n");
|
|
1615
|
+
const marks = createTextMarks(cell);
|
|
1616
|
+
const paragraphs = cell.segments && cell.segments.length > 0 ? createParagraphContentFromSegments(cell.segments) : (lines.length > 0 ? lines : [""]).map((line) => createParagraphContent(line, marks));
|
|
1617
|
+
return {
|
|
1618
|
+
type: cell.isHeader ? "tableHeader" : "tableCell",
|
|
1619
|
+
...cell.attrs ? { attrs: cell.attrs } : {},
|
|
1620
|
+
content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
function getRowspanLimitedCell(cell, remainingRowCount) {
|
|
1624
|
+
const attrs = cell.attrs;
|
|
1625
|
+
if (!attrs?.rowspan || attrs.rowspan <= remainingRowCount) return cell;
|
|
1626
|
+
if (remainingRowCount <= 1) {
|
|
1627
|
+
const { rowspan: _rowspan, ...nextAttrs } = attrs;
|
|
1628
|
+
return {
|
|
1629
|
+
...cell,
|
|
1630
|
+
attrs: Object.keys(nextAttrs).length > 0 ? nextAttrs : void 0
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
return {
|
|
1634
|
+
...cell,
|
|
1635
|
+
attrs: {
|
|
1636
|
+
...attrs,
|
|
1637
|
+
rowspan: remainingRowCount
|
|
1638
|
+
}
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
function normalizeTableRows(rows) {
|
|
1642
|
+
const positionedRows = [];
|
|
1643
|
+
let rowspans = [];
|
|
1644
|
+
let columnCount = 0;
|
|
1645
|
+
rows.forEach((row, rowIndex) => {
|
|
1646
|
+
const coveredColumns = rowspans.map((span) => span > 0);
|
|
1647
|
+
const nextRowspans = rowspans.map((span) => Math.max(0, span - 1));
|
|
1648
|
+
const positionedCells = [];
|
|
1649
|
+
let columnIndex = 0;
|
|
1650
|
+
for (const rawCell of row.cells) {
|
|
1651
|
+
while (coveredColumns[columnIndex]) columnIndex += 1;
|
|
1652
|
+
const remainingRowCount = rows.length - rowIndex;
|
|
1653
|
+
const cell = getRowspanLimitedCell(rawCell, remainingRowCount);
|
|
1654
|
+
const colspan = Math.max(1, cell.attrs?.colspan ?? 1);
|
|
1655
|
+
const rowspan = Math.max(1, cell.attrs?.rowspan ?? 1);
|
|
1656
|
+
positionedCells.push({ startColumn: columnIndex, colspan, cell });
|
|
1657
|
+
if (rowspan > 1) {
|
|
1658
|
+
for (let offset = 0; offset < colspan; offset += 1) {
|
|
1659
|
+
const spannedColumn = columnIndex + offset;
|
|
1660
|
+
nextRowspans[spannedColumn] = Math.max(nextRowspans[spannedColumn] ?? 0, rowspan - 1);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
columnIndex += colspan;
|
|
1664
|
+
}
|
|
1665
|
+
const lastCoveredColumn = coveredColumns.reduce((lastIndex, covered, index) => covered ? index : lastIndex, -1);
|
|
1666
|
+
const lastFutureRowspanColumn = nextRowspans.reduce((lastIndex, span, index) => span > 0 ? index : lastIndex, -1);
|
|
1667
|
+
columnCount = Math.max(columnCount, columnIndex, lastCoveredColumn + 1, lastFutureRowspanColumn + 1);
|
|
1668
|
+
positionedRows.push({
|
|
1669
|
+
attrs: row.attrs,
|
|
1670
|
+
cells: positionedCells,
|
|
1671
|
+
coveredColumns
|
|
1672
|
+
});
|
|
1673
|
+
rowspans = nextRowspans;
|
|
1674
|
+
});
|
|
1675
|
+
return { positionedRows, columnCount };
|
|
1676
|
+
}
|
|
1677
|
+
function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
|
|
1678
|
+
const content = [];
|
|
1679
|
+
const cellByStartColumn = new Map(row.cells.map((cell) => [cell.startColumn, cell]));
|
|
1680
|
+
let columnIndex = 0;
|
|
1681
|
+
while (columnIndex < columnCount) {
|
|
1682
|
+
if (row.coveredColumns[columnIndex]) {
|
|
1683
|
+
columnIndex += 1;
|
|
1684
|
+
continue;
|
|
1685
|
+
}
|
|
1686
|
+
const positionedCell = cellByStartColumn.get(columnIndex);
|
|
1687
|
+
if (positionedCell) {
|
|
1688
|
+
content.push(createTableCellContent(positionedCell.cell));
|
|
1689
|
+
columnIndex += positionedCell.colspan;
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
content.push(createTableCellContent({ text: "", isHeader: false, attrs: fillerCellAttrs }));
|
|
1693
|
+
columnIndex += 1;
|
|
1694
|
+
}
|
|
1695
|
+
return content;
|
|
1696
|
+
}
|
|
1697
|
+
function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
|
|
1698
|
+
const tableRows = rows.filter((row) => row.cells.length > 0);
|
|
1699
|
+
if (tableRows.length === 0) return null;
|
|
1700
|
+
const { positionedRows, columnCount } = normalizeTableRows(tableRows);
|
|
1701
|
+
if (columnCount < minColumnCount) return null;
|
|
1702
|
+
return {
|
|
1703
|
+
type: "table",
|
|
1704
|
+
content: positionedRows.map((row) => ({
|
|
1705
|
+
type: "tableRow",
|
|
1706
|
+
...row.attrs ? { attrs: row.attrs } : {},
|
|
1707
|
+
content: createNormalizedRowContent(row, columnCount, fillerCellAttrs)
|
|
1708
|
+
}))
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
function getClipboardTableContent(dataTransfer) {
|
|
1712
|
+
const html = getClipboardData(dataTransfer, "text/html");
|
|
1713
|
+
if (!/<table(?:\s|>)/i.test(html)) return null;
|
|
1714
|
+
if (typeof DOMParser === "undefined") return null;
|
|
1715
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
1716
|
+
const fragment = extractClipboardHtmlFragment(html);
|
|
1717
|
+
const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
|
|
1718
|
+
const styleMap = parseClipboardCssClassStyles(doc);
|
|
1719
|
+
const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
|
|
1720
|
+
if (!(table instanceof HTMLTableElement)) return null;
|
|
1721
|
+
return createTableContent(getHtmlTableRows(table, styleMap), 1, {
|
|
1722
|
+
backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
function parseClipboardTsvRows(text) {
|
|
1726
|
+
const rows = [];
|
|
1727
|
+
let row = [];
|
|
1728
|
+
let field = "";
|
|
1729
|
+
let inQuotes = false;
|
|
1730
|
+
const pushField = () => {
|
|
1731
|
+
row.push(field);
|
|
1732
|
+
field = "";
|
|
1733
|
+
};
|
|
1734
|
+
const pushRow = () => {
|
|
1735
|
+
pushField();
|
|
1736
|
+
rows.push(row);
|
|
1737
|
+
row = [];
|
|
1738
|
+
};
|
|
1739
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
1740
|
+
const char = text[index];
|
|
1741
|
+
const nextChar = text[index + 1];
|
|
1742
|
+
if (inQuotes) {
|
|
1743
|
+
if (char === '"' && nextChar === '"') {
|
|
1744
|
+
field += '"';
|
|
1745
|
+
index += 1;
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
if (char === '"') {
|
|
1749
|
+
inQuotes = false;
|
|
1750
|
+
continue;
|
|
1751
|
+
}
|
|
1752
|
+
field += char;
|
|
1753
|
+
continue;
|
|
1754
|
+
}
|
|
1755
|
+
if (char === '"' && field.length === 0) {
|
|
1756
|
+
inQuotes = true;
|
|
1757
|
+
continue;
|
|
1758
|
+
}
|
|
1759
|
+
if (char === " ") {
|
|
1760
|
+
pushField();
|
|
1761
|
+
continue;
|
|
1762
|
+
}
|
|
1763
|
+
if (char === "\n") {
|
|
1764
|
+
pushRow();
|
|
1765
|
+
continue;
|
|
1766
|
+
}
|
|
1767
|
+
field += char;
|
|
1768
|
+
}
|
|
1769
|
+
pushRow();
|
|
1770
|
+
while (rows.length > 0 && rows[rows.length - 1].every((cell) => cell === "")) {
|
|
1771
|
+
rows.pop();
|
|
1772
|
+
}
|
|
1773
|
+
return rows;
|
|
1774
|
+
}
|
|
1775
|
+
function getClipboardTsvTableContent(dataTransfer) {
|
|
1776
|
+
const text = getClipboardData(dataTransfer, "text/plain").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1777
|
+
if (!text.includes(" ")) return null;
|
|
1778
|
+
const rows = parseClipboardTsvRows(text);
|
|
1779
|
+
return createTableContent(
|
|
1780
|
+
rows.map((row) => ({
|
|
1781
|
+
cells: row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))
|
|
1782
|
+
})),
|
|
1783
|
+
2
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
// src/components/UEditor/clipboard-images.ts
|
|
1788
|
+
var DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
1789
|
+
var DEFAULT_UEDITOR_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"];
|
|
1790
|
+
function getImageFiles(dataTransfer) {
|
|
1791
|
+
if (!dataTransfer) return [];
|
|
1792
|
+
const itemFiles = [];
|
|
1793
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1794
|
+
for (const item of Array.from(dataTransfer.items ?? [])) {
|
|
1795
|
+
if (item.kind !== "file") continue;
|
|
1796
|
+
if (!item.type.startsWith("image/")) continue;
|
|
1797
|
+
const file = item.getAsFile();
|
|
1798
|
+
if (!file) continue;
|
|
1799
|
+
byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
|
|
1800
|
+
}
|
|
1801
|
+
itemFiles.push(...Array.from(byKey.values()));
|
|
1802
|
+
if (itemFiles.length > 0) return itemFiles;
|
|
1803
|
+
for (const file of Array.from(dataTransfer.files ?? [])) {
|
|
1804
|
+
if (!file.type.startsWith("image/")) continue;
|
|
1805
|
+
byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
|
|
1806
|
+
}
|
|
1807
|
+
return Array.from(byKey.values());
|
|
1808
|
+
}
|
|
1809
|
+
function fileToDataUrl(file) {
|
|
1810
|
+
return new Promise((resolve, reject) => {
|
|
1811
|
+
const reader = new FileReader();
|
|
1812
|
+
reader.onload = () => resolve(String(reader.result ?? ""));
|
|
1813
|
+
reader.onerror = () => reject(reader.error ?? new Error("Failed to read image file"));
|
|
1814
|
+
reader.readAsDataURL(file);
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
async function resolveImageSrc(file, options) {
|
|
1818
|
+
if (options.insertMode === "upload" && options.upload) {
|
|
1819
|
+
try {
|
|
1820
|
+
const result = await options.upload(file);
|
|
1821
|
+
const src = typeof result === "string" ? sanitizeUEditorUrl(result, "image") : "";
|
|
1822
|
+
if (src) return src;
|
|
1823
|
+
} catch (err) {
|
|
1824
|
+
if (!options.fallbackToDataUrl) throw err;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
return fileToDataUrl(file);
|
|
1828
|
+
}
|
|
1829
|
+
var ClipboardImages = Extension.create({
|
|
1830
|
+
name: "clipboardImages",
|
|
1831
|
+
addOptions() {
|
|
1832
|
+
return {
|
|
1833
|
+
maxFileSize: DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
|
|
1834
|
+
allowedMimeTypes: DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
|
|
1835
|
+
upload: void 0,
|
|
1836
|
+
fallbackToDataUrl: true,
|
|
1837
|
+
insertMode: "base64"
|
|
1838
|
+
};
|
|
1839
|
+
},
|
|
1840
|
+
addProseMirrorPlugins() {
|
|
1841
|
+
const editor = this.editor;
|
|
1842
|
+
const options = this.options;
|
|
1843
|
+
const insertFiles = async (files, selectionPos) => {
|
|
1844
|
+
if (selectionPos !== void 0) {
|
|
1845
|
+
editor.commands.setTextSelection(selectionPos);
|
|
1846
|
+
}
|
|
1847
|
+
for (const file of files) {
|
|
1848
|
+
if (file.size > options.maxFileSize) continue;
|
|
1849
|
+
if (options.allowedMimeTypes.length > 0 && !options.allowedMimeTypes.includes(file.type)) continue;
|
|
1850
|
+
try {
|
|
1851
|
+
const src = await resolveImageSrc(file, options);
|
|
1852
|
+
editor.chain().focus().setImage({ src, alt: file.name }).run();
|
|
1853
|
+
editor.commands.createParagraphNear();
|
|
1854
|
+
} catch {
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
};
|
|
1858
|
+
return [
|
|
1859
|
+
new Plugin({
|
|
1860
|
+
props: {
|
|
1861
|
+
handlePaste: (_view, event) => {
|
|
1862
|
+
if (!event || !event.clipboardData) return false;
|
|
1863
|
+
const tableContent = getClipboardTableContent(event.clipboardData);
|
|
1864
|
+
if (tableContent) {
|
|
1865
|
+
event.preventDefault();
|
|
1866
|
+
editor.chain().focus().insertContent(tableContent).run();
|
|
1867
|
+
return true;
|
|
1868
|
+
}
|
|
1869
|
+
const tsvTableContent = getClipboardTsvTableContent(event.clipboardData);
|
|
1870
|
+
if (tsvTableContent) {
|
|
1871
|
+
event.preventDefault();
|
|
1872
|
+
editor.chain().focus().insertContent(tsvTableContent).run();
|
|
1873
|
+
return true;
|
|
1874
|
+
}
|
|
1875
|
+
const files = getImageFiles(event.clipboardData);
|
|
1876
|
+
if (files.length === 0) return false;
|
|
1877
|
+
event.preventDefault();
|
|
1878
|
+
void insertFiles(files);
|
|
1879
|
+
return true;
|
|
1880
|
+
},
|
|
1881
|
+
handleDrop: (view, event, _slice, moved) => {
|
|
1882
|
+
if (moved) return false;
|
|
1883
|
+
if (!(event instanceof DragEvent)) return false;
|
|
1884
|
+
const files = getImageFiles(event.dataTransfer);
|
|
1885
|
+
if (files.length === 0) return false;
|
|
1886
|
+
const pos = view.posAtCoords({ left: event.clientX, top: event.clientY })?.pos;
|
|
1887
|
+
event.preventDefault();
|
|
1888
|
+
void insertFiles(files, pos);
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
})
|
|
1893
|
+
];
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1896
|
+
|
|
1897
|
+
// src/components/UEditor/inputs.tsx
|
|
1898
|
+
import { useEffect as useEffect3, useId as useId2, useRef as useRef2, useState as useState3 } from "react";
|
|
1899
|
+
import { Check, X } from "lucide-react";
|
|
1900
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1901
|
+
function normalizeUrl(raw) {
|
|
1902
|
+
return sanitizeUEditorUrl(raw, "link");
|
|
1903
|
+
}
|
|
1904
|
+
var LinkInput = ({
|
|
1905
|
+
onSubmit,
|
|
1906
|
+
onCancel,
|
|
1907
|
+
initialUrl = ""
|
|
1908
|
+
}) => {
|
|
1909
|
+
const t = useSmartTranslations("UEditor");
|
|
1910
|
+
const [url, setUrl] = useState3(initialUrl);
|
|
1911
|
+
const [error, setError] = useState3("");
|
|
1912
|
+
const inputRef = useRef2(null);
|
|
1913
|
+
const errorId = useId2();
|
|
1914
|
+
useEffect3(() => {
|
|
1915
|
+
inputRef.current?.focus();
|
|
1916
|
+
inputRef.current?.select();
|
|
1917
|
+
}, []);
|
|
1918
|
+
const handleSubmit = (e) => {
|
|
1919
|
+
e.preventDefault();
|
|
1920
|
+
const normalized = normalizeUrl(url);
|
|
1921
|
+
if (!normalized) {
|
|
1922
|
+
setError(t("linkInput.invalid"));
|
|
1923
|
+
return;
|
|
1924
|
+
}
|
|
1925
|
+
setError("");
|
|
1926
|
+
onSubmit(normalized);
|
|
1927
|
+
};
|
|
1928
|
+
return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "p-2", children: [
|
|
1929
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
1930
|
+
/* @__PURE__ */ jsx4(
|
|
1931
|
+
"input",
|
|
1932
|
+
{
|
|
1933
|
+
ref: inputRef,
|
|
1934
|
+
type: "text",
|
|
1935
|
+
value: url,
|
|
1936
|
+
onChange: (e) => {
|
|
1937
|
+
setUrl(e.target.value);
|
|
1938
|
+
if (error) setError("");
|
|
1939
|
+
},
|
|
1940
|
+
placeholder: t("linkInput.placeholder"),
|
|
1941
|
+
"aria-invalid": Boolean(error),
|
|
1942
|
+
"aria-describedby": error ? errorId : void 0,
|
|
1943
|
+
className: "flex-1 px-3 py-2 text-sm bg-muted/50 border-0 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 aria-invalid:ring-2 aria-invalid:ring-destructive/40"
|
|
1944
|
+
}
|
|
1945
|
+
),
|
|
1946
|
+
/* @__PURE__ */ jsx4("button", { type: "submit", className: "p-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors", children: /* @__PURE__ */ jsx4(Check, { className: "w-4 h-4" }) }),
|
|
1947
|
+
/* @__PURE__ */ jsx4("button", { type: "button", onClick: onCancel, className: "p-2 rounded-lg hover:bg-muted transition-colors text-muted-foreground", children: /* @__PURE__ */ jsx4(X, { className: "w-4 h-4" }) })
|
|
1948
|
+
] }),
|
|
1949
|
+
error ? /* @__PURE__ */ jsx4("p", { id: errorId, role: "alert", className: "mt-1.5 px-1 text-xs text-destructive", children: error }) : null
|
|
1950
|
+
] });
|
|
1951
|
+
};
|
|
1952
|
+
var ImageInput = ({ onSubmit, onCancel }) => {
|
|
1953
|
+
const t = useSmartTranslations("UEditor");
|
|
1954
|
+
const [url, setUrl] = useState3("");
|
|
1955
|
+
const [alt, setAlt] = useState3("");
|
|
1956
|
+
const inputRef = useRef2(null);
|
|
1957
|
+
useEffect3(() => {
|
|
1958
|
+
inputRef.current?.focus();
|
|
1959
|
+
}, []);
|
|
1960
|
+
const handleSubmit = (e) => {
|
|
1961
|
+
e.preventDefault();
|
|
1962
|
+
const safeUrl = sanitizeUEditorUrl(url, "image");
|
|
1963
|
+
if (safeUrl) {
|
|
1964
|
+
onSubmit(safeUrl, alt);
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "p-3 space-y-3", children: [
|
|
1968
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1969
|
+
/* @__PURE__ */ jsx4("label", { className: "text-xs font-medium text-muted-foreground", children: t("imageInput.urlLabel") }),
|
|
1970
|
+
/* @__PURE__ */ jsx4(
|
|
1971
|
+
"input",
|
|
1972
|
+
{
|
|
1973
|
+
ref: inputRef,
|
|
1974
|
+
type: "text",
|
|
1975
|
+
value: url,
|
|
1976
|
+
onChange: (e) => setUrl(e.target.value),
|
|
1977
|
+
placeholder: t("imageInput.urlPlaceholder"),
|
|
1978
|
+
className: "w-full mt-1 px-3 py-2 text-sm bg-muted/50 border-0 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20"
|
|
1979
|
+
}
|
|
1980
|
+
)
|
|
1981
|
+
] }),
|
|
1982
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1983
|
+
/* @__PURE__ */ jsx4("label", { className: "text-xs font-medium text-muted-foreground", children: t("imageInput.altLabel") }),
|
|
1984
|
+
/* @__PURE__ */ jsx4(
|
|
1985
|
+
"input",
|
|
1986
|
+
{
|
|
1987
|
+
type: "text",
|
|
1988
|
+
value: alt,
|
|
1989
|
+
onChange: (e) => setAlt(e.target.value),
|
|
1990
|
+
placeholder: t("imageInput.altPlaceholder"),
|
|
1991
|
+
className: "w-full mt-1 px-3 py-2 text-sm bg-muted/50 border-0 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20"
|
|
1992
|
+
}
|
|
1993
|
+
)
|
|
1994
|
+
] }),
|
|
1995
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
|
|
1996
|
+
/* @__PURE__ */ jsx4(
|
|
1997
|
+
"button",
|
|
1998
|
+
{
|
|
1999
|
+
type: "submit",
|
|
2000
|
+
disabled: !url,
|
|
2001
|
+
className: "flex-1 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50",
|
|
2002
|
+
children: t("imageInput.addBtn")
|
|
2003
|
+
}
|
|
2004
|
+
),
|
|
2005
|
+
/* @__PURE__ */ jsx4("button", { type: "button", onClick: onCancel, className: "px-4 py-2 rounded-lg hover:bg-muted transition-colors text-muted-foreground", children: t("imageInput.cancelBtn") })
|
|
2006
|
+
] })
|
|
2007
|
+
] });
|
|
2008
|
+
};
|
|
2009
|
+
|
|
2010
|
+
// src/components/UEditor/link-commands.ts
|
|
2011
|
+
import { TextSelection } from "@tiptap/pm/state";
|
|
2012
|
+
function applyEditorLink(editor, href) {
|
|
2013
|
+
const isEditingLink = editor.isActive("link");
|
|
2014
|
+
const hasSelectedText = !editor.state.selection.empty;
|
|
2015
|
+
const chain = editor.chain().focus();
|
|
2016
|
+
if (isEditingLink) {
|
|
2017
|
+
chain.extendMarkRange("link");
|
|
2018
|
+
}
|
|
2019
|
+
chain.setLink({ href });
|
|
2020
|
+
if (!hasSelectedText && !isEditingLink) {
|
|
2021
|
+
chain.insertContent(href);
|
|
2022
|
+
}
|
|
2023
|
+
return chain.command(({ tr }) => {
|
|
2024
|
+
tr.setSelection(TextSelection.create(tr.doc, tr.selection.to));
|
|
2025
|
+
tr.removeStoredMark(editor.schema.marks.link);
|
|
2026
|
+
return true;
|
|
2027
|
+
}).run();
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
// src/components/UEditor/toolbar.tsx
|
|
2031
|
+
import React7, { useRef as useRef4, useState as useState4 } from "react";
|
|
2032
|
+
import { useEditorState } from "@tiptap/react";
|
|
2033
|
+
import {
|
|
2034
|
+
AlignCenter,
|
|
2035
|
+
AlignJustify,
|
|
2036
|
+
AlignLeft,
|
|
2037
|
+
AlignRight,
|
|
2038
|
+
ArrowDown,
|
|
2039
|
+
ArrowLeft,
|
|
2040
|
+
ArrowRight,
|
|
2041
|
+
ArrowUp,
|
|
2042
|
+
CircleCheckBig,
|
|
2043
|
+
FileCode,
|
|
2044
|
+
Heading1 as Heading1Icon,
|
|
2045
|
+
Heading2 as Heading2Icon,
|
|
2046
|
+
Heading3 as Heading3Icon,
|
|
2047
|
+
IndentDecrease,
|
|
2048
|
+
IndentIncrease,
|
|
2049
|
+
Link as LinkIcon,
|
|
2050
|
+
List as ListIcon,
|
|
2051
|
+
ListOrdered as ListOrderedIcon,
|
|
2052
|
+
ListTodo,
|
|
2053
|
+
Quote as QuoteIcon,
|
|
2054
|
+
RotateCcw,
|
|
2055
|
+
SquareCheckBig,
|
|
2056
|
+
TableCellsMerge,
|
|
2057
|
+
Trash2,
|
|
2058
|
+
Type,
|
|
2059
|
+
Upload,
|
|
2060
|
+
AlignStartVertical,
|
|
2061
|
+
AlignCenterVertical,
|
|
2062
|
+
AlignEndVertical
|
|
2063
|
+
} from "lucide-react";
|
|
2064
|
+
import { setCellAttr } from "@tiptap/pm/tables";
|
|
2065
|
+
|
|
2066
|
+
// src/components/UEditor/colors.tsx
|
|
2067
|
+
import { useMemo as useMemo2, useRef as useRef3 } from "react";
|
|
2068
|
+
import { Check as Check2, Palette, Paintbrush, Grid } from "lucide-react";
|
|
2069
|
+
|
|
2070
|
+
// src/components/UEditor/figma-toolbar-icons.tsx
|
|
2071
|
+
import React5 from "react";
|
|
2072
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
2073
|
+
function createToolbarIcon(displayName, width, height, path) {
|
|
2074
|
+
const Icon = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
|
|
2075
|
+
"svg",
|
|
2076
|
+
{
|
|
2077
|
+
ref,
|
|
2078
|
+
"aria-hidden": "true",
|
|
2079
|
+
focusable: "false",
|
|
2080
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
2081
|
+
className,
|
|
2082
|
+
fill: "currentColor",
|
|
2083
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2084
|
+
...props,
|
|
2085
|
+
children: /* @__PURE__ */ jsx5("path", { d: path })
|
|
2086
|
+
}
|
|
2087
|
+
));
|
|
2088
|
+
Icon.displayName = displayName;
|
|
2089
|
+
return Icon;
|
|
2090
|
+
}
|
|
2091
|
+
var FigmaChevronDownIcon = createToolbarIcon(
|
|
2092
|
+
"FigmaChevronDownIcon",
|
|
2093
|
+
512,
|
|
2094
|
+
512,
|
|
2095
|
+
"M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"
|
|
2096
|
+
);
|
|
2097
|
+
var FigmaTextStyleIcon = ({ className, ...props }) => /* @__PURE__ */ jsx5("svg", { "aria-hidden": "true", focusable: "false", viewBox: "0 0 24 24", className, fill: "currentColor", ...props, children: /* @__PURE__ */ jsx5("path", { d: "M4 4.25C4 3.56 4.56 3 5.25 3h13.5C19.44 3 20 3.56 20 4.25v2.5a1 1 0 1 1-2 0V5h-5v14h2a1 1 0 1 1 0 2H9a1 1 0 1 1 0-2h2V5H6v1.75a1 1 0 1 1-2 0v-2.5Z" }) });
|
|
2098
|
+
var FigmaLineHeightIcon = createToolbarIcon(
|
|
2099
|
+
"FigmaLineHeightIcon",
|
|
2100
|
+
576,
|
|
2101
|
+
512,
|
|
2102
|
+
"M64 128l0-32 64 0 0 320-32 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l128 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-32 0 0-320 64 0 0 32c0 17.7 14.3 32 32 32s32-14.3 32-32l0-48c0-26.5-21.5-48-48-48L160 32 48 32C21.5 32 0 53.5 0 80l0 48c0 17.7 14.3 32 32 32s32-14.3 32-32zM502.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-64 64c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l32 0 0 192-32 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l64 64c12.5 12.5 32.8 12.5 45.3 0l64-64c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8l-32 0 0-192 32 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-64-64z"
|
|
2103
|
+
);
|
|
2104
|
+
var FigmaLetterSpacingIcon = createToolbarIcon(
|
|
2105
|
+
"FigmaLetterSpacingIcon",
|
|
2106
|
+
448,
|
|
2107
|
+
512,
|
|
2108
|
+
"M64 128l0-32 128 0 0 128-16 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-16 0 0-128 128 0 0 32c0 17.7 14.3 32 32 32s32-14.3 32-32l0-48c0-26.5-21.5-48-48-48L224 32 48 32C21.5 32 0 53.5 0 80l0 48c0 17.7 14.3 32 32 32s32-14.3 32-32zM9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6l0-32 192 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 32-192 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64z"
|
|
2109
|
+
);
|
|
2110
|
+
var FigmaBoldIcon = createToolbarIcon(
|
|
2111
|
+
"FigmaBoldIcon",
|
|
2112
|
+
384,
|
|
2113
|
+
512,
|
|
2114
|
+
"M0 64C0 46.3 14.3 32 32 32l48 0 16 0 128 0c70.7 0 128 57.3 128 128c0 31.3-11.3 60.1-30 82.3c37.1 22.4 62 63.1 62 109.7c0 70.7-57.3 128-128 128L96 480l-16 0-48 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l16 0 0-160L48 96 32 96C14.3 96 0 81.7 0 64zM224 224c35.3 0 64-28.7 64-64s-28.7-64-64-64L112 96l0 128 112 0zM112 288l0 128 144 0c35.3 0 64-28.7 64-64s-28.7-64-64-64l-32 0-112 0z"
|
|
2115
|
+
);
|
|
2116
|
+
var FigmaItalicIcon = createToolbarIcon(
|
|
2117
|
+
"FigmaItalicIcon",
|
|
2118
|
+
384,
|
|
2119
|
+
512,
|
|
2120
|
+
"M128 64c0-17.7 14.3-32 32-32l192 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-58.7 0L160 416l64 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 480c-17.7 0-32-14.3-32-32s14.3-32 32-32l58.7 0L224 96l-64 0c-17.7 0-32-14.3-32-32z"
|
|
2121
|
+
);
|
|
2122
|
+
var FigmaUnderlineIcon = createToolbarIcon(
|
|
2123
|
+
"FigmaUnderlineIcon",
|
|
2124
|
+
448,
|
|
2125
|
+
512,
|
|
2126
|
+
"M16 64c0-17.7 14.3-32 32-32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-16 0 0 128c0 53 43 96 96 96s96-43 96-96l0-128-16 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-16 0 0 128c0 88.4-71.6 160-160 160s-160-71.6-160-160L64 96 48 96C30.3 96 16 81.7 16 64zM0 448c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 480c-17.7 0-32-14.3-32-32z"
|
|
2127
|
+
);
|
|
2128
|
+
var FigmaStrikeIcon = createToolbarIcon(
|
|
2129
|
+
"FigmaStrikeIcon",
|
|
2130
|
+
512,
|
|
2131
|
+
512,
|
|
2132
|
+
"M161.3 144c3.2-17.2 14-30.1 33.7-38.6c21.1-9 51.8-12.3 88.6-6.5c11.9 1.9 48.8 9.1 60.1 12c17.1 4.5 34.6-5.6 39.2-22.7s-5.6-34.6-22.7-39.2c-14.3-3.8-53.6-11.4-66.6-13.4c-44.7-7-88.3-4.2-123.7 10.9c-36.5 15.6-64.4 44.8-71.8 87.3c-.1 .6-.2 1.1-.2 1.7c-2.8 23.9 .5 45.6 10.1 64.6c4.5 9 10.2 16.9 16.7 23.9L32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l448 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-209.9 0-.4-.1-1.1-.3c-36-10.8-65.2-19.6-85.2-33.1c-9.3-6.3-15-12.6-18.2-19.1c-3.1-6.1-5.2-14.6-3.8-27.4zM348.9 337.2c2.7 6.5 4.4 15.8 1.9 30.1c-3 17.6-13.8 30.8-33.9 39.4c-21.1 9-51.7 12.3-88.5 6.5c-18-2.9-49.1-13.5-74.4-22.1c-5.6-1.9-11-3.7-15.9-5.4c-16.8-5.6-34.9 3.5-40.5 20.3s3.5 34.9 20.3 40.5c3.6 1.2 7.9 2.7 12.7 4.3c24.9 8.5 63.6 21.7 87.6 25.6l.2 0c44.7 7 88.3 4.2 123.7-10.9c36.5-15.6 64.4-44.8 71.8-87.3c3.6-21 2.7-40.4-3.1-58.1l-75.7 0c7 5.6 11.4 11.2 13.9 17.2z"
|
|
2133
|
+
);
|
|
2134
|
+
var FigmaCodeIcon = createToolbarIcon(
|
|
2135
|
+
"FigmaCodeIcon",
|
|
2136
|
+
640,
|
|
2137
|
+
512,
|
|
2138
|
+
"M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"
|
|
2139
|
+
);
|
|
2140
|
+
var FigmaSubscriptIcon = createToolbarIcon(
|
|
2141
|
+
"FigmaSubscriptIcon",
|
|
2142
|
+
512,
|
|
2143
|
+
512,
|
|
2144
|
+
"M32 64C14.3 64 0 78.3 0 96s14.3 32 32 32l15.3 0 89.6 128L47.3 384 32 384c-17.7 0-32 14.3-32 32s14.3 32 32 32l32 0c10.4 0 20.2-5.1 26.2-13.6L176 311.8l85.8 122.6c6 8.6 15.8 13.6 26.2 13.6l32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-15.3 0L215.1 256l89.6-128 15.3 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-32 0c-10.4 0-20.2 5.1-26.2 13.6L176 200.2 90.2 77.6C84.2 69.1 74.4 64 64 64L32 64zM480 320c0-11.1-5.7-21.4-15.2-27.2s-21.2-6.4-31.1-1.4l-32 16c-15.8 7.9-22.2 27.1-14.3 42.9C393 361.5 404.3 368 416 368l0 80c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-128z"
|
|
2145
|
+
);
|
|
2146
|
+
var FigmaSuperscriptIcon = createToolbarIcon(
|
|
2147
|
+
"FigmaSuperscriptIcon",
|
|
2148
|
+
512,
|
|
2149
|
+
512,
|
|
2150
|
+
"M480 32c0-11.1-5.7-21.4-15.2-27.2s-21.2-6.4-31.1-1.4l-32 16c-15.8 7.9-22.2 27.1-14.3 42.9C393 73.5 404.3 80 416 80l0 80c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-128zM32 64C14.3 64 0 78.3 0 96s14.3 32 32 32l15.3 0 89.6 128L47.3 384 32 384c-17.7 0-32 14.3-32 32s14.3 32 32 32l32 0c10.4 0 20.2-5.1 26.2-13.6L176 311.8l85.8 122.6c6 8.6 15.8 13.6 26.2 13.6l32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-15.3 0L215.1 256l89.6-128 15.3 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-32 0c-10.4 0-20.2 5.1-26.2 13.6L176 200.2 90.2 77.6C84.2 69.1 74.4 64 64 64L32 64z"
|
|
2151
|
+
);
|
|
2152
|
+
var FigmaLinkIcon = createToolbarIcon(
|
|
2153
|
+
"FigmaLinkIcon",
|
|
2154
|
+
640,
|
|
2155
|
+
512,
|
|
2156
|
+
"M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"
|
|
2157
|
+
);
|
|
2158
|
+
var FigmaSmileIcon = createToolbarIcon(
|
|
2159
|
+
"FigmaSmileIcon",
|
|
2160
|
+
512,
|
|
2161
|
+
512,
|
|
2162
|
+
"M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm177.6 62.1C192.8 334.5 218.8 352 256 352s63.2-17.5 78.4-33.9c9-9.7 24.2-10.4 33.9-1.4s10.4 24.2 1.4 33.9c-22 23.8-60 49.4-113.6 49.4s-91.7-25.5-113.6-49.4c-9-9.7-8.4-24.9 1.4-33.9s24.9-8.4 33.9 1.4zM144.4 208a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm192-32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"
|
|
2163
|
+
);
|
|
2164
|
+
var FigmaAlignLeftIcon = createToolbarIcon(
|
|
2165
|
+
"FigmaAlignLeftIcon",
|
|
2166
|
+
448,
|
|
2167
|
+
512,
|
|
2168
|
+
"M288 64c0 17.7-14.3 32-32 32L32 96C14.3 96 0 81.7 0 64S14.3 32 32 32l224 0c17.7 0 32 14.3 32 32zm0 256c0 17.7-14.3 32-32 32L32 352c-17.7 0-32-14.3-32-32s14.3-32 32-32l224 0c17.7 0 32 14.3 32 32zM0 192c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 224c-17.7 0-32-14.3-32-32zM448 448c0 17.7-14.3 32-32 32L32 480c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"
|
|
2169
|
+
);
|
|
2170
|
+
var FigmaListIcon = createToolbarIcon(
|
|
2171
|
+
"FigmaListIcon",
|
|
2172
|
+
512,
|
|
2173
|
+
512,
|
|
2174
|
+
"M64 144a48 48 0 1 0 0-96 48 48 0 1 0 0 96zM192 64c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L192 64zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-288 0zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-288 0zM64 464a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm48-208a48 48 0 1 0-96 0 48 48 0 1 0 96 0z"
|
|
2175
|
+
);
|
|
2176
|
+
var FigmaQuoteIcon = createToolbarIcon(
|
|
2177
|
+
"FigmaQuoteIcon",
|
|
2178
|
+
448,
|
|
2179
|
+
512,
|
|
2180
|
+
"M0 216C0 149.7 53.7 96 120 96l8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-8 0c-30.9 0-56 25.1-56 56l0 8 64 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64l-64 0c-35.3 0-64-28.7-64-64l0-136zm256 0c0-66.3 53.7-120 120-120l8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-8 0c-30.9 0-56 25.1-56 56l0 8 64 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64l-64 0c-35.3 0-64-28.7-64-64l0-136z"
|
|
2181
|
+
);
|
|
2182
|
+
var FigmaImageIcon = createToolbarIcon(
|
|
2183
|
+
"FigmaImageIcon",
|
|
2184
|
+
512,
|
|
2185
|
+
512,
|
|
2186
|
+
"M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l336 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"
|
|
2187
|
+
);
|
|
2188
|
+
var FigmaTableIcon = createToolbarIcon(
|
|
2189
|
+
"FigmaTableIcon",
|
|
2190
|
+
512,
|
|
2191
|
+
512,
|
|
2192
|
+
"M64 256l0-96 160 0 0 96L64 256zm0 64l160 0 0 96L64 416l0-96zm224 96l0-96 160 0 0 96-160 0zM448 256l-160 0 0-96 160 0 0 96zM64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32z"
|
|
2193
|
+
);
|
|
2194
|
+
var FigmaUndoIcon = createToolbarIcon(
|
|
2195
|
+
"FigmaUndoIcon",
|
|
2196
|
+
512,
|
|
2197
|
+
512,
|
|
2198
|
+
"M48.5 224L40 224c-13.3 0-24-10.7-24-24L16 72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2l41.6 41.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2S177.7 224 168 224L48.5 224z"
|
|
2199
|
+
);
|
|
2200
|
+
var FigmaRedoIcon = createToolbarIcon(
|
|
2201
|
+
"FigmaRedoIcon",
|
|
2202
|
+
512,
|
|
2203
|
+
512,
|
|
2204
|
+
"M463.5 224l8.5 0c13.3 0 24-10.7 24-24l0-128c0-9.7-5.8-18.5-14.8-22.2s-19.3-1.7-26.2 5.2l-41.6 41.6c-87.6-86.5-228.7-86.2-315.8 1c-87.5 87.5-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3c62.2-62.2 162.7-62.5 225.3-1L327 183c-6.9 6.9-8.9 17.2-5.2 26.2S334.3 224 344 224l119.5 0z"
|
|
2205
|
+
);
|
|
2206
|
+
var FigmaHighlighterIcon = createToolbarIcon(
|
|
2207
|
+
"FigmaHighlighterIcon",
|
|
2208
|
+
576,
|
|
2209
|
+
512,
|
|
2210
|
+
"M315 315l158.4-215L444.1 70.6 229 229 315 315zm-187 5l0-71.7c0-15.3 7.2-29.6 19.5-38.6L420.6 8.4C428 2.9 437 0 446.2 0c11.4 0 22.4 4.5 30.5 12.6l54.8 54.8c8.1 8.1 12.6 19 12.6 30.5c0 9.2-2.9 18.2-8.4 25.6L334.4 396.5c-9 12.3-23.4 19.5-38.6 19.5L224 416l-25.4 25.4c-12.5 12.5-32.8 12.5-45.3 0l-50.7-50.7c-12.5-12.5-12.5-32.8 0-45.3L128 320zM7 466.3l63-63 70.6 70.6-31 31c-4.5 4.5-10.6 7-17 7L24 512c-13.3 0-24-10.7-24-24l0-4.7c0-6.4 2.5-12.5 7-17z"
|
|
2211
|
+
);
|
|
2212
|
+
|
|
2213
|
+
// src/components/UEditor/colors.tsx
|
|
2214
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2215
|
+
var TextColorIcon = ({ color }) => {
|
|
2216
|
+
const underlineColor = color && color !== "inherit" ? color : "currentColor";
|
|
2217
|
+
return /* @__PURE__ */ jsxs4("span", { className: "relative flex h-5 w-5 items-center justify-center leading-none", children: [
|
|
2218
|
+
/* @__PURE__ */ jsx6("span", { className: "text-[15px] font-semibold leading-none", children: "A" }),
|
|
2219
|
+
/* @__PURE__ */ jsx6(
|
|
2220
|
+
"span",
|
|
2221
|
+
{
|
|
2222
|
+
"aria-hidden": "true",
|
|
2223
|
+
className: "absolute bottom-0 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full",
|
|
2224
|
+
style: { backgroundColor: underlineColor }
|
|
2225
|
+
}
|
|
2226
|
+
)
|
|
2227
|
+
] });
|
|
2228
|
+
};
|
|
2229
|
+
var HighlightColorIcon = ({ color }) => {
|
|
2230
|
+
const underlineColor = color || "currentColor";
|
|
2231
|
+
return /* @__PURE__ */ jsxs4("span", { className: "relative flex h-5 w-5 items-center justify-center leading-none", children: [
|
|
2232
|
+
/* @__PURE__ */ jsx6(FigmaHighlighterIcon, { className: "h-4 w-4" }),
|
|
2233
|
+
/* @__PURE__ */ jsx6(
|
|
2234
|
+
"span",
|
|
2235
|
+
{
|
|
2236
|
+
"aria-hidden": "true",
|
|
2237
|
+
className: "absolute bottom-0 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full",
|
|
2238
|
+
style: { backgroundColor: underlineColor }
|
|
2239
|
+
}
|
|
2240
|
+
)
|
|
2241
|
+
] });
|
|
2242
|
+
};
|
|
2243
|
+
var CellBgColorIcon = ({ color }) => {
|
|
2244
|
+
const underlineColor = color && color !== "inherit" ? color : "currentColor";
|
|
2245
|
+
return /* @__PURE__ */ jsxs4("span", { className: "relative flex h-5 w-5 items-center justify-center leading-none", children: [
|
|
2246
|
+
/* @__PURE__ */ jsx6(Paintbrush, { className: "h-4 w-4" }),
|
|
2247
|
+
/* @__PURE__ */ jsx6(
|
|
2248
|
+
"span",
|
|
2249
|
+
{
|
|
2250
|
+
"aria-hidden": "true",
|
|
2251
|
+
className: "absolute bottom-0 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full",
|
|
2252
|
+
style: { backgroundColor: underlineColor }
|
|
2253
|
+
}
|
|
2254
|
+
)
|
|
2255
|
+
] });
|
|
2256
|
+
};
|
|
2257
|
+
var CellBorderIcon = ({ color }) => {
|
|
2258
|
+
const underlineColor = color && color !== "inherit" ? color : "currentColor";
|
|
2259
|
+
return /* @__PURE__ */ jsxs4("span", { className: "relative flex h-5 w-5 items-center justify-center leading-none", children: [
|
|
2260
|
+
/* @__PURE__ */ jsx6(Grid, { className: "h-4 w-4" }),
|
|
2261
|
+
/* @__PURE__ */ jsx6(
|
|
2262
|
+
"span",
|
|
2263
|
+
{
|
|
2264
|
+
"aria-hidden": "true",
|
|
2265
|
+
className: "absolute bottom-0 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full",
|
|
2266
|
+
style: { backgroundColor: underlineColor }
|
|
2267
|
+
}
|
|
2268
|
+
)
|
|
2269
|
+
] });
|
|
2270
|
+
};
|
|
2271
|
+
var EDITOR_COLOR_SWATCHES = [
|
|
2272
|
+
"#000000",
|
|
2273
|
+
"#3f3f46",
|
|
2274
|
+
"#713f12",
|
|
2275
|
+
"#14532d",
|
|
2276
|
+
"#164e63",
|
|
2277
|
+
"#1e3a8a",
|
|
2278
|
+
"#3730a3",
|
|
2279
|
+
"#404040",
|
|
2280
|
+
"#b91c1c",
|
|
2281
|
+
"#c2410c",
|
|
2282
|
+
"#a16207",
|
|
2283
|
+
"#15803d",
|
|
2284
|
+
"#0f766e",
|
|
2285
|
+
"#2563eb",
|
|
2286
|
+
"#4f46e5",
|
|
2287
|
+
"#737373",
|
|
2288
|
+
"#ef4444",
|
|
2289
|
+
"#f97316",
|
|
2290
|
+
"#eab308",
|
|
2291
|
+
"#22c55e",
|
|
2292
|
+
"#14b8a6",
|
|
2293
|
+
"#3b82f6",
|
|
2294
|
+
"#7c3aed",
|
|
2295
|
+
"#a3a3a3",
|
|
2296
|
+
"#f43f5e",
|
|
2297
|
+
"#f59e0b",
|
|
2298
|
+
"#facc15",
|
|
2299
|
+
"#00e676",
|
|
2300
|
+
"#22d3ee",
|
|
2301
|
+
"#06b6d4",
|
|
2302
|
+
"#be185d",
|
|
2303
|
+
"#bdbdbd",
|
|
2304
|
+
"#f9a8d4",
|
|
2305
|
+
"#fecaca",
|
|
2306
|
+
"#fde68a",
|
|
2307
|
+
"#bbf7d0",
|
|
2308
|
+
"#a7f3d0",
|
|
2309
|
+
"#bae6fd",
|
|
2310
|
+
"#c4b5fd",
|
|
2311
|
+
"#f5f5f5"
|
|
2312
|
+
];
|
|
2313
|
+
var HIGHLIGHT_COLOR_SWATCHES = [
|
|
2314
|
+
"#fef08a",
|
|
2315
|
+
"#fde68a",
|
|
2316
|
+
"#fed7aa",
|
|
2317
|
+
"#fecaca",
|
|
2318
|
+
"#fbcfe8",
|
|
2319
|
+
"#e9d5ff",
|
|
2320
|
+
"#c7d2fe",
|
|
2321
|
+
"#bfdbfe",
|
|
2322
|
+
"#bae6fd",
|
|
2323
|
+
"#ccfbf1",
|
|
2324
|
+
"#bbf7d0",
|
|
2325
|
+
"#d9f99d",
|
|
2326
|
+
"#e5e7eb",
|
|
2327
|
+
"#fca5a5",
|
|
2328
|
+
"#fdba74",
|
|
2329
|
+
"#facc15",
|
|
2330
|
+
"#86efac",
|
|
2331
|
+
"#5eead4",
|
|
2332
|
+
"#7dd3fc",
|
|
2333
|
+
"#a5b4fc",
|
|
2334
|
+
"#d8b4fe",
|
|
2335
|
+
"#f0abfc",
|
|
2336
|
+
"#f9a8d4",
|
|
2337
|
+
"#d4d4d4"
|
|
2338
|
+
];
|
|
2339
|
+
function buildColorOptions(colors, prefix) {
|
|
2340
|
+
return colors.map((color, index) => ({ name: `${prefix} ${index + 1}`, color }));
|
|
2341
|
+
}
|
|
2342
|
+
function getSwatchCheckClass(color) {
|
|
2343
|
+
return /^#(?:fff|ffffff)$/i.test(color) ? "text-foreground" : "text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]";
|
|
2344
|
+
}
|
|
2345
|
+
var useEditorColors = () => {
|
|
2346
|
+
const t = useSmartTranslations("UEditor");
|
|
2347
|
+
const textColors = useMemo2(
|
|
2348
|
+
() => [
|
|
2349
|
+
{ name: t("colors.default"), color: "inherit", cssClass: "text-foreground" },
|
|
2350
|
+
{ name: t("colors.muted"), color: "var(--muted-foreground)", cssClass: "text-muted-foreground" },
|
|
2351
|
+
{ name: t("colors.primary"), color: "var(--primary)", cssClass: "text-primary" },
|
|
2352
|
+
{ name: t("colors.secondary"), color: "var(--secondary)", cssClass: "text-secondary" },
|
|
2353
|
+
{ name: t("colors.success"), color: "var(--success)", cssClass: "text-success" },
|
|
2354
|
+
{ name: t("colors.warning"), color: "var(--warning)", cssClass: "text-warning" },
|
|
2355
|
+
{ name: t("colors.destructive"), color: "var(--destructive)", cssClass: "text-destructive" },
|
|
2356
|
+
{ name: t("colors.info"), color: "var(--info)", cssClass: "text-info" },
|
|
2357
|
+
...buildColorOptions(EDITOR_COLOR_SWATCHES, t("colors.color"))
|
|
2358
|
+
],
|
|
2359
|
+
[t]
|
|
2360
|
+
);
|
|
2361
|
+
const highlightColors = useMemo2(
|
|
2362
|
+
() => [
|
|
2363
|
+
{ name: t("colors.default"), color: "", cssClass: "" },
|
|
2364
|
+
{ name: t("colors.muted"), color: "var(--muted)", cssClass: "bg-muted" },
|
|
2365
|
+
{ name: t("colors.primary"), color: "color-mix(in oklch, var(--primary) 20%, transparent)", cssClass: "bg-primary/20" },
|
|
2366
|
+
{ name: t("colors.secondary"), color: "color-mix(in oklch, var(--secondary) 20%, transparent)", cssClass: "bg-secondary/20" },
|
|
2367
|
+
{ name: t("colors.success"), color: "color-mix(in oklch, var(--success) 20%, transparent)", cssClass: "bg-success/20" },
|
|
2368
|
+
{ name: t("colors.warning"), color: "color-mix(in oklch, var(--warning) 20%, transparent)", cssClass: "bg-warning/20" },
|
|
2369
|
+
{ name: t("colors.destructive"), color: "color-mix(in oklch, var(--destructive) 20%, transparent)", cssClass: "bg-destructive/20" },
|
|
2370
|
+
{ name: t("colors.info"), color: "color-mix(in oklch, var(--info) 20%, transparent)", cssClass: "bg-info/20" },
|
|
2371
|
+
{ name: t("colors.accent"), color: "var(--accent)", cssClass: "bg-accent" },
|
|
2372
|
+
...buildColorOptions(HIGHLIGHT_COLOR_SWATCHES, t("colors.color"))
|
|
2373
|
+
],
|
|
2374
|
+
[t]
|
|
2375
|
+
);
|
|
2376
|
+
return { textColors, highlightColors };
|
|
2377
|
+
};
|
|
2378
|
+
var EditorColorPalette = ({
|
|
2379
|
+
colors,
|
|
2380
|
+
currentColor,
|
|
2381
|
+
onSelect,
|
|
2382
|
+
label
|
|
2383
|
+
}) => {
|
|
2384
|
+
const t = useSmartTranslations("UEditor");
|
|
2385
|
+
const colorInputRef = useRef3(null);
|
|
2386
|
+
const automaticColor = colors[0]?.color ?? "";
|
|
2387
|
+
const paletteColors = colors.slice(1);
|
|
2388
|
+
return /* @__PURE__ */ jsxs4("div", { className: "w-56 p-2", children: [
|
|
2389
|
+
/* @__PURE__ */ jsx6("span", { className: "px-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: label }),
|
|
2390
|
+
/* @__PURE__ */ jsxs4(
|
|
2391
|
+
"button",
|
|
2392
|
+
{
|
|
2393
|
+
type: "button",
|
|
2394
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2395
|
+
onClick: () => onSelect(automaticColor),
|
|
2396
|
+
className: cn(
|
|
2397
|
+
"mt-2 flex h-9 w-full items-center gap-3 rounded-md border px-2 text-sm transition-colors",
|
|
2398
|
+
"bg-muted/50 hover:bg-muted",
|
|
2399
|
+
currentColor === automaticColor ? "border-primary text-primary" : "border-transparent text-foreground"
|
|
2400
|
+
),
|
|
2401
|
+
children: [
|
|
2402
|
+
/* @__PURE__ */ jsx6("span", { className: "flex h-5 w-5 items-center justify-center rounded border border-border bg-background", children: currentColor === automaticColor && /* @__PURE__ */ jsx6(Check2, { className: "h-3.5 w-3.5" }) }),
|
|
2403
|
+
/* @__PURE__ */ jsx6("span", { className: "flex-1 text-center", children: t("colors.automatic") })
|
|
2404
|
+
]
|
|
2405
|
+
}
|
|
2406
|
+
),
|
|
2407
|
+
/* @__PURE__ */ jsx6("div", { className: "mt-2 grid grid-cols-8 gap-1", children: paletteColors.map((c) => /* @__PURE__ */ jsx6(Tooltip, { placement: "top", content: /* @__PURE__ */ jsx6("span", { className: "text-xs font-medium", children: c.name }), children: /* @__PURE__ */ jsx6(
|
|
2408
|
+
"button",
|
|
2409
|
+
{
|
|
2410
|
+
type: "button",
|
|
2411
|
+
"aria-label": c.name,
|
|
2412
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2413
|
+
onClick: () => onSelect(c.color),
|
|
2414
|
+
className: cn(
|
|
2415
|
+
"relative h-5 w-5 rounded-[3px] border transition-transform hover:scale-110",
|
|
2416
|
+
currentColor === c.color ? "border-primary ring-2 ring-primary/25" : "border-border/70"
|
|
2417
|
+
),
|
|
2418
|
+
style: { backgroundColor: c.color || "transparent" },
|
|
2419
|
+
children: currentColor === c.color && /* @__PURE__ */ jsx6("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx6(Check2, { className: cn("h-3.5 w-3.5", getSwatchCheckClass(c.color)) }) })
|
|
2420
|
+
}
|
|
2421
|
+
) }, `${c.name}-${c.color}`)) }),
|
|
2422
|
+
/* @__PURE__ */ jsxs4(
|
|
2423
|
+
"button",
|
|
2424
|
+
{
|
|
2425
|
+
type: "button",
|
|
2426
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2427
|
+
onClick: () => colorInputRef.current?.click(),
|
|
2428
|
+
className: "mt-3 flex h-9 w-full items-center gap-3 rounded-md px-2 text-sm text-foreground transition-colors hover:bg-muted",
|
|
2429
|
+
children: [
|
|
2430
|
+
/* @__PURE__ */ jsx6(
|
|
2431
|
+
"span",
|
|
2432
|
+
{
|
|
2433
|
+
"aria-hidden": "true",
|
|
2434
|
+
className: "h-5 w-5 rounded border border-border",
|
|
2435
|
+
style: {
|
|
2436
|
+
background: "linear-gradient(135deg, #ff004c 0%, #fffb00 22%, #00ff66 42%, #00d5ff 62%, #2446ff 78%, #ff00d4 100%)"
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
),
|
|
2440
|
+
/* @__PURE__ */ jsx6("span", { className: "flex-1 text-center", children: t("colors.moreColors") }),
|
|
2441
|
+
/* @__PURE__ */ jsx6(Palette, { className: "h-4 w-4 text-muted-foreground" }),
|
|
2442
|
+
/* @__PURE__ */ jsx6(
|
|
2443
|
+
"input",
|
|
2444
|
+
{
|
|
2445
|
+
ref: colorInputRef,
|
|
2446
|
+
type: "color",
|
|
2447
|
+
value: currentColor.startsWith("#") ? currentColor : "#000000",
|
|
2448
|
+
onChange: (event) => onSelect(event.target.value),
|
|
2449
|
+
className: "sr-only",
|
|
2450
|
+
tabIndex: -1
|
|
2451
|
+
}
|
|
2452
|
+
)
|
|
2453
|
+
]
|
|
2454
|
+
}
|
|
2455
|
+
)
|
|
2456
|
+
] });
|
|
2457
|
+
};
|
|
2458
|
+
|
|
2459
|
+
// src/components/UEditor/image-commands.ts
|
|
2460
|
+
import { NodeSelection, TextSelection as TextSelection2 } from "@tiptap/pm/state";
|
|
2461
|
+
var IMAGE_WIDTHS_BY_LAYOUT = {
|
|
2462
|
+
block: {
|
|
2463
|
+
sm: 180,
|
|
2464
|
+
md: 280,
|
|
2465
|
+
lg: 380
|
|
2466
|
+
},
|
|
2467
|
+
wrap: {
|
|
2468
|
+
sm: 140,
|
|
2469
|
+
md: 200,
|
|
2470
|
+
lg: 260
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
function isSelectedImage(editor) {
|
|
2474
|
+
const { selection } = editor.state;
|
|
2475
|
+
return selection instanceof NodeSelection && selection.node.type.name === "image";
|
|
2476
|
+
}
|
|
2477
|
+
function toPositiveNumber(value) {
|
|
2478
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
|
|
2479
|
+
if (typeof value === "string") {
|
|
2480
|
+
const parsed = Number.parseInt(value, 10);
|
|
2481
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
2482
|
+
}
|
|
2483
|
+
return null;
|
|
2484
|
+
}
|
|
2485
|
+
function getImageElementAtSelection(editor, pos) {
|
|
2486
|
+
const nodeDom = editor.view.nodeDOM(pos);
|
|
2487
|
+
if (!(nodeDom instanceof HTMLElement)) return null;
|
|
2488
|
+
if (nodeDom.tagName === "IMG") return nodeDom;
|
|
2489
|
+
return nodeDom.querySelector("img");
|
|
2490
|
+
}
|
|
2491
|
+
function getImageAspectRatio(editor, attrs, pos) {
|
|
2492
|
+
const widthAttr = toPositiveNumber(attrs.width);
|
|
2493
|
+
const heightAttr = toPositiveNumber(attrs.height);
|
|
2494
|
+
if (widthAttr && heightAttr) return widthAttr / heightAttr;
|
|
2495
|
+
const imageElement = typeof pos === "number" ? getImageElementAtSelection(editor, pos) : null;
|
|
2496
|
+
if (!imageElement) return null;
|
|
2497
|
+
if (imageElement.naturalWidth > 0 && imageElement.naturalHeight > 0) {
|
|
2498
|
+
return imageElement.naturalWidth / imageElement.naturalHeight;
|
|
2499
|
+
}
|
|
2500
|
+
const rect = imageElement.getBoundingClientRect();
|
|
2501
|
+
if (rect.width > 0 && rect.height > 0) return rect.width / rect.height;
|
|
2502
|
+
const width = toPositiveNumber(imageElement.getAttribute("width")) ?? toPositiveNumber(imageElement.style.width);
|
|
2503
|
+
const height = toPositiveNumber(imageElement.getAttribute("height")) ?? toPositiveNumber(imageElement.style.height);
|
|
2504
|
+
return width && height ? width / height : null;
|
|
2505
|
+
}
|
|
2506
|
+
function getImagePresetAttributes(editor, width, preset, attrs, pos) {
|
|
2507
|
+
const aspect = getImageAspectRatio(editor, attrs, pos);
|
|
2508
|
+
return {
|
|
2509
|
+
width,
|
|
2510
|
+
height: aspect ? Math.round(width / aspect) : toPositiveNumber(attrs.height),
|
|
2511
|
+
imageWidthPreset: preset
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
function applyImageLayout(editor, layout) {
|
|
2515
|
+
const { state, view } = editor;
|
|
2516
|
+
const { selection, schema } = state;
|
|
2517
|
+
if (!(selection instanceof NodeSelection) || selection.node.type.name !== "image") {
|
|
2518
|
+
editor.chain().focus().updateAttributes("image", { imageLayout: layout }).run();
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
let transaction = state.tr.setNodeMarkup(selection.from, void 0, {
|
|
2522
|
+
...selection.node.attrs,
|
|
2523
|
+
imageLayout: layout
|
|
2524
|
+
});
|
|
2525
|
+
if (layout !== "block") {
|
|
2526
|
+
const nextPos = transaction.mapping.map(selection.to);
|
|
2527
|
+
const nextNode = transaction.doc.nodeAt(nextPos);
|
|
2528
|
+
if (!nextNode || nextNode.type.name !== "paragraph") {
|
|
2529
|
+
const paragraph = schema.nodes.paragraph?.create();
|
|
2530
|
+
if (paragraph) {
|
|
2531
|
+
transaction = transaction.insert(nextPos, paragraph);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
const resolvedPos = transaction.doc.resolve(Math.min(nextPos + 1, transaction.doc.content.size));
|
|
2535
|
+
transaction = transaction.setSelection(TextSelection2.near(resolvedPos));
|
|
2536
|
+
} else {
|
|
2537
|
+
const resolvedPos = transaction.doc.resolve(selection.from);
|
|
2538
|
+
transaction = transaction.setSelection(NodeSelection.create(transaction.doc, resolvedPos.pos));
|
|
2539
|
+
}
|
|
2540
|
+
view.dispatch(transaction.scrollIntoView());
|
|
2541
|
+
view.focus();
|
|
2542
|
+
}
|
|
2543
|
+
function applyImageWidthPreset(editor, preset) {
|
|
2544
|
+
const attrs = editor.getAttributes("image");
|
|
2545
|
+
const mode = attrs.imageLayout === "left" || attrs.imageLayout === "right" ? "wrap" : "block";
|
|
2546
|
+
const width = IMAGE_WIDTHS_BY_LAYOUT[mode][preset];
|
|
2547
|
+
if (!isSelectedImage(editor)) {
|
|
2548
|
+
editor.chain().focus().updateAttributes("image", getImagePresetAttributes(editor, width, preset, attrs)).run();
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
const { state, view } = editor;
|
|
2552
|
+
const selection = state.selection;
|
|
2553
|
+
const nextAttrs = getImagePresetAttributes(editor, width, preset, selection.node.attrs, selection.from);
|
|
2554
|
+
const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
|
|
2555
|
+
...selection.node.attrs,
|
|
2556
|
+
...nextAttrs
|
|
2557
|
+
});
|
|
2558
|
+
view.dispatch(transaction.scrollIntoView());
|
|
2559
|
+
view.focus();
|
|
2560
|
+
}
|
|
2561
|
+
function resetImageSize(editor) {
|
|
2562
|
+
if (!isSelectedImage(editor)) {
|
|
2563
|
+
editor.chain().focus().updateAttributes("image", {
|
|
2564
|
+
width: null,
|
|
2565
|
+
height: null,
|
|
2566
|
+
imageWidthPreset: null
|
|
2567
|
+
}).run();
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
const { state, view } = editor;
|
|
2571
|
+
const selection = state.selection;
|
|
2572
|
+
const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
|
|
2573
|
+
...selection.node.attrs,
|
|
2574
|
+
width: null,
|
|
2575
|
+
height: null,
|
|
2576
|
+
imageWidthPreset: null
|
|
2577
|
+
});
|
|
2578
|
+
view.dispatch(transaction.scrollIntoView());
|
|
2579
|
+
view.focus();
|
|
2580
|
+
}
|
|
2581
|
+
function deleteSelectedImage(editor) {
|
|
2582
|
+
if (!isSelectedImage(editor)) return;
|
|
2583
|
+
editor.chain().focus().deleteSelection().run();
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
// src/components/UEditor/table-dom-utils.ts
|
|
2587
|
+
var DEFAULT_TABLE_ROW_HEIGHT = 25;
|
|
2588
|
+
var MIN_TABLE_ROW_HEIGHT = DEFAULT_TABLE_ROW_HEIGHT;
|
|
2589
|
+
var COLUMN_RESIZE_LINE_THICKNESS = 2;
|
|
2590
|
+
var ROW_RESIZE_LINE_THICKNESS = 2;
|
|
2591
|
+
var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
|
|
2592
|
+
var TABLE_RESIZE_HIT_ZONE = 10;
|
|
2593
|
+
function findTableRowNodeInfo(view, rowElement) {
|
|
2594
|
+
const firstCell = rowElement.querySelector("th,td");
|
|
2595
|
+
if (!firstCell) return null;
|
|
2596
|
+
const cellPos = view.posAtDOM(firstCell, 0);
|
|
2597
|
+
const $pos = view.state.doc.resolve(cellPos);
|
|
2598
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
2599
|
+
const node = $pos.node(depth);
|
|
2600
|
+
if (node.type.name === "tableRow") {
|
|
2601
|
+
return {
|
|
2602
|
+
pos: $pos.before(depth),
|
|
2603
|
+
node
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
return null;
|
|
2608
|
+
}
|
|
2609
|
+
function resolveEventElement(target) {
|
|
2610
|
+
if (target instanceof Element) return target;
|
|
2611
|
+
if (target instanceof Node) return target.parentElement;
|
|
2612
|
+
return null;
|
|
2613
|
+
}
|
|
2614
|
+
function getSelectionTableCell(view) {
|
|
2615
|
+
const browserSelection = window.getSelection();
|
|
2616
|
+
const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
|
|
2617
|
+
const anchorCell = anchorElement?.closest?.("th,td");
|
|
2618
|
+
if (anchorCell instanceof HTMLElement) {
|
|
2619
|
+
return anchorCell;
|
|
2620
|
+
}
|
|
2621
|
+
const { from } = view.state.selection;
|
|
2622
|
+
const domAtPos = view.domAtPos(from);
|
|
2623
|
+
const element = resolveEventElement(domAtPos.node);
|
|
2624
|
+
const cell = element?.closest?.("th,td");
|
|
2625
|
+
return cell instanceof HTMLElement ? cell : null;
|
|
2626
|
+
}
|
|
2627
|
+
function isRowResizeHotspot(cell, clientX, clientY) {
|
|
2628
|
+
const rect = cell.getBoundingClientRect();
|
|
2629
|
+
const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
|
|
2630
|
+
const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
|
|
2631
|
+
return nearBottom && !nearRight;
|
|
2632
|
+
}
|
|
2633
|
+
function isColumnResizeHotspot(cell, clientX, clientY) {
|
|
2634
|
+
const rect = cell.getBoundingClientRect();
|
|
2635
|
+
const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
|
|
2636
|
+
const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
|
|
2637
|
+
return nearRight && !nearBottom;
|
|
2638
|
+
}
|
|
2639
|
+
function getRelativeBoundaryMetrics(surface, table, row, cell) {
|
|
2640
|
+
const surfaceRect = surface.getBoundingClientRect();
|
|
2641
|
+
const tableRect = table.getBoundingClientRect();
|
|
2642
|
+
const rowRect = row.getBoundingClientRect();
|
|
2643
|
+
const cellRect = cell.getBoundingClientRect();
|
|
2644
|
+
return {
|
|
2645
|
+
left: tableRect.left - surfaceRect.left + surface.scrollLeft,
|
|
2646
|
+
top: tableRect.top - surfaceRect.top + surface.scrollTop,
|
|
2647
|
+
width: tableRect.width,
|
|
2648
|
+
height: tableRect.height,
|
|
2649
|
+
rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
|
|
2650
|
+
columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
function getRelativeCellMetrics(surface, cell) {
|
|
2654
|
+
const surfaceRect = surface.getBoundingClientRect();
|
|
2655
|
+
const cellRect = cell.getBoundingClientRect();
|
|
2656
|
+
return {
|
|
2657
|
+
left: cellRect.left - surfaceRect.left + surface.scrollLeft,
|
|
2658
|
+
top: cellRect.top - surfaceRect.top + surface.scrollTop,
|
|
2659
|
+
width: cellRect.width,
|
|
2660
|
+
height: cellRect.height
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
function getRelativeSelectedCellsMetrics(surface) {
|
|
2664
|
+
const selectedCells = Array.from(
|
|
2665
|
+
surface.querySelectorAll("td.selectedCell, th.selectedCell")
|
|
2666
|
+
);
|
|
2667
|
+
if (selectedCells.length === 0) {
|
|
2668
|
+
return null;
|
|
2669
|
+
}
|
|
2670
|
+
const surfaceRect = surface.getBoundingClientRect();
|
|
2671
|
+
let left = Number.POSITIVE_INFINITY;
|
|
2672
|
+
let top = Number.POSITIVE_INFINITY;
|
|
2673
|
+
let right = Number.NEGATIVE_INFINITY;
|
|
2674
|
+
let bottom = Number.NEGATIVE_INFINITY;
|
|
2675
|
+
selectedCells.forEach((cell) => {
|
|
2676
|
+
const rect = cell.getBoundingClientRect();
|
|
2677
|
+
left = Math.min(left, rect.left);
|
|
2678
|
+
top = Math.min(top, rect.top);
|
|
2679
|
+
right = Math.max(right, rect.right);
|
|
2680
|
+
bottom = Math.max(bottom, rect.bottom);
|
|
2681
|
+
});
|
|
2682
|
+
return {
|
|
2683
|
+
left: left - surfaceRect.left + surface.scrollLeft,
|
|
2684
|
+
top: top - surfaceRect.top + surface.scrollTop,
|
|
2685
|
+
width: right - left,
|
|
2686
|
+
height: bottom - top
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
// src/components/UEditor/table-align-utils.ts
|
|
2691
|
+
function findTableNodeInfoAtResolvedPos($pos) {
|
|
2692
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
2693
|
+
const node = $pos.node(depth);
|
|
2694
|
+
if (node.type.name === "table") {
|
|
2695
|
+
return {
|
|
2696
|
+
depth,
|
|
2697
|
+
pos: $pos.before(depth),
|
|
2698
|
+
node
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
return null;
|
|
2703
|
+
}
|
|
2704
|
+
function findTableNodeInfoFromState(state, anchorPos) {
|
|
2705
|
+
if (typeof anchorPos === "number" && Number.isFinite(anchorPos)) {
|
|
2706
|
+
const safePos = Math.max(0, Math.min(anchorPos, state.doc.content.size));
|
|
2707
|
+
return findTableNodeInfoAtResolvedPos(state.doc.resolve(safePos));
|
|
2708
|
+
}
|
|
2709
|
+
return findTableNodeInfoAtResolvedPos(state.selection.$from);
|
|
2710
|
+
}
|
|
2711
|
+
function applyTableAlignment(editor, tableAlign, anchorPos) {
|
|
2712
|
+
const tableInfo = findTableNodeInfoFromState(editor.state, anchorPos);
|
|
2713
|
+
if (!tableInfo) return false;
|
|
2714
|
+
editor.view.dispatch(
|
|
2715
|
+
editor.state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
|
|
2716
|
+
...tableInfo.node.attrs,
|
|
2717
|
+
textAlign: tableAlign
|
|
2718
|
+
})
|
|
2719
|
+
);
|
|
2720
|
+
const domAtTable = editor.view.domAtPos(Math.min(tableInfo.pos + 1, editor.state.doc.content.size)).node;
|
|
2721
|
+
const domAtTableElement = resolveEventElement(domAtTable);
|
|
2722
|
+
const tableDom = editor.view.nodeDOM(tableInfo.pos);
|
|
2723
|
+
const tableElement = domAtTableElement?.closest?.("table") ?? (tableDom instanceof HTMLTableElement ? tableDom : tableDom instanceof HTMLElement ? tableDom.querySelector("table") : null) ?? (editor.view.dom.querySelectorAll("table").length === 1 ? editor.view.dom.querySelector("table") : null);
|
|
2724
|
+
if (tableElement instanceof HTMLTableElement) {
|
|
2725
|
+
if (tableAlign) {
|
|
2726
|
+
tableElement.setAttribute("data-table-align", tableAlign);
|
|
2727
|
+
tableElement.style.width = "max-content";
|
|
2728
|
+
tableElement.style.maxWidth = "100%";
|
|
2729
|
+
tableElement.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
|
|
2730
|
+
tableElement.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
|
|
2731
|
+
} else {
|
|
2732
|
+
tableElement.removeAttribute("data-table-align");
|
|
2733
|
+
tableElement.style.removeProperty("width");
|
|
2734
|
+
tableElement.style.removeProperty("max-width");
|
|
2735
|
+
tableElement.style.removeProperty("margin-left");
|
|
2736
|
+
tableElement.style.removeProperty("margin-right");
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
return true;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
// src/components/UEditor/table-cell-commands.ts
|
|
2743
|
+
import { TextSelection as TextSelection3 } from "@tiptap/pm/state";
|
|
2744
|
+
import { selectedRect, TableMap } from "@tiptap/pm/tables";
|
|
2745
|
+
function getCellSelectionPositions(selection) {
|
|
2746
|
+
const value = selection;
|
|
2747
|
+
const anchor = value.$anchorCell?.pos;
|
|
2748
|
+
const head = value.$headCell?.pos;
|
|
2749
|
+
return typeof anchor === "number" && typeof head === "number" ? { anchor, head } : null;
|
|
2750
|
+
}
|
|
2751
|
+
function findTableInfoFromCellPos(editor, cellPos) {
|
|
2752
|
+
const $pos = editor.state.doc.resolve(cellPos);
|
|
2753
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
2754
|
+
const node = $pos.node(depth);
|
|
2755
|
+
if (node.type.name === "table") {
|
|
2756
|
+
return {
|
|
2757
|
+
table: node,
|
|
2758
|
+
tablePos: $pos.before(depth),
|
|
2759
|
+
tableStart: $pos.start(depth)
|
|
2760
|
+
};
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
return null;
|
|
2764
|
+
}
|
|
2765
|
+
function getFocusableCellPos(editor, cellPos) {
|
|
2766
|
+
const cellNode = editor.state.doc.nodeAt(cellPos);
|
|
2767
|
+
if (!cellNode) return cellPos + 1;
|
|
2768
|
+
let offset = cellPos + 1;
|
|
2769
|
+
let node = cellNode.firstChild ?? null;
|
|
2770
|
+
while (node && !node.isTextblock) {
|
|
2771
|
+
offset += 1;
|
|
2772
|
+
node = node.firstChild ?? null;
|
|
2773
|
+
}
|
|
2774
|
+
return node?.isTextblock ? offset + 1 : cellPos + 1;
|
|
2775
|
+
}
|
|
2776
|
+
function focusCell(editor, cellPos) {
|
|
2777
|
+
const selection = TextSelection3.near(editor.state.doc.resolve(getFocusableCellPos(editor, cellPos)));
|
|
2778
|
+
editor.view.dispatch(editor.state.tr.setSelection(selection));
|
|
2779
|
+
editor.view.focus();
|
|
2780
|
+
}
|
|
2781
|
+
function collectChildren(node) {
|
|
2782
|
+
const children = [];
|
|
2783
|
+
node.forEach((child) => children.push(child));
|
|
2784
|
+
return children;
|
|
2785
|
+
}
|
|
2786
|
+
function createEmptyCellNode(cellNode) {
|
|
2787
|
+
return cellNode.type.createAndFill(cellNode.attrs) ?? cellNode;
|
|
2788
|
+
}
|
|
2789
|
+
function createCellCopyForColumnDuplicate(cellNode) {
|
|
2790
|
+
return cellNode.type.create(cellNode.attrs, cellNode.content);
|
|
2791
|
+
}
|
|
2792
|
+
function createCellWithDuplicatedLogicalColumn(cellNode, widthIndex) {
|
|
2793
|
+
const colspan = Math.max(1, Number(cellNode.attrs.colspan) || 1);
|
|
2794
|
+
let nextColwidth = null;
|
|
2795
|
+
if (Array.isArray(cellNode.attrs.colwidth)) {
|
|
2796
|
+
nextColwidth = [...cellNode.attrs.colwidth];
|
|
2797
|
+
const duplicateWidth = nextColwidth[widthIndex];
|
|
2798
|
+
nextColwidth.splice(widthIndex + 1, 0, typeof duplicateWidth === "number" ? duplicateWidth : 0);
|
|
2799
|
+
}
|
|
2800
|
+
return cellNode.type.create({
|
|
2801
|
+
...cellNode.attrs,
|
|
2802
|
+
colspan: colspan + 1,
|
|
2803
|
+
...nextColwidth ? { colwidth: nextColwidth } : null
|
|
2804
|
+
}, cellNode.content);
|
|
2805
|
+
}
|
|
2806
|
+
function getTableRows(tableNode) {
|
|
2807
|
+
const rows = [];
|
|
2808
|
+
tableNode.forEach((rowNode, rowOffset) => {
|
|
2809
|
+
const cells = [];
|
|
2810
|
+
rowNode.forEach((cellNode, cellOffset, index) => {
|
|
2811
|
+
cells.push({
|
|
2812
|
+
index,
|
|
2813
|
+
node: cellNode,
|
|
2814
|
+
relativePos: rowOffset + 1 + cellOffset
|
|
2815
|
+
});
|
|
2816
|
+
});
|
|
2817
|
+
rows.push({
|
|
2818
|
+
node: rowNode,
|
|
2819
|
+
cells
|
|
2820
|
+
});
|
|
2821
|
+
});
|
|
2822
|
+
return rows;
|
|
2823
|
+
}
|
|
2824
|
+
function safeFindCell(map, relativePos) {
|
|
2825
|
+
try {
|
|
2826
|
+
return map.findCell(relativePos);
|
|
2827
|
+
} catch {
|
|
2828
|
+
return null;
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
function getSelectedTableRect(editor) {
|
|
2832
|
+
const cellSelection = getCellSelectionPositions(editor.state.selection);
|
|
2833
|
+
if (cellSelection) {
|
|
2834
|
+
const tableInfo = findTableInfoFromCellPos(editor, cellSelection.anchor);
|
|
2835
|
+
if (tableInfo) {
|
|
2836
|
+
const map = TableMap.get(tableInfo.table);
|
|
2837
|
+
const rect = map.rectBetween(
|
|
2838
|
+
cellSelection.anchor - tableInfo.tableStart,
|
|
2839
|
+
cellSelection.head - tableInfo.tableStart
|
|
2840
|
+
);
|
|
2841
|
+
return {
|
|
2842
|
+
...rect,
|
|
2843
|
+
map,
|
|
2844
|
+
table: tableInfo.table,
|
|
2845
|
+
tableStart: tableInfo.tableStart
|
|
2846
|
+
};
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
return selectedRect(editor.state);
|
|
2850
|
+
}
|
|
2851
|
+
function parsePixelWidth(value) {
|
|
2852
|
+
if (!value) return null;
|
|
2853
|
+
const parsed = Number.parseFloat(value);
|
|
2854
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : null;
|
|
2855
|
+
}
|
|
2856
|
+
function getDomColumnWidths(editor, rect) {
|
|
2857
|
+
const tableDom = editor.view.nodeDOM(rect.tableStart - 1);
|
|
2858
|
+
if (!(tableDom instanceof HTMLTableElement)) return null;
|
|
2859
|
+
const cols = Array.from(tableDom.querySelectorAll("colgroup > col"));
|
|
2860
|
+
if (cols.length === 0) return null;
|
|
2861
|
+
const widths = [];
|
|
2862
|
+
for (let col = rect.left; col < rect.right; col += 1) {
|
|
2863
|
+
const colElement = cols[col];
|
|
2864
|
+
if (!colElement) return null;
|
|
2865
|
+
const width = parsePixelWidth(colElement.style.width) ?? parsePixelWidth(colElement.getAttribute("width")) ?? Math.round(colElement.getBoundingClientRect().width);
|
|
2866
|
+
if (!Number.isFinite(width) || width <= 0) return null;
|
|
2867
|
+
widths.push(width);
|
|
2868
|
+
}
|
|
2869
|
+
return widths.length > 0 ? widths : null;
|
|
2870
|
+
}
|
|
2871
|
+
function getNodeColumnWidths(rect) {
|
|
2872
|
+
const widths = [];
|
|
2873
|
+
for (let col = rect.left; col < rect.right; col += 1) {
|
|
2874
|
+
let width = null;
|
|
2875
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2876
|
+
for (let row = 0; row < rect.map.height && width == null; row += 1) {
|
|
2877
|
+
const cellPos = rect.map.map[row * rect.map.width + col];
|
|
2878
|
+
if (seen.has(cellPos)) continue;
|
|
2879
|
+
seen.add(cellPos);
|
|
2880
|
+
const cell = rect.table.nodeAt(cellPos);
|
|
2881
|
+
const colwidth = cell?.attrs.colwidth;
|
|
2882
|
+
if (!Array.isArray(colwidth)) continue;
|
|
2883
|
+
const cellLeft = rect.map.colCount(cellPos);
|
|
2884
|
+
const widthIndex = col - cellLeft;
|
|
2885
|
+
const candidate = colwidth[widthIndex];
|
|
2886
|
+
if (typeof candidate === "number" && candidate > 0) {
|
|
2887
|
+
width = candidate;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
if (width == null) return null;
|
|
2891
|
+
widths.push(width);
|
|
2892
|
+
}
|
|
2893
|
+
return widths.length > 0 ? widths : null;
|
|
2894
|
+
}
|
|
2895
|
+
function getSelectedColumnWidths(editor, rect) {
|
|
2896
|
+
return getDomColumnWidths(editor, rect) ?? getNodeColumnWidths(rect);
|
|
2897
|
+
}
|
|
2898
|
+
function dispatchTableLayoutChange(editor) {
|
|
2899
|
+
editor.view.dom.dispatchEvent(new CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, { bubbles: true }));
|
|
2900
|
+
}
|
|
2901
|
+
function mergeTableCellsPreservingColumnWidths(editor) {
|
|
2902
|
+
const rect = getSelectedTableRect(editor);
|
|
2903
|
+
const widths = getSelectedColumnWidths(editor, rect);
|
|
2904
|
+
const merged = editor.chain().focus().mergeCells().run();
|
|
2905
|
+
if (!merged) return merged;
|
|
2906
|
+
if (!widths) {
|
|
2907
|
+
dispatchTableLayoutChange(editor);
|
|
2908
|
+
return merged;
|
|
2909
|
+
}
|
|
2910
|
+
const nextRect = getSelectedTableRect(editor);
|
|
2911
|
+
const cellPos = nextRect.map.map[nextRect.top * nextRect.map.width + nextRect.left];
|
|
2912
|
+
const absolutePos = nextRect.tableStart + cellPos;
|
|
2913
|
+
const node = editor.state.doc.nodeAt(absolutePos);
|
|
2914
|
+
if (!node) return merged;
|
|
2915
|
+
editor.view.dispatch(
|
|
2916
|
+
editor.state.tr.setNodeMarkup(absolutePos, node.type, {
|
|
2917
|
+
...node.attrs,
|
|
2918
|
+
colwidth: widths
|
|
2919
|
+
})
|
|
2920
|
+
);
|
|
2921
|
+
dispatchTableLayoutChange(editor);
|
|
2922
|
+
return true;
|
|
2923
|
+
}
|
|
2924
|
+
function runTableCommandAtCellPos(editor, cellPos, command) {
|
|
2925
|
+
if (cellPos == null) return false;
|
|
2926
|
+
focusCell(editor, cellPos);
|
|
2927
|
+
return command(editor.chain().focus(null, { scrollIntoView: false })).run();
|
|
2928
|
+
}
|
|
2929
|
+
function getTableCornerCellPos(editor, activePos) {
|
|
2930
|
+
const tableInfo = findTableInfoFromCellPos(editor, activePos);
|
|
2931
|
+
if (!tableInfo) return null;
|
|
2932
|
+
const map = TableMap.get(tableInfo.table);
|
|
2933
|
+
return tableInfo.tableStart + map.positionAt(map.height - 1, map.width - 1, tableInfo.table);
|
|
2934
|
+
}
|
|
2935
|
+
function replaceTableAtCellPos(editor, cellPos, updateTable) {
|
|
2936
|
+
if (cellPos == null) return false;
|
|
2937
|
+
const tableInfo = findTableInfoFromCellPos(editor, cellPos);
|
|
2938
|
+
if (!tableInfo) return false;
|
|
2939
|
+
const nextTable = updateTable(tableInfo.table);
|
|
2940
|
+
if (!nextTable) return false;
|
|
2941
|
+
editor.view.dispatch(editor.state.tr.replaceWith(tableInfo.tablePos, tableInfo.tablePos + tableInfo.table.nodeSize, nextTable));
|
|
2942
|
+
dispatchTableLayoutChange(editor);
|
|
2943
|
+
return true;
|
|
2944
|
+
}
|
|
2945
|
+
function duplicateTableRowAt(editor, rowIndex, cellPos) {
|
|
2946
|
+
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
2947
|
+
const rows = collectChildren(tableNode);
|
|
2948
|
+
const rowNode = rows[rowIndex];
|
|
2949
|
+
if (!rowNode) return null;
|
|
2950
|
+
rows.splice(rowIndex + 1, 0, rowNode.copy(rowNode.content));
|
|
2951
|
+
return tableNode.type.create(tableNode.attrs, rows);
|
|
2952
|
+
});
|
|
2953
|
+
}
|
|
2954
|
+
function clearTableRowAt(editor, rowIndex, cellPos) {
|
|
2955
|
+
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
2956
|
+
const map = TableMap.get(tableNode);
|
|
2957
|
+
if (rowIndex < 0 || rowIndex >= map.height) return null;
|
|
2958
|
+
const rows = getTableRows(tableNode).map((rowInfo) => {
|
|
2959
|
+
const cells = collectChildren(rowInfo.node);
|
|
2960
|
+
for (const entry of rowInfo.cells) {
|
|
2961
|
+
const rect = safeFindCell(map, entry.relativePos);
|
|
2962
|
+
if (!rect || rect.top > rowIndex || rowIndex >= rect.bottom) continue;
|
|
2963
|
+
cells[entry.index] = createEmptyCellNode(entry.node);
|
|
2964
|
+
}
|
|
2965
|
+
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
2966
|
+
});
|
|
2967
|
+
return tableNode.type.create(tableNode.attrs, rows);
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
function duplicateTableColumnAt(editor, columnIndex, cellPos) {
|
|
2971
|
+
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
2972
|
+
const map = TableMap.get(tableNode);
|
|
2973
|
+
if (columnIndex < 0 || columnIndex >= map.width) return null;
|
|
2974
|
+
const rows = getTableRows(tableNode).map((rowInfo, rowIndex) => {
|
|
2975
|
+
const cells = collectChildren(rowInfo.node);
|
|
2976
|
+
const sourceCell = rowInfo.cells.find((entry) => {
|
|
2977
|
+
const rect = safeFindCell(map, entry.relativePos);
|
|
2978
|
+
return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
|
|
2979
|
+
});
|
|
2980
|
+
if (!sourceCell) return rowInfo.node;
|
|
2981
|
+
const sourceRect = safeFindCell(map, sourceCell.relativePos);
|
|
2982
|
+
if (sourceRect && (sourceRect.left < columnIndex || sourceRect.right > columnIndex + 1)) {
|
|
2983
|
+
cells[sourceCell.index] = createCellWithDuplicatedLogicalColumn(sourceCell.node, columnIndex - sourceRect.left);
|
|
2984
|
+
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
2985
|
+
}
|
|
2986
|
+
cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
|
|
2987
|
+
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
2988
|
+
});
|
|
2989
|
+
return tableNode.type.create(tableNode.attrs, rows);
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
function clearTableColumnAt(editor, columnIndex, cellPos) {
|
|
2993
|
+
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
2994
|
+
const map = TableMap.get(tableNode);
|
|
2995
|
+
if (columnIndex < 0 || columnIndex >= map.width) return null;
|
|
2996
|
+
const rows = getTableRows(tableNode).map((rowInfo) => {
|
|
2997
|
+
const cells = collectChildren(rowInfo.node);
|
|
2998
|
+
for (const entry of rowInfo.cells) {
|
|
2999
|
+
const rect = safeFindCell(map, entry.relativePos);
|
|
3000
|
+
if (!rect || rect.left > columnIndex || columnIndex >= rect.right) continue;
|
|
3001
|
+
cells[entry.index] = createEmptyCellNode(entry.node);
|
|
3002
|
+
}
|
|
3003
|
+
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
3004
|
+
});
|
|
3005
|
+
return tableNode.type.create(tableNode.attrs, rows);
|
|
3006
|
+
});
|
|
3007
|
+
}
|
|
3008
|
+
function expandTableFromCell(editor, activeCellPos, rows, columns) {
|
|
3009
|
+
let cornerCellPos = getTableCornerCellPos(editor, activeCellPos);
|
|
3010
|
+
if (cornerCellPos == null) return false;
|
|
3011
|
+
for (let index = 0; index < rows; index += 1) {
|
|
3012
|
+
const ok = runTableCommandAtCellPos(editor, cornerCellPos, (chain) => chain.addRowAfter());
|
|
3013
|
+
if (!ok) return false;
|
|
3014
|
+
cornerCellPos = getTableCornerCellPos(editor, cornerCellPos);
|
|
3015
|
+
if (cornerCellPos == null) return false;
|
|
3016
|
+
}
|
|
3017
|
+
for (let index = 0; index < columns; index += 1) {
|
|
3018
|
+
const ok = runTableCommandAtCellPos(editor, cornerCellPos, (chain) => chain.addColumnAfter());
|
|
3019
|
+
if (!ok) return false;
|
|
3020
|
+
cornerCellPos = getTableCornerCellPos(editor, cornerCellPos);
|
|
3021
|
+
if (cornerCellPos == null) return false;
|
|
3022
|
+
}
|
|
3023
|
+
dispatchTableLayoutChange(editor);
|
|
3024
|
+
return true;
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
// src/components/UEditor/typography-options.ts
|
|
3028
|
+
function normalizeStyleValue(value) {
|
|
3029
|
+
return typeof value === "string" ? value.trim().replace(/^['"]|['"]$/g, "") : "";
|
|
3030
|
+
}
|
|
3031
|
+
function getDefaultFontFamilies(t) {
|
|
3032
|
+
return [
|
|
3033
|
+
{ label: "Inter", value: '"Inter", "Noto Sans", "Noto Sans CJK KR", "Noto Sans CJK JP", "Segoe UI", sans-serif' },
|
|
3034
|
+
{ label: "\uAD74\uB9BC", value: '"Gulim", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
|
|
3035
|
+
{ label: "\uAD74\uB9BC\uCCB4", value: '"GulimChe", "Gulim", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
|
|
3036
|
+
{ label: "\uAD81\uC11C", value: '"Gungsuh", "Nanum Myeongjo", serif' },
|
|
3037
|
+
{ label: "\uAD81\uC11C\uCCB4", value: '"GungsuhChe", "Gungsuh", "Nanum Myeongjo", serif' },
|
|
3038
|
+
{ label: "\uB3CB\uC6C0", value: '"Dotum", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
|
|
3039
|
+
{ label: "\uB3CB\uC6C0\uCCB4", value: '"DotumChe", "Dotum", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
|
|
3040
|
+
{ label: "\uBC14\uD0D5", value: '"Batang", "Nanum Myeongjo", serif' },
|
|
3041
|
+
{ label: "\uBC14\uD0D5\uCCB4", value: '"BatangChe", "Batang", "Nanum Myeongjo", serif' },
|
|
3042
|
+
{ label: "\uB9D1\uC740\uACE0\uB515", value: '"Malgun Gothic", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
|
|
3043
|
+
{ label: "\uB098\uB214\uBA85\uC870", value: '"Nanum Myeongjo", "Batang", serif' },
|
|
3044
|
+
{ label: "System UI", value: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' },
|
|
3045
|
+
{ label: "Roboto", value: '"Roboto", "Noto Sans", "Apple SD Gothic Neo", "Hiragino Kaku Gothic ProN", sans-serif' },
|
|
3046
|
+
{ label: "Lexend", value: '"Lexend", "Be Vietnam Pro", "Segoe UI", sans-serif' },
|
|
3047
|
+
{ label: "Montserrat", value: '"Montserrat", "Segoe UI", sans-serif' },
|
|
3048
|
+
{ label: "Lora", value: '"Lora", "Georgia", "Times New Roman", "Nanum Myeongjo", "BIZ UDPMincho", serif' },
|
|
3049
|
+
{ label: "Playfair Display", value: '"Playfair Display", "Times New Roman", "Nanum Myeongjo", serif' },
|
|
3050
|
+
{ label: "Georgia", value: 'Georgia, "Nanum Myeongjo", "Batang", "Times New Roman", serif' },
|
|
3051
|
+
{ label: "Times New Roman", value: '"Times New Roman", Times, "BIZ UDPMincho", serif' },
|
|
3052
|
+
{ label: "Meiryo (JA)", value: '"Meiryo", "Hiragino Sans", "Noto Sans JP", sans-serif' },
|
|
3053
|
+
{ label: "Apple SD Gothic Neo (KO)", value: '"Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", sans-serif' },
|
|
3054
|
+
{ label: "JetBrains Mono", value: '"JetBrains Mono", "Fira Code", "SFMono-Regular", Consolas, "Noto Sans Mono CJK KR", "Noto Sans Mono CJK JP", monospace' }
|
|
3055
|
+
];
|
|
3056
|
+
}
|
|
3057
|
+
function getDefaultFontSizes() {
|
|
3058
|
+
return [
|
|
3059
|
+
{ label: "8", value: "8px" },
|
|
3060
|
+
{ label: "9", value: "9px" },
|
|
3061
|
+
{ label: "10", value: "10px" },
|
|
3062
|
+
{ label: "11", value: "11px" },
|
|
3063
|
+
{ label: "12", value: "12px" },
|
|
3064
|
+
{ label: "13", value: "13px" },
|
|
3065
|
+
{ label: "14", value: "14px" },
|
|
3066
|
+
{ label: "15", value: "15px" },
|
|
3067
|
+
{ label: "16", value: "16px" },
|
|
3068
|
+
{ label: "17", value: "17px" },
|
|
3069
|
+
{ label: "18", value: "18px" },
|
|
3070
|
+
{ label: "19", value: "19px" },
|
|
3071
|
+
{ label: "20", value: "20px" },
|
|
3072
|
+
{ label: "21", value: "21px" },
|
|
3073
|
+
{ label: "22", value: "22px" },
|
|
3074
|
+
{ label: "23", value: "23px" },
|
|
3075
|
+
{ label: "24", value: "24px" },
|
|
3076
|
+
{ label: "25", value: "25px" },
|
|
3077
|
+
{ label: "26", value: "26px" },
|
|
3078
|
+
{ label: "27", value: "27px" },
|
|
3079
|
+
{ label: "28", value: "28px" },
|
|
3080
|
+
{ label: "36", value: "36px" },
|
|
3081
|
+
{ label: "48", value: "48px" },
|
|
3082
|
+
{ label: "72", value: "72px" },
|
|
3083
|
+
{ label: "96", value: "96px" }
|
|
3084
|
+
];
|
|
3085
|
+
}
|
|
3086
|
+
function getDefaultLineHeights() {
|
|
3087
|
+
return [
|
|
3088
|
+
{ label: "1.2", value: "1.2" },
|
|
3089
|
+
{ label: "1.5", value: "1.5" },
|
|
3090
|
+
{ label: "1.75", value: "1.75" },
|
|
3091
|
+
{ label: "2", value: "2" }
|
|
3092
|
+
];
|
|
3093
|
+
}
|
|
3094
|
+
function getDefaultLetterSpacings() {
|
|
3095
|
+
return [
|
|
3096
|
+
{ label: "-0.02em", value: "-0.02em" },
|
|
3097
|
+
{ label: "0", value: "0" },
|
|
3098
|
+
{ label: "0.02em", value: "0.02em" },
|
|
3099
|
+
{ label: "0.05em", value: "0.05em" },
|
|
3100
|
+
{ label: "0.08em", value: "0.08em" }
|
|
3101
|
+
];
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
// src/components/UEditor/toolbar.tsx
|
|
3105
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3106
|
+
function getTableAnchorPos(editor) {
|
|
3107
|
+
const tableInfo = findTableNodeInfoFromState(editor.state);
|
|
3108
|
+
if (tableInfo) return editor.state.selection.from;
|
|
3109
|
+
const selectionAnchor = resolveEventElement(window.getSelection()?.anchorNode ?? null);
|
|
3110
|
+
const selectionCell = selectionAnchor?.closest?.("th,td");
|
|
3111
|
+
if (selectionCell instanceof HTMLTableCellElement && editor.view.dom.contains(selectionCell)) {
|
|
3112
|
+
return editor.view.posAtDOM(selectionCell, 0) + 1;
|
|
3113
|
+
}
|
|
3114
|
+
const activeElement = document.activeElement instanceof Element ? document.activeElement : null;
|
|
3115
|
+
const activeCell = activeElement?.closest?.("th,td");
|
|
3116
|
+
if (activeCell instanceof HTMLTableCellElement && editor.view.dom.contains(activeCell)) {
|
|
3117
|
+
return editor.view.posAtDOM(activeCell, 0) + 1;
|
|
3118
|
+
}
|
|
3119
|
+
const tables = editor.view.dom.querySelectorAll("table");
|
|
3120
|
+
if (tables.length !== 1) return null;
|
|
3121
|
+
const firstCell = tables[0]?.querySelector("th,td");
|
|
3122
|
+
return firstCell instanceof HTMLTableCellElement ? editor.view.posAtDOM(firstCell, 0) + 1 : null;
|
|
3123
|
+
}
|
|
3124
|
+
var EDITOR_UI_ACTIVE_MARKS = [
|
|
3125
|
+
"blockquote",
|
|
3126
|
+
"bold",
|
|
3127
|
+
"bulletList",
|
|
3128
|
+
"code",
|
|
3129
|
+
"codeBlock",
|
|
3130
|
+
"formCheckbox",
|
|
3131
|
+
"highlight",
|
|
3132
|
+
"image",
|
|
3133
|
+
"italic",
|
|
3134
|
+
"link",
|
|
3135
|
+
"orderedList",
|
|
3136
|
+
"paragraph",
|
|
3137
|
+
"strike",
|
|
3138
|
+
"subscript",
|
|
3139
|
+
"superscript",
|
|
3140
|
+
"taskList",
|
|
3141
|
+
"underline"
|
|
3142
|
+
];
|
|
3143
|
+
function computeEditorUiRenderState(editor) {
|
|
3144
|
+
const textStyle = editor.getAttributes("textStyle");
|
|
3145
|
+
const highlight = editor.getAttributes("highlight");
|
|
3146
|
+
const image = editor.getAttributes("image");
|
|
3147
|
+
const link = editor.getAttributes("link");
|
|
3148
|
+
const tableCell = editor.getAttributes("tableCell");
|
|
3149
|
+
const tableHeader = editor.getAttributes("tableHeader");
|
|
3150
|
+
const hasTableContext = findTableNodeInfoFromState(editor.state) !== null;
|
|
3151
|
+
const can = editor.can();
|
|
3152
|
+
return {
|
|
3153
|
+
active: EDITOR_UI_ACTIVE_MARKS.map((name) => editor.isActive(name)),
|
|
3154
|
+
alignment: ["left", "center", "right", "justify"].map((textAlign) => editor.isActive({ textAlign })),
|
|
3155
|
+
heading: [1, 2, 3].map((level) => editor.isActive("heading", { level })),
|
|
3156
|
+
textStyle: {
|
|
3157
|
+
color: textStyle.color ?? null,
|
|
3158
|
+
fontFamily: textStyle.fontFamily ?? null,
|
|
3159
|
+
fontSize: textStyle.fontSize ?? null,
|
|
3160
|
+
letterSpacing: textStyle.letterSpacing ?? null,
|
|
3161
|
+
lineHeight: textStyle.lineHeight ?? null
|
|
3162
|
+
},
|
|
3163
|
+
highlightColor: highlight.color ?? null,
|
|
3164
|
+
image: {
|
|
3165
|
+
imageLayout: image.imageLayout ?? null,
|
|
3166
|
+
imageWidthPreset: image.imageWidthPreset ?? null
|
|
3167
|
+
},
|
|
3168
|
+
linkHref: link.href ?? null,
|
|
3169
|
+
tableCell: {
|
|
3170
|
+
backgroundColor: tableCell.backgroundColor ?? tableHeader.backgroundColor ?? null,
|
|
3171
|
+
borderColor: tableCell.borderColor ?? tableHeader.borderColor ?? null,
|
|
3172
|
+
borderStyle: tableCell.borderStyle ?? tableHeader.borderStyle ?? null,
|
|
3173
|
+
borderWidth: tableCell.borderWidth ?? tableHeader.borderWidth ?? null,
|
|
3174
|
+
formula: tableCell.formula ?? tableHeader.formula ?? null,
|
|
3175
|
+
numberFormat: tableCell.numberFormat ?? tableHeader.numberFormat ?? null,
|
|
3176
|
+
textDirection: tableCell.textDirection ?? tableHeader.textDirection ?? null,
|
|
3177
|
+
verticalAlign: tableCell.verticalAlign ?? tableHeader.verticalAlign ?? null
|
|
3178
|
+
},
|
|
3179
|
+
can: {
|
|
3180
|
+
addColumnAfter: hasTableContext && can.addColumnAfter(),
|
|
3181
|
+
addColumnBefore: hasTableContext && can.addColumnBefore(),
|
|
3182
|
+
addRowAfter: hasTableContext && can.addRowAfter(),
|
|
3183
|
+
addRowBefore: hasTableContext && can.addRowBefore(),
|
|
3184
|
+
decreaseIndent: can.decreaseIndent(),
|
|
3185
|
+
increaseIndent: can.increaseIndent(),
|
|
3186
|
+
mergeCells: hasTableContext && can.mergeCells(),
|
|
3187
|
+
redo: can.redo(),
|
|
3188
|
+
splitCell: hasTableContext && can.splitCell(),
|
|
3189
|
+
undo: can.undo()
|
|
3190
|
+
},
|
|
3191
|
+
hasTableContext,
|
|
3192
|
+
isEmpty: editor.isEmpty
|
|
3193
|
+
};
|
|
3194
|
+
}
|
|
3195
|
+
var editorUiRenderStateCache = /* @__PURE__ */ new WeakMap();
|
|
3196
|
+
function getEditorUiRenderState(editor) {
|
|
3197
|
+
const state = editor.state;
|
|
3198
|
+
const cached = editorUiRenderStateCache.get(editor);
|
|
3199
|
+
if (cached?.state === state) return cached.value;
|
|
3200
|
+
const value = computeEditorUiRenderState(editor);
|
|
3201
|
+
editorUiRenderStateCache.set(editor, { state, value });
|
|
3202
|
+
return value;
|
|
3203
|
+
}
|
|
3204
|
+
var EditorUiRenderStateContext = React7.createContext(null);
|
|
3205
|
+
function EditorUiRenderStateProvider({
|
|
3206
|
+
children,
|
|
3207
|
+
editor
|
|
3208
|
+
}) {
|
|
3209
|
+
const value = useEditorState({
|
|
3210
|
+
editor,
|
|
3211
|
+
selector: ({ editor: currentEditor }) => getEditorUiRenderState(currentEditor)
|
|
3212
|
+
});
|
|
3213
|
+
return /* @__PURE__ */ jsx7(EditorUiRenderStateContext.Provider, { value: { editor, value }, children });
|
|
3214
|
+
}
|
|
3215
|
+
function useSharedEditorUiRenderState(editor) {
|
|
3216
|
+
const context = React7.useContext(EditorUiRenderStateContext);
|
|
3217
|
+
if (!context || context.editor !== editor) {
|
|
3218
|
+
throw new Error("UEditor chrome must be rendered inside EditorUiRenderStateProvider");
|
|
3219
|
+
}
|
|
3220
|
+
return context.value;
|
|
3221
|
+
}
|
|
3222
|
+
function fileToDataUrl2(file) {
|
|
3223
|
+
return new Promise((resolve, reject) => {
|
|
3224
|
+
const reader = new FileReader();
|
|
3225
|
+
reader.onload = () => resolve(String(reader.result ?? ""));
|
|
3226
|
+
reader.onerror = () => reject(reader.error ?? new Error("Failed to read image file"));
|
|
3227
|
+
reader.readAsDataURL(file);
|
|
3228
|
+
});
|
|
3229
|
+
}
|
|
3230
|
+
function formatTableInsertLabel(template, rows, cols) {
|
|
3231
|
+
return template.replace("{rows}", String(rows)).replace("{cols}", String(cols));
|
|
3232
|
+
}
|
|
3233
|
+
var ToolbarButton = React7.forwardRef(({ onClick, onMouseDown, active, disabled, children, title, className }, ref) => {
|
|
3234
|
+
const button = /* @__PURE__ */ jsx7(
|
|
3235
|
+
"button",
|
|
3236
|
+
{
|
|
3237
|
+
ref,
|
|
3238
|
+
type: "button",
|
|
3239
|
+
"aria-label": title,
|
|
3240
|
+
onMouseDown: (e) => {
|
|
3241
|
+
onMouseDown?.(e);
|
|
3242
|
+
e.preventDefault();
|
|
3243
|
+
},
|
|
3244
|
+
onClick,
|
|
3245
|
+
disabled,
|
|
3246
|
+
className: cn(
|
|
3247
|
+
"flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors duration-150",
|
|
3248
|
+
"gap-0.5 [&>svg]:h-3.5 [&>svg]:w-3.5 [&>svg]:shrink-0",
|
|
3249
|
+
"hover:bg-accent",
|
|
3250
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/20",
|
|
3251
|
+
"disabled:opacity-40 disabled:cursor-not-allowed",
|
|
3252
|
+
active ? "bg-primary/10 text-primary shadow-sm" : "text-[#7B8184] hover:text-foreground dark:text-muted-foreground",
|
|
3253
|
+
className
|
|
3254
|
+
),
|
|
3255
|
+
children
|
|
3256
|
+
}
|
|
3257
|
+
);
|
|
3258
|
+
if (title) {
|
|
3259
|
+
return /* @__PURE__ */ jsx7(Tooltip, { content: title, placement: "top", delay: { open: 200, close: 0 }, children: button });
|
|
3260
|
+
}
|
|
3261
|
+
return button;
|
|
3262
|
+
});
|
|
3263
|
+
ToolbarButton.displayName = "ToolbarButton";
|
|
3264
|
+
var ToolbarDivider = () => /* @__PURE__ */ jsx7("div", { "aria-hidden": "true", className: "mx-1.5 h-7 w-px shrink-0 bg-[rgba(123,129,132,0.24)]" });
|
|
3265
|
+
var TableInsertGrid = ({
|
|
3266
|
+
insertLabel,
|
|
3267
|
+
previewTemplate,
|
|
3268
|
+
onInsert
|
|
3269
|
+
}) => {
|
|
3270
|
+
const [selection, setSelection] = React7.useState({ rows: 3, cols: 3 });
|
|
3271
|
+
const maxRows = 8;
|
|
3272
|
+
const maxCols = 8;
|
|
3273
|
+
return /* @__PURE__ */ jsxs5("div", { className: "mb-2 rounded-xl border border-border/60 bg-muted/20 p-2", children: [
|
|
3274
|
+
/* @__PURE__ */ jsx7("div", { className: "mb-2 text-sm font-medium text-foreground", children: formatTableInsertLabel(previewTemplate, selection.rows, selection.cols) }),
|
|
3275
|
+
/* @__PURE__ */ jsx7("div", { className: "grid grid-cols-8 gap-1", children: Array.from({ length: maxRows }).map(
|
|
3276
|
+
(_, rowIndex) => Array.from({ length: maxCols }).map((__, colIndex) => {
|
|
3277
|
+
const rows = rowIndex + 1;
|
|
3278
|
+
const cols = colIndex + 1;
|
|
3279
|
+
const active = rows <= selection.rows && cols <= selection.cols;
|
|
3280
|
+
return /* @__PURE__ */ jsx7(
|
|
3281
|
+
"button",
|
|
3282
|
+
{
|
|
3283
|
+
type: "button",
|
|
3284
|
+
"aria-label": formatTableInsertLabel(previewTemplate, rows, cols),
|
|
3285
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3286
|
+
onMouseEnter: () => setSelection({ rows, cols }),
|
|
3287
|
+
onFocus: () => setSelection({ rows, cols }),
|
|
3288
|
+
onClick: () => onInsert(rows, cols),
|
|
3289
|
+
className: cn(
|
|
3290
|
+
"h-5 w-5 rounded-sm border transition-colors",
|
|
3291
|
+
active ? "border-primary bg-primary/20" : "border-border/70 bg-background hover:border-primary/60 hover:bg-primary/10"
|
|
3292
|
+
)
|
|
3293
|
+
},
|
|
3294
|
+
`${rows}-${cols}`
|
|
3295
|
+
);
|
|
3296
|
+
})
|
|
3297
|
+
) }),
|
|
3298
|
+
/* @__PURE__ */ jsx7("div", { className: "mt-2 text-xs text-muted-foreground", children: insertLabel })
|
|
3299
|
+
] });
|
|
3300
|
+
};
|
|
3301
|
+
function applyTableCellAttribute(editor, name, value) {
|
|
3302
|
+
const { state, view } = editor;
|
|
3303
|
+
const applied = setCellAttr(name, value)(state, view.dispatch.bind(view));
|
|
3304
|
+
if (applied) {
|
|
3305
|
+
view.focus();
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3308
|
+
editor.chain().focus().setCellAttribute(name, value).run();
|
|
3309
|
+
}
|
|
3310
|
+
var EditorToolbar = ({
|
|
3311
|
+
editor,
|
|
3312
|
+
variant,
|
|
3313
|
+
uploadImage,
|
|
3314
|
+
imageInsertMode = "base64",
|
|
3315
|
+
maxImageFileSize = DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
|
|
3316
|
+
allowedImageMimeTypes = DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
|
|
3317
|
+
fontFamilies,
|
|
3318
|
+
fontSizes,
|
|
3319
|
+
lineHeights,
|
|
3320
|
+
letterSpacings
|
|
3321
|
+
}) => {
|
|
3322
|
+
const t = useSmartTranslations("UEditor");
|
|
3323
|
+
const editorUiState = useSharedEditorUiRenderState(editor);
|
|
3324
|
+
const { textColors, highlightColors } = useEditorColors();
|
|
3325
|
+
const [showImageInput, setShowImageInput] = useState4(false);
|
|
3326
|
+
const [showLinkInput, setShowLinkInput] = useState4(false);
|
|
3327
|
+
const [isTableMenuOpen, setIsTableMenuOpen] = useState4(false);
|
|
3328
|
+
const fileInputRef = useRef4(null);
|
|
3329
|
+
const [isUploadingImage, setIsUploadingImage] = useState4(false);
|
|
3330
|
+
const [imageUploadError, setImageUploadError] = useState4(null);
|
|
3331
|
+
const isImageSelected = editor.isActive("image");
|
|
3332
|
+
const imageAttrs = editor.getAttributes("image");
|
|
3333
|
+
const tableAnchorPos = getTableAnchorPos(editor);
|
|
3334
|
+
const tableInfo = tableAnchorPos == null ? null : findTableNodeInfoFromState(editor.state, tableAnchorPos);
|
|
3335
|
+
const textStyleAttrs = editor.getAttributes("textStyle");
|
|
3336
|
+
const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
|
|
3337
|
+
const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
|
|
3338
|
+
const isTableSelected = tableInfo !== null;
|
|
3339
|
+
const hasTableContext = isTableSelected;
|
|
3340
|
+
const canMergeCells = hasTableContext && editor.can().mergeCells();
|
|
3341
|
+
const canSplitCell = hasTableContext && editor.can().splitCell();
|
|
3342
|
+
const currentCellVerticalAlign = normalizeStyleValue(editor.getAttributes("tableCell").verticalAlign || editor.getAttributes("tableHeader").verticalAlign) || "";
|
|
3343
|
+
const currentCellTextDirection = normalizeStyleValue(editor.getAttributes("tableCell").textDirection || editor.getAttributes("tableHeader").textDirection) || "horizontal";
|
|
3344
|
+
const currentFontFamily = normalizeStyleValue(textStyleAttrs.fontFamily);
|
|
3345
|
+
const currentFontSize = normalizeStyleValue(textStyleAttrs.fontSize);
|
|
3346
|
+
const currentTextColor = normalizeStyleValue(textStyleAttrs.color) || "inherit";
|
|
3347
|
+
const currentHighlightColor = normalizeStyleValue(editor.getAttributes("highlight").color) || "";
|
|
3348
|
+
const currentLineHeight = normalizeStyleValue(textStyleAttrs.lineHeight);
|
|
3349
|
+
const currentLetterSpacing = normalizeStyleValue(textStyleAttrs.letterSpacing);
|
|
3350
|
+
const availableFontFamilies = React7.useMemo(() => fontFamilies ?? getDefaultFontFamilies(t), [fontFamilies, t]);
|
|
3351
|
+
const availableFontSizes = React7.useMemo(() => fontSizes ?? getDefaultFontSizes(), [fontSizes]);
|
|
3352
|
+
const availableLineHeights = React7.useMemo(() => lineHeights ?? getDefaultLineHeights(), [lineHeights]);
|
|
3353
|
+
const availableLetterSpacings = React7.useMemo(() => letterSpacings ?? getDefaultLetterSpacings(), [letterSpacings]);
|
|
3354
|
+
const currentFontFamilyDisplayValue = currentFontFamily.split(",")[0]?.trim() ?? currentFontFamily;
|
|
3355
|
+
const currentFontFamilyLabel = availableFontFamilies.find((option) => normalizeStyleValue(option.value) === currentFontFamily)?.label ?? (currentFontFamilyDisplayValue || t("toolbar.fontDefault"));
|
|
3356
|
+
const currentFontSizeLabel = availableFontSizes.find((option) => normalizeStyleValue(option.value) === currentFontSize)?.label ?? "13";
|
|
3357
|
+
const currentLineHeightLabel = availableLineHeights.find((option) => normalizeStyleValue(option.value) === currentLineHeight)?.label ?? t("toolbar.lineHeightDefault");
|
|
3358
|
+
const currentLetterSpacingLabel = availableLetterSpacings.find((option) => normalizeStyleValue(option.value) === currentLetterSpacing)?.label ?? t("toolbar.letterSpacingDefault");
|
|
3359
|
+
const defaultFontFamily = availableFontFamilies[0];
|
|
3360
|
+
const defaultFontFamilyValue = defaultFontFamily?.value ?? "";
|
|
3361
|
+
const displayedFontFamilyLabel = currentFontFamily ? currentFontFamilyLabel : defaultFontFamily?.label ?? t("toolbar.fontDefault");
|
|
3362
|
+
const displayedFontFamilyValue = currentFontFamily || defaultFontFamilyValue;
|
|
3363
|
+
const displayedFontSizeLabel = currentFontSize ? currentFontSizeLabel : "13";
|
|
3364
|
+
const activeFontSize = currentFontSize || "13px";
|
|
3365
|
+
const isMedium = variant === "medium";
|
|
3366
|
+
const isMediumFull = variant === "medium-full";
|
|
3367
|
+
const isFull = variant === "default" || variant === "full" || variant === "notion" || !variant;
|
|
3368
|
+
const insertImageFiles = async (files) => {
|
|
3369
|
+
if (files.length === 0) return;
|
|
3370
|
+
setIsUploadingImage(true);
|
|
3371
|
+
setImageUploadError(null);
|
|
3372
|
+
for (const file of files) {
|
|
3373
|
+
if (!file.type.startsWith("image/")) continue;
|
|
3374
|
+
if (file.size > maxImageFileSize) continue;
|
|
3375
|
+
if (allowedImageMimeTypes.length > 0 && !allowedImageMimeTypes.includes(file.type)) continue;
|
|
3376
|
+
try {
|
|
3377
|
+
const src = imageInsertMode === "upload" && uploadImage ? await uploadImage(file) : await fileToDataUrl2(file);
|
|
3378
|
+
const safeSrc = sanitizeUEditorUrl(src, "image");
|
|
3379
|
+
if (!safeSrc) continue;
|
|
3380
|
+
editor.chain().focus().setImage({ src: safeSrc, alt: file.name }).run();
|
|
3381
|
+
editor.commands.createParagraphNear();
|
|
3382
|
+
} catch {
|
|
3383
|
+
setImageUploadError(t("imageInput.uploadError"));
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
setIsUploadingImage(false);
|
|
3387
|
+
};
|
|
3388
|
+
if (variant === "minimal") {
|
|
3389
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-0.5 border-b border-border/35 bg-muted/30 p-1.5", children: [
|
|
3390
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().undo().run(), disabled: !editor.can().undo(), title: t("toolbar.undo"), children: /* @__PURE__ */ jsx7(FigmaUndoIcon, { className: "h-4 w-4" }) }),
|
|
3391
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().redo().run(), disabled: !editor.can().redo(), title: t("toolbar.redo"), children: /* @__PURE__ */ jsx7(FigmaRedoIcon, { className: "h-4 w-4" }) }),
|
|
3392
|
+
/* @__PURE__ */ jsx7(ToolbarDivider, {}),
|
|
3393
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), title: t("toolbar.bold"), children: /* @__PURE__ */ jsx7(FigmaBoldIcon, { className: "h-4 w-4" }) }),
|
|
3394
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleItalic().run(), active: editor.isActive("italic"), title: t("toolbar.italic"), children: /* @__PURE__ */ jsx7(FigmaItalicIcon, { className: "h-4 w-4" }) }),
|
|
3395
|
+
/* @__PURE__ */ jsx7(
|
|
3396
|
+
ToolbarButton,
|
|
3397
|
+
{
|
|
3398
|
+
onClick: () => editor.chain().focus().toggleBulletList().run(),
|
|
3399
|
+
active: editor.isActive("bulletList"),
|
|
3400
|
+
title: t("toolbar.bulletList"),
|
|
3401
|
+
children: /* @__PURE__ */ jsx7(FigmaListIcon, { className: "h-4 w-4" })
|
|
3402
|
+
}
|
|
3403
|
+
),
|
|
3404
|
+
/* @__PURE__ */ jsx7(
|
|
3405
|
+
ToolbarButton,
|
|
3406
|
+
{
|
|
3407
|
+
onClick: () => editor.chain().focus().decreaseIndent().run(),
|
|
3408
|
+
disabled: !editorUiState.can.decreaseIndent,
|
|
3409
|
+
title: t("toolbar.decreaseIndent"),
|
|
3410
|
+
children: /* @__PURE__ */ jsx7(IndentDecrease, { className: "h-4 w-4" })
|
|
3411
|
+
}
|
|
3412
|
+
),
|
|
3413
|
+
/* @__PURE__ */ jsx7(
|
|
3414
|
+
ToolbarButton,
|
|
3415
|
+
{
|
|
3416
|
+
onClick: () => editor.chain().focus().increaseIndent().run(),
|
|
3417
|
+
disabled: !editorUiState.can.increaseIndent,
|
|
3418
|
+
title: t("toolbar.increaseIndent"),
|
|
3419
|
+
children: /* @__PURE__ */ jsx7(IndentIncrease, { className: "h-4 w-4" })
|
|
3420
|
+
}
|
|
3421
|
+
),
|
|
3422
|
+
/* @__PURE__ */ jsxs5(
|
|
3423
|
+
DropdownMenu,
|
|
3424
|
+
{
|
|
3425
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3426
|
+
}, active: editor.isActive("formCheckbox"), title: t("slashCommand.formCheckbox"), children: /* @__PURE__ */ jsx7(SquareCheckBig, { className: "w-4 h-4" }) }),
|
|
3427
|
+
children: [
|
|
3428
|
+
/* @__PURE__ */ jsx7(
|
|
3429
|
+
DropdownMenuItem,
|
|
3430
|
+
{
|
|
3431
|
+
icon: SquareCheckBig,
|
|
3432
|
+
label: t("slashCommand.formCheckbox"),
|
|
3433
|
+
onClick: () => editor.chain().focus().setFormCheckbox().run()
|
|
3434
|
+
}
|
|
3435
|
+
),
|
|
3436
|
+
/* @__PURE__ */ jsx7(
|
|
3437
|
+
DropdownMenuItem,
|
|
3438
|
+
{
|
|
3439
|
+
icon: CircleCheckBig,
|
|
3440
|
+
label: t("slashCommand.roundCheckbox"),
|
|
3441
|
+
onClick: () => editor.chain().focus().setFormCheckbox({ variant: "circle" }).run()
|
|
3442
|
+
}
|
|
3443
|
+
)
|
|
3444
|
+
]
|
|
3445
|
+
}
|
|
3446
|
+
),
|
|
3447
|
+
/* @__PURE__ */ jsx7(
|
|
3448
|
+
DropdownMenu,
|
|
3449
|
+
{
|
|
3450
|
+
contentClassName: "min-w-72",
|
|
3451
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => setShowLinkInput(!editor.isActive("link")), active: editor.isActive("link"), title: t("toolbar.link"), children: /* @__PURE__ */ jsx7(FigmaLinkIcon, { className: "h-4 w-4" }) }),
|
|
3452
|
+
children: showLinkInput ? /* @__PURE__ */ jsx7(
|
|
3453
|
+
LinkInput,
|
|
3454
|
+
{
|
|
3455
|
+
initialUrl: String(editor.getAttributes("link").href ?? ""),
|
|
3456
|
+
onSubmit: (url) => {
|
|
3457
|
+
applyEditorLink(editor, url);
|
|
3458
|
+
setShowLinkInput(false);
|
|
3459
|
+
},
|
|
3460
|
+
onCancel: () => setShowLinkInput(false)
|
|
3461
|
+
}
|
|
3462
|
+
) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3463
|
+
/* @__PURE__ */ jsx7(
|
|
3464
|
+
DropdownMenuItem,
|
|
3465
|
+
{
|
|
3466
|
+
icon: LinkIcon,
|
|
3467
|
+
label: t("toolbar.link"),
|
|
3468
|
+
onClick: () => setShowLinkInput(true),
|
|
3469
|
+
active: editor.isActive("link"),
|
|
3470
|
+
closeOnSelect: false
|
|
3471
|
+
}
|
|
3472
|
+
),
|
|
3473
|
+
/* @__PURE__ */ jsx7(
|
|
3474
|
+
DropdownMenuItem,
|
|
3475
|
+
{
|
|
3476
|
+
icon: Trash2,
|
|
3477
|
+
label: t("toolbar.removeLink"),
|
|
3478
|
+
onClick: () => editor.chain().focus().extendMarkRange("link").unsetLink().run(),
|
|
3479
|
+
disabled: !editor.isActive("link"),
|
|
3480
|
+
destructive: true
|
|
3481
|
+
}
|
|
3482
|
+
)
|
|
3483
|
+
] })
|
|
3484
|
+
}
|
|
3485
|
+
)
|
|
3486
|
+
] });
|
|
3487
|
+
}
|
|
3488
|
+
const VerticalAlignActiveIcon = currentCellVerticalAlign === "middle" ? AlignCenterVertical : currentCellVerticalAlign === "bottom" ? AlignEndVertical : AlignStartVertical;
|
|
3489
|
+
return /* @__PURE__ */ jsxs5(
|
|
3490
|
+
"div",
|
|
3491
|
+
{
|
|
3492
|
+
role: "toolbar",
|
|
3493
|
+
className: "flex min-h-12 flex-nowrap items-center gap-0.5 overflow-x-auto border-b border-[rgba(196,197,213,0.6)] bg-[#F4F4F4] px-2 py-2 dark:bg-muted/60",
|
|
3494
|
+
children: [
|
|
3495
|
+
isFull && /* @__PURE__ */ jsx7(
|
|
3496
|
+
DropdownMenu,
|
|
3497
|
+
{
|
|
3498
|
+
trigger: /* @__PURE__ */ jsxs5(
|
|
3499
|
+
ToolbarButton,
|
|
3500
|
+
{
|
|
3501
|
+
onClick: () => {
|
|
3502
|
+
},
|
|
3503
|
+
title: t("toolbar.fontFamily"),
|
|
3504
|
+
className: "h-8 w-44 max-w-44 justify-between gap-2 border border-[rgba(196,197,213,0.6)] bg-white px-2.5 text-[#404040] shadow-[0_1px_2px_rgba(0,0,0,0.07)] hover:bg-white hover:text-[#404040] dark:bg-background dark:text-foreground",
|
|
3505
|
+
children: [
|
|
3506
|
+
/* @__PURE__ */ jsx7("span", { className: "min-w-0 flex-1 truncate text-left text-sm font-normal", style: { fontFamily: displayedFontFamilyValue || void 0 }, children: displayedFontFamilyLabel }),
|
|
3507
|
+
/* @__PURE__ */ jsx7(FigmaChevronDownIcon, { className: "h-3 w-3 shrink-0 text-[#7B8184]" })
|
|
3508
|
+
]
|
|
3509
|
+
}
|
|
3510
|
+
),
|
|
3511
|
+
contentClassName: "max-h-80 overflow-y-auto min-w-56 p-2",
|
|
3512
|
+
children: availableFontFamilies.map((option) => /* @__PURE__ */ jsx7(
|
|
3513
|
+
DropdownMenuItem,
|
|
3514
|
+
{
|
|
3515
|
+
label: option.label,
|
|
3516
|
+
onClick: () => editor.chain().focus().setFontFamily(option.value).run(),
|
|
3517
|
+
active: normalizeStyleValue(option.value) === (currentFontFamily || normalizeStyleValue(defaultFontFamilyValue)),
|
|
3518
|
+
className: "font-medium"
|
|
3519
|
+
},
|
|
3520
|
+
option.value
|
|
3521
|
+
))
|
|
3522
|
+
}
|
|
3523
|
+
),
|
|
3524
|
+
(isMediumFull || isFull) && /* @__PURE__ */ jsx7(
|
|
3525
|
+
DropdownMenu,
|
|
3526
|
+
{
|
|
3527
|
+
trigger: /* @__PURE__ */ jsxs5(
|
|
3528
|
+
ToolbarButton,
|
|
3529
|
+
{
|
|
3530
|
+
onClick: () => {
|
|
3531
|
+
},
|
|
3532
|
+
title: t("toolbar.fontSize"),
|
|
3533
|
+
className: "h-8 w-16 justify-between gap-2 border border-[rgba(196,197,213,0.6)] bg-white px-2.5 text-[#404040] shadow-[0_1px_2px_rgba(0,0,0,0.07)] hover:bg-white hover:text-[#404040] dark:bg-background dark:text-foreground",
|
|
3534
|
+
children: [
|
|
3535
|
+
/* @__PURE__ */ jsx7("span", { className: "text-sm font-normal leading-none", children: displayedFontSizeLabel }),
|
|
3536
|
+
/* @__PURE__ */ jsx7(FigmaChevronDownIcon, { className: "h-3 w-3 shrink-0 text-[#7B8184]" })
|
|
3537
|
+
]
|
|
3538
|
+
}
|
|
3539
|
+
),
|
|
3540
|
+
contentClassName: "max-h-80 overflow-y-auto min-w-32 p-2",
|
|
3541
|
+
children: availableFontSizes.map((option) => /* @__PURE__ */ jsx7(
|
|
3542
|
+
DropdownMenuItem,
|
|
3543
|
+
{
|
|
3544
|
+
label: option.label,
|
|
3545
|
+
onClick: () => editor.chain().focus().setFontSize(option.value).run(),
|
|
3546
|
+
active: normalizeStyleValue(option.value) === activeFontSize
|
|
3547
|
+
},
|
|
3548
|
+
option.value
|
|
3549
|
+
))
|
|
3550
|
+
}
|
|
3551
|
+
),
|
|
3552
|
+
/* @__PURE__ */ jsxs5(
|
|
3553
|
+
DropdownMenu,
|
|
3554
|
+
{
|
|
3555
|
+
contentClassName: "p-1",
|
|
3556
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3557
|
+
}, title: t("toolbar.textStyle"), className: "px-1.5 w-auto gap-0.5", children: /* @__PURE__ */ jsx7(FigmaTextStyleIcon, { className: "h-4 w-4" }) }),
|
|
3558
|
+
children: [
|
|
3559
|
+
/* @__PURE__ */ jsx7(
|
|
3560
|
+
DropdownMenuItem,
|
|
3561
|
+
{
|
|
3562
|
+
icon: Type,
|
|
3563
|
+
label: t("toolbar.normal"),
|
|
3564
|
+
onClick: () => editor.chain().focus().setParagraph().run(),
|
|
3565
|
+
active: editor.isActive("paragraph")
|
|
3566
|
+
}
|
|
3567
|
+
),
|
|
3568
|
+
/* @__PURE__ */ jsx7(
|
|
3569
|
+
DropdownMenuItem,
|
|
3570
|
+
{
|
|
3571
|
+
icon: Heading1Icon,
|
|
3572
|
+
label: t("toolbar.heading1"),
|
|
3573
|
+
onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
|
|
3574
|
+
active: editor.isActive("heading", { level: 1 }),
|
|
3575
|
+
shortcut: "Ctrl+Alt+1"
|
|
3576
|
+
}
|
|
3577
|
+
),
|
|
3578
|
+
/* @__PURE__ */ jsx7(
|
|
3579
|
+
DropdownMenuItem,
|
|
3580
|
+
{
|
|
3581
|
+
icon: Heading2Icon,
|
|
3582
|
+
label: t("toolbar.heading2"),
|
|
3583
|
+
onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
|
|
3584
|
+
active: editor.isActive("heading", { level: 2 }),
|
|
3585
|
+
shortcut: "Ctrl+Alt+2"
|
|
3586
|
+
}
|
|
3587
|
+
),
|
|
3588
|
+
/* @__PURE__ */ jsx7(
|
|
3589
|
+
DropdownMenuItem,
|
|
3590
|
+
{
|
|
3591
|
+
icon: Heading3Icon,
|
|
3592
|
+
label: t("toolbar.heading3"),
|
|
3593
|
+
onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(),
|
|
3594
|
+
active: editor.isActive("heading", { level: 3 }),
|
|
3595
|
+
shortcut: "Ctrl+Alt+3"
|
|
3596
|
+
}
|
|
3597
|
+
)
|
|
3598
|
+
]
|
|
3599
|
+
}
|
|
3600
|
+
),
|
|
3601
|
+
isFull && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3602
|
+
/* @__PURE__ */ jsxs5(
|
|
3603
|
+
DropdownMenu,
|
|
3604
|
+
{
|
|
3605
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3606
|
+
}, title: t("toolbar.lineHeight"), className: "gap-0.5", children: /* @__PURE__ */ jsx7(FigmaLineHeightIcon, { className: "h-4 w-4" }) }),
|
|
3607
|
+
contentClassName: "max-h-72 overflow-y-auto p-1",
|
|
3608
|
+
children: [
|
|
3609
|
+
/* @__PURE__ */ jsx7(
|
|
3610
|
+
DropdownMenuItem,
|
|
3611
|
+
{
|
|
3612
|
+
icon: Type,
|
|
3613
|
+
label: t("toolbar.lineHeightDefault"),
|
|
3614
|
+
onClick: () => editor.chain().focus().unsetLineHeight().run(),
|
|
3615
|
+
active: !currentLineHeight
|
|
3616
|
+
}
|
|
3617
|
+
),
|
|
3618
|
+
availableLineHeights.map((option) => /* @__PURE__ */ jsx7(
|
|
3619
|
+
DropdownMenuItem,
|
|
3620
|
+
{
|
|
3621
|
+
label: option.label,
|
|
3622
|
+
onClick: () => editor.chain().focus().setLineHeight(option.value).run(),
|
|
3623
|
+
active: normalizeStyleValue(option.value) === currentLineHeight
|
|
3624
|
+
},
|
|
3625
|
+
option.value
|
|
3626
|
+
))
|
|
3627
|
+
]
|
|
3628
|
+
}
|
|
3629
|
+
),
|
|
3630
|
+
/* @__PURE__ */ jsxs5(
|
|
3631
|
+
DropdownMenu,
|
|
3632
|
+
{
|
|
3633
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3634
|
+
}, title: t("toolbar.letterSpacing"), className: "gap-0.5", children: /* @__PURE__ */ jsx7(FigmaLetterSpacingIcon, { className: "h-4 w-4" }) }),
|
|
3635
|
+
contentClassName: "max-h-72 overflow-y-auto p-1",
|
|
3636
|
+
children: [
|
|
3637
|
+
/* @__PURE__ */ jsx7(
|
|
3638
|
+
DropdownMenuItem,
|
|
3639
|
+
{
|
|
3640
|
+
icon: Type,
|
|
3641
|
+
label: t("toolbar.letterSpacingDefault"),
|
|
3642
|
+
onClick: () => editor.chain().focus().unsetLetterSpacing().run(),
|
|
3643
|
+
active: !currentLetterSpacing
|
|
3644
|
+
}
|
|
3645
|
+
),
|
|
3646
|
+
availableLetterSpacings.map((option) => /* @__PURE__ */ jsx7(
|
|
3647
|
+
DropdownMenuItem,
|
|
3648
|
+
{
|
|
3649
|
+
label: option.label,
|
|
3650
|
+
onClick: () => editor.chain().focus().setLetterSpacing(option.value).run(),
|
|
3651
|
+
active: normalizeStyleValue(option.value) === currentLetterSpacing
|
|
3652
|
+
},
|
|
3653
|
+
option.value
|
|
3654
|
+
))
|
|
3655
|
+
]
|
|
3656
|
+
}
|
|
3657
|
+
)
|
|
3658
|
+
] }),
|
|
3659
|
+
/* @__PURE__ */ jsx7(ToolbarDivider, {}),
|
|
3660
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), title: t("toolbar.bold"), children: /* @__PURE__ */ jsx7(FigmaBoldIcon, { className: "h-4 w-4" }) }),
|
|
3661
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleItalic().run(), active: editor.isActive("italic"), title: t("toolbar.italic"), children: /* @__PURE__ */ jsx7(FigmaItalicIcon, { className: "h-4 w-4" }) }),
|
|
3662
|
+
/* @__PURE__ */ jsx7(
|
|
3663
|
+
ToolbarButton,
|
|
3664
|
+
{
|
|
3665
|
+
onClick: () => editor.chain().focus().toggleUnderline().run(),
|
|
3666
|
+
active: editor.isActive("underline"),
|
|
3667
|
+
title: t("toolbar.underline"),
|
|
3668
|
+
children: /* @__PURE__ */ jsx7(FigmaUnderlineIcon, { className: "h-4 w-4" })
|
|
3669
|
+
}
|
|
3670
|
+
),
|
|
3671
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleStrike().run(), active: editor.isActive("strike"), title: t("toolbar.strike"), children: /* @__PURE__ */ jsx7(FigmaStrikeIcon, { className: "h-4 w-4" }) }),
|
|
3672
|
+
(isMediumFull || isFull) && /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleCode().run(), active: editor.isActive("code"), title: t("toolbar.code"), children: /* @__PURE__ */ jsx7(FigmaCodeIcon, { className: "h-4 w-4" }) }),
|
|
3673
|
+
isFull && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3674
|
+
/* @__PURE__ */ jsx7(
|
|
3675
|
+
ToolbarButton,
|
|
3676
|
+
{
|
|
3677
|
+
onClick: () => editor.chain().focus().toggleSubscript().run(),
|
|
3678
|
+
active: editor.isActive("subscript"),
|
|
3679
|
+
title: t("toolbar.subscript"),
|
|
3680
|
+
children: /* @__PURE__ */ jsx7(FigmaSubscriptIcon, { className: "h-4 w-4" })
|
|
3681
|
+
}
|
|
3682
|
+
),
|
|
3683
|
+
/* @__PURE__ */ jsx7(
|
|
3684
|
+
ToolbarButton,
|
|
3685
|
+
{
|
|
3686
|
+
onClick: () => editor.chain().focus().toggleSuperscript().run(),
|
|
3687
|
+
active: editor.isActive("superscript"),
|
|
3688
|
+
title: t("toolbar.superscript"),
|
|
3689
|
+
children: /* @__PURE__ */ jsx7(FigmaSuperscriptIcon, { className: "h-4 w-4" })
|
|
3690
|
+
}
|
|
3691
|
+
)
|
|
3692
|
+
] }),
|
|
3693
|
+
/* @__PURE__ */ jsx7(
|
|
3694
|
+
DropdownMenu,
|
|
3695
|
+
{
|
|
3696
|
+
contentClassName: "min-w-72",
|
|
3697
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => setShowLinkInput(!editor.isActive("link")), active: editor.isActive("link"), title: t("toolbar.link"), children: /* @__PURE__ */ jsx7(FigmaLinkIcon, { className: "h-4 w-4" }) }),
|
|
3698
|
+
children: showLinkInput ? /* @__PURE__ */ jsx7(
|
|
3699
|
+
LinkInput,
|
|
3700
|
+
{
|
|
3701
|
+
initialUrl: String(editor.getAttributes("link").href ?? ""),
|
|
3702
|
+
onSubmit: (url) => {
|
|
3703
|
+
applyEditorLink(editor, url);
|
|
3704
|
+
setShowLinkInput(false);
|
|
3705
|
+
},
|
|
3706
|
+
onCancel: () => setShowLinkInput(false)
|
|
3707
|
+
}
|
|
3708
|
+
) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3709
|
+
/* @__PURE__ */ jsx7(
|
|
3710
|
+
DropdownMenuItem,
|
|
3711
|
+
{
|
|
3712
|
+
icon: LinkIcon,
|
|
3713
|
+
label: t("toolbar.link"),
|
|
3714
|
+
onClick: () => setShowLinkInput(true),
|
|
3715
|
+
active: editor.isActive("link"),
|
|
3716
|
+
closeOnSelect: false
|
|
3717
|
+
}
|
|
3718
|
+
),
|
|
3719
|
+
/* @__PURE__ */ jsx7(
|
|
3720
|
+
DropdownMenuItem,
|
|
3721
|
+
{
|
|
3722
|
+
icon: Trash2,
|
|
3723
|
+
label: t("toolbar.removeLink"),
|
|
3724
|
+
onClick: () => editor.chain().focus().extendMarkRange("link").unsetLink().run(),
|
|
3725
|
+
disabled: !editor.isActive("link"),
|
|
3726
|
+
destructive: true
|
|
3727
|
+
}
|
|
3728
|
+
)
|
|
3729
|
+
] })
|
|
3730
|
+
}
|
|
3731
|
+
),
|
|
3732
|
+
/* @__PURE__ */ jsx7(ToolbarDivider, {}),
|
|
3733
|
+
/* @__PURE__ */ jsx7(
|
|
3734
|
+
DropdownMenu,
|
|
3735
|
+
{
|
|
3736
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3737
|
+
}, title: t("colors.textColor"), children: /* @__PURE__ */ jsx7(TextColorIcon, { color: currentTextColor }) }),
|
|
3738
|
+
children: /* @__PURE__ */ jsx7(
|
|
3739
|
+
EditorColorPalette,
|
|
3740
|
+
{
|
|
3741
|
+
colors: textColors,
|
|
3742
|
+
currentColor: currentTextColor,
|
|
3743
|
+
onSelect: (color) => {
|
|
3744
|
+
if (color === "inherit") {
|
|
3745
|
+
editor.chain().focus().unsetColor().run();
|
|
3746
|
+
} else {
|
|
3747
|
+
editor.chain().focus().setColor(color).run();
|
|
3748
|
+
}
|
|
3749
|
+
},
|
|
3750
|
+
label: t("colors.textColor")
|
|
3751
|
+
}
|
|
3752
|
+
)
|
|
3753
|
+
}
|
|
3754
|
+
),
|
|
3755
|
+
/* @__PURE__ */ jsx7(
|
|
3756
|
+
DropdownMenu,
|
|
3757
|
+
{
|
|
3758
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3759
|
+
}, active: editor.isActive("highlight"), title: t("colors.highlight"), children: /* @__PURE__ */ jsx7(HighlightColorIcon, { color: currentHighlightColor }) }),
|
|
3760
|
+
children: /* @__PURE__ */ jsx7(
|
|
3761
|
+
EditorColorPalette,
|
|
3762
|
+
{
|
|
3763
|
+
colors: highlightColors,
|
|
3764
|
+
currentColor: currentHighlightColor,
|
|
3765
|
+
onSelect: (color) => {
|
|
3766
|
+
if (color === "") {
|
|
3767
|
+
editor.chain().focus().unsetHighlight().run();
|
|
3768
|
+
} else {
|
|
3769
|
+
editor.chain().focus().toggleHighlight({ color }).run();
|
|
3770
|
+
}
|
|
3771
|
+
},
|
|
3772
|
+
label: t("colors.highlight")
|
|
3773
|
+
}
|
|
3774
|
+
)
|
|
3775
|
+
}
|
|
3776
|
+
),
|
|
3777
|
+
(isMediumFull || isFull) && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3778
|
+
/* @__PURE__ */ jsx7(ToolbarDivider, {}),
|
|
3779
|
+
/* @__PURE__ */ jsxs5(
|
|
3780
|
+
DropdownMenu,
|
|
3781
|
+
{
|
|
3782
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3783
|
+
}, title: t("toolbar.alignment"), children: /* @__PURE__ */ jsx7(FigmaAlignLeftIcon, { className: "h-4 w-4" }) }),
|
|
3784
|
+
children: [
|
|
3785
|
+
/* @__PURE__ */ jsx7(
|
|
3786
|
+
DropdownMenuItem,
|
|
3787
|
+
{
|
|
3788
|
+
icon: AlignLeft,
|
|
3789
|
+
label: t("toolbar.alignLeft"),
|
|
3790
|
+
onClick: () => editor.chain().focus().setTextAlign("left").run(),
|
|
3791
|
+
active: editor.isActive({ textAlign: "left" })
|
|
3792
|
+
}
|
|
3793
|
+
),
|
|
3794
|
+
/* @__PURE__ */ jsx7(
|
|
3795
|
+
DropdownMenuItem,
|
|
3796
|
+
{
|
|
3797
|
+
icon: AlignCenter,
|
|
3798
|
+
label: t("toolbar.alignCenter"),
|
|
3799
|
+
onClick: () => editor.chain().focus().setTextAlign("center").run(),
|
|
3800
|
+
active: editor.isActive({ textAlign: "center" })
|
|
3801
|
+
}
|
|
3802
|
+
),
|
|
3803
|
+
/* @__PURE__ */ jsx7(
|
|
3804
|
+
DropdownMenuItem,
|
|
3805
|
+
{
|
|
3806
|
+
icon: AlignRight,
|
|
3807
|
+
label: t("toolbar.alignRight"),
|
|
3808
|
+
onClick: () => editor.chain().focus().setTextAlign("right").run(),
|
|
3809
|
+
active: editor.isActive({ textAlign: "right" })
|
|
3810
|
+
}
|
|
3811
|
+
),
|
|
3812
|
+
/* @__PURE__ */ jsx7(
|
|
3813
|
+
DropdownMenuItem,
|
|
3814
|
+
{
|
|
3815
|
+
icon: AlignJustify,
|
|
3816
|
+
label: t("toolbar.justify"),
|
|
3817
|
+
onClick: () => editor.chain().focus().setTextAlign("justify").run(),
|
|
3818
|
+
active: editor.isActive({ textAlign: "justify" })
|
|
3819
|
+
}
|
|
3820
|
+
),
|
|
3821
|
+
hasTableContext && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3822
|
+
/* @__PURE__ */ jsx7("div", { className: "my-1 border-t" }),
|
|
3823
|
+
/* @__PURE__ */ jsx7(
|
|
3824
|
+
DropdownMenuItem,
|
|
3825
|
+
{
|
|
3826
|
+
icon: AlignStartVertical,
|
|
3827
|
+
label: t("tableMenu.alignVerticalTop") || "Align top",
|
|
3828
|
+
onClick: () => applyTableCellAttribute(editor, "verticalAlign", "top"),
|
|
3829
|
+
active: currentCellVerticalAlign === "top"
|
|
3830
|
+
}
|
|
3831
|
+
),
|
|
3832
|
+
/* @__PURE__ */ jsx7(
|
|
3833
|
+
DropdownMenuItem,
|
|
3834
|
+
{
|
|
3835
|
+
icon: AlignCenterVertical,
|
|
3836
|
+
label: t("tableMenu.alignVerticalMiddle") || "Align middle",
|
|
3837
|
+
onClick: () => applyTableCellAttribute(editor, "verticalAlign", "middle"),
|
|
3838
|
+
active: currentCellVerticalAlign === "middle"
|
|
3839
|
+
}
|
|
3840
|
+
),
|
|
3841
|
+
/* @__PURE__ */ jsx7(
|
|
3842
|
+
DropdownMenuItem,
|
|
3843
|
+
{
|
|
3844
|
+
icon: AlignEndVertical,
|
|
3845
|
+
label: t("tableMenu.alignVerticalBottom") || "Align bottom",
|
|
3846
|
+
onClick: () => applyTableCellAttribute(editor, "verticalAlign", "bottom"),
|
|
3847
|
+
active: currentCellVerticalAlign === "bottom"
|
|
3848
|
+
}
|
|
3849
|
+
)
|
|
3850
|
+
] })
|
|
3851
|
+
]
|
|
3852
|
+
}
|
|
3853
|
+
),
|
|
3854
|
+
hasTableContext && /* @__PURE__ */ jsxs5(
|
|
3855
|
+
DropdownMenu,
|
|
3856
|
+
{
|
|
3857
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3858
|
+
}, title: t("tableMenu.textDirection"), children: currentCellTextDirection === "vertical" ? /* @__PURE__ */ jsx7(ArrowDown, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx7(ArrowRight, { className: "w-4 h-4" }) }),
|
|
3859
|
+
children: [
|
|
3860
|
+
/* @__PURE__ */ jsx7(
|
|
3861
|
+
DropdownMenuItem,
|
|
3862
|
+
{
|
|
3863
|
+
icon: ArrowRight,
|
|
3864
|
+
label: t("tableMenu.horizontalText"),
|
|
3865
|
+
onClick: () => applyTableCellAttribute(editor, "textDirection", null),
|
|
3866
|
+
active: currentCellTextDirection === "horizontal"
|
|
3867
|
+
}
|
|
3868
|
+
),
|
|
3869
|
+
/* @__PURE__ */ jsx7(
|
|
3870
|
+
DropdownMenuItem,
|
|
3871
|
+
{
|
|
3872
|
+
icon: ArrowDown,
|
|
3873
|
+
label: t("tableMenu.verticalText"),
|
|
3874
|
+
onClick: () => applyTableCellAttribute(editor, "textDirection", "vertical"),
|
|
3875
|
+
active: currentCellTextDirection === "vertical"
|
|
3876
|
+
}
|
|
3877
|
+
)
|
|
3878
|
+
]
|
|
3879
|
+
}
|
|
3880
|
+
)
|
|
3881
|
+
] }),
|
|
3882
|
+
/* @__PURE__ */ jsx7(ToolbarDivider, {}),
|
|
3883
|
+
/* @__PURE__ */ jsxs5(
|
|
3884
|
+
DropdownMenu,
|
|
3885
|
+
{
|
|
3886
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3887
|
+
}, title: t("toolbar.bulletList"), children: /* @__PURE__ */ jsx7(FigmaListIcon, { className: "h-4 w-4" }) }),
|
|
3888
|
+
children: [
|
|
3889
|
+
/* @__PURE__ */ jsx7(
|
|
3890
|
+
DropdownMenuItem,
|
|
3891
|
+
{
|
|
3892
|
+
icon: ListIcon,
|
|
3893
|
+
label: t("toolbar.bulletList"),
|
|
3894
|
+
onClick: () => editor.chain().focus().toggleBulletList().run(),
|
|
3895
|
+
active: editor.isActive("bulletList"),
|
|
3896
|
+
shortcut: "Ctrl+Shift+8"
|
|
3897
|
+
}
|
|
3898
|
+
),
|
|
3899
|
+
/* @__PURE__ */ jsx7(
|
|
3900
|
+
DropdownMenuItem,
|
|
3901
|
+
{
|
|
3902
|
+
icon: ListOrderedIcon,
|
|
3903
|
+
label: t("toolbar.orderedList"),
|
|
3904
|
+
onClick: () => editor.chain().focus().toggleOrderedList().run(),
|
|
3905
|
+
active: editor.isActive("orderedList"),
|
|
3906
|
+
shortcut: "Ctrl+Shift+7"
|
|
3907
|
+
}
|
|
3908
|
+
),
|
|
3909
|
+
/* @__PURE__ */ jsx7(
|
|
3910
|
+
DropdownMenuItem,
|
|
3911
|
+
{
|
|
3912
|
+
icon: ListTodo,
|
|
3913
|
+
label: t("toolbar.taskList"),
|
|
3914
|
+
onClick: () => editor.chain().focus().toggleTaskList().run(),
|
|
3915
|
+
active: editor.isActive("taskList"),
|
|
3916
|
+
shortcut: "Ctrl+Shift+9"
|
|
3917
|
+
}
|
|
3918
|
+
)
|
|
3919
|
+
]
|
|
3920
|
+
}
|
|
3921
|
+
),
|
|
3922
|
+
/* @__PURE__ */ jsx7(
|
|
3923
|
+
ToolbarButton,
|
|
3924
|
+
{
|
|
3925
|
+
onClick: () => editor.chain().focus().decreaseIndent().run(),
|
|
3926
|
+
disabled: !editorUiState.can.decreaseIndent,
|
|
3927
|
+
title: t("toolbar.decreaseIndent"),
|
|
3928
|
+
children: /* @__PURE__ */ jsx7(IndentDecrease, { className: "h-4 w-4" })
|
|
3929
|
+
}
|
|
3930
|
+
),
|
|
3931
|
+
/* @__PURE__ */ jsx7(
|
|
3932
|
+
ToolbarButton,
|
|
3933
|
+
{
|
|
3934
|
+
onClick: () => editor.chain().focus().increaseIndent().run(),
|
|
3935
|
+
disabled: !editorUiState.can.increaseIndent,
|
|
3936
|
+
title: t("toolbar.increaseIndent"),
|
|
3937
|
+
children: /* @__PURE__ */ jsx7(IndentIncrease, { className: "h-4 w-4" })
|
|
3938
|
+
}
|
|
3939
|
+
),
|
|
3940
|
+
/* @__PURE__ */ jsxs5(
|
|
3941
|
+
DropdownMenu,
|
|
3942
|
+
{
|
|
3943
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3944
|
+
}, active: editor.isActive("formCheckbox"), title: t("slashCommand.formCheckbox"), children: /* @__PURE__ */ jsx7(SquareCheckBig, { className: "w-4 h-4" }) }),
|
|
3945
|
+
children: [
|
|
3946
|
+
/* @__PURE__ */ jsx7(
|
|
3947
|
+
DropdownMenuItem,
|
|
3948
|
+
{
|
|
3949
|
+
icon: SquareCheckBig,
|
|
3950
|
+
label: t("slashCommand.formCheckbox"),
|
|
3951
|
+
onClick: () => editor.chain().focus().setFormCheckbox().run()
|
|
3952
|
+
}
|
|
3953
|
+
),
|
|
3954
|
+
/* @__PURE__ */ jsx7(
|
|
3955
|
+
DropdownMenuItem,
|
|
3956
|
+
{
|
|
3957
|
+
icon: CircleCheckBig,
|
|
3958
|
+
label: t("slashCommand.roundCheckbox"),
|
|
3959
|
+
onClick: () => editor.chain().focus().setFormCheckbox({ variant: "circle" }).run()
|
|
3960
|
+
}
|
|
3961
|
+
)
|
|
3962
|
+
]
|
|
3963
|
+
}
|
|
3964
|
+
),
|
|
3965
|
+
isMediumFull && /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleBlockquote().run(), active: editor.isActive("blockquote"), title: t("toolbar.quote"), children: /* @__PURE__ */ jsx7(FigmaQuoteIcon, { className: "h-4 w-4" }) }),
|
|
3966
|
+
isFull && /* @__PURE__ */ jsxs5(
|
|
3967
|
+
DropdownMenu,
|
|
3968
|
+
{
|
|
3969
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3970
|
+
}, title: t("toolbar.quote"), children: /* @__PURE__ */ jsx7(FigmaQuoteIcon, { className: "h-4 w-4" }) }),
|
|
3971
|
+
children: [
|
|
3972
|
+
/* @__PURE__ */ jsx7(
|
|
3973
|
+
DropdownMenuItem,
|
|
3974
|
+
{
|
|
3975
|
+
icon: QuoteIcon,
|
|
3976
|
+
label: t("toolbar.quote"),
|
|
3977
|
+
onClick: () => editor.chain().focus().toggleBlockquote().run(),
|
|
3978
|
+
active: editor.isActive("blockquote"),
|
|
3979
|
+
shortcut: "Ctrl+Shift+B"
|
|
3980
|
+
}
|
|
3981
|
+
),
|
|
3982
|
+
/* @__PURE__ */ jsx7(
|
|
3983
|
+
DropdownMenuItem,
|
|
3984
|
+
{
|
|
3985
|
+
icon: FileCode,
|
|
3986
|
+
label: t("toolbar.codeBlock"),
|
|
3987
|
+
onClick: () => editor.chain().focus().toggleCodeBlock().run(),
|
|
3988
|
+
active: editor.isActive("codeBlock"),
|
|
3989
|
+
shortcut: "Ctrl+Alt+C"
|
|
3990
|
+
}
|
|
3991
|
+
)
|
|
3992
|
+
]
|
|
3993
|
+
}
|
|
3994
|
+
),
|
|
3995
|
+
(isMediumFull || isFull) && /* @__PURE__ */ jsx7(
|
|
3996
|
+
DropdownMenu,
|
|
3997
|
+
{
|
|
3998
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
3999
|
+
}, title: t("toolbar.image"), children: /* @__PURE__ */ jsx7(FigmaImageIcon, { className: "h-4 w-4" }) }),
|
|
4000
|
+
children: showImageInput ? /* @__PURE__ */ jsx7(
|
|
4001
|
+
ImageInput,
|
|
4002
|
+
{
|
|
4003
|
+
onSubmit: (url, alt) => {
|
|
4004
|
+
editor.chain().focus().setImage({ src: url, alt }).run();
|
|
4005
|
+
setShowImageInput(false);
|
|
4006
|
+
},
|
|
4007
|
+
onCancel: () => setShowImageInput(false)
|
|
4008
|
+
}
|
|
4009
|
+
) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
4010
|
+
/* @__PURE__ */ jsx7(DropdownMenuItem, { icon: LinkIcon, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true), closeOnSelect: false }),
|
|
4011
|
+
/* @__PURE__ */ jsx7(
|
|
4012
|
+
DropdownMenuItem,
|
|
4013
|
+
{
|
|
4014
|
+
icon: Upload,
|
|
4015
|
+
label: isUploadingImage ? t("imageInput.uploading") : t("imageInput.uploadTab"),
|
|
4016
|
+
disabled: isUploadingImage,
|
|
4017
|
+
onClick: () => fileInputRef.current?.click(),
|
|
4018
|
+
closeOnSelect: false
|
|
4019
|
+
}
|
|
4020
|
+
),
|
|
4021
|
+
imageUploadError && /* @__PURE__ */ jsx7(DropdownMenuItem, { label: imageUploadError, disabled: true, destructive: true }),
|
|
4022
|
+
/* @__PURE__ */ jsx7(
|
|
4023
|
+
"input",
|
|
4024
|
+
{
|
|
4025
|
+
ref: fileInputRef,
|
|
4026
|
+
type: "file",
|
|
4027
|
+
accept: "image/*",
|
|
4028
|
+
multiple: true,
|
|
4029
|
+
className: "hidden",
|
|
4030
|
+
onChange: (e) => {
|
|
4031
|
+
const files = Array.from(e.target.files ?? []);
|
|
4032
|
+
e.target.value = "";
|
|
4033
|
+
void insertImageFiles(files);
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
),
|
|
4037
|
+
/* @__PURE__ */ jsx7("div", { className: "my-1 border-t" }),
|
|
4038
|
+
/* @__PURE__ */ jsx7(
|
|
4039
|
+
DropdownMenuItem,
|
|
4040
|
+
{
|
|
4041
|
+
icon: AlignCenter,
|
|
4042
|
+
label: t("toolbar.imageLayoutBlock"),
|
|
4043
|
+
onClick: () => applyImageLayout(editor, "block"),
|
|
4044
|
+
active: isImageSelected && imageLayout === "block",
|
|
4045
|
+
disabled: !isImageSelected
|
|
4046
|
+
}
|
|
4047
|
+
),
|
|
4048
|
+
/* @__PURE__ */ jsx7(
|
|
4049
|
+
DropdownMenuItem,
|
|
4050
|
+
{
|
|
4051
|
+
icon: AlignLeft,
|
|
4052
|
+
label: t("toolbar.imageLayoutLeft"),
|
|
4053
|
+
onClick: () => applyImageLayout(editor, "left"),
|
|
4054
|
+
active: isImageSelected && imageLayout === "left",
|
|
4055
|
+
disabled: !isImageSelected
|
|
4056
|
+
}
|
|
4057
|
+
),
|
|
4058
|
+
/* @__PURE__ */ jsx7(
|
|
4059
|
+
DropdownMenuItem,
|
|
4060
|
+
{
|
|
4061
|
+
icon: AlignRight,
|
|
4062
|
+
label: t("toolbar.imageLayoutRight"),
|
|
4063
|
+
onClick: () => applyImageLayout(editor, "right"),
|
|
4064
|
+
active: isImageSelected && imageLayout === "right",
|
|
4065
|
+
disabled: !isImageSelected
|
|
4066
|
+
}
|
|
4067
|
+
),
|
|
4068
|
+
/* @__PURE__ */ jsx7("div", { className: "my-1 border-t" }),
|
|
4069
|
+
/* @__PURE__ */ jsx7(
|
|
4070
|
+
DropdownMenuItem,
|
|
4071
|
+
{
|
|
4072
|
+
label: t("toolbar.imageWidthSm"),
|
|
4073
|
+
onClick: () => applyImageWidthPreset(editor, "sm"),
|
|
4074
|
+
active: isImageSelected && imageWidthPreset === "sm",
|
|
4075
|
+
disabled: !isImageSelected
|
|
4076
|
+
}
|
|
4077
|
+
),
|
|
4078
|
+
/* @__PURE__ */ jsx7(
|
|
4079
|
+
DropdownMenuItem,
|
|
4080
|
+
{
|
|
4081
|
+
label: t("toolbar.imageWidthMd"),
|
|
4082
|
+
onClick: () => applyImageWidthPreset(editor, "md"),
|
|
4083
|
+
active: isImageSelected && imageWidthPreset === "md",
|
|
4084
|
+
disabled: !isImageSelected
|
|
4085
|
+
}
|
|
4086
|
+
),
|
|
4087
|
+
/* @__PURE__ */ jsx7(
|
|
4088
|
+
DropdownMenuItem,
|
|
4089
|
+
{
|
|
4090
|
+
label: t("toolbar.imageWidthLg"),
|
|
4091
|
+
onClick: () => applyImageWidthPreset(editor, "lg"),
|
|
4092
|
+
active: isImageSelected && imageWidthPreset === "lg",
|
|
4093
|
+
disabled: !isImageSelected
|
|
4094
|
+
}
|
|
4095
|
+
),
|
|
4096
|
+
/* @__PURE__ */ jsx7("div", { className: "my-1 border-t" }),
|
|
4097
|
+
/* @__PURE__ */ jsx7(
|
|
4098
|
+
DropdownMenuItem,
|
|
4099
|
+
{
|
|
4100
|
+
icon: RotateCcw,
|
|
4101
|
+
label: t("toolbar.imageResetSize"),
|
|
4102
|
+
onClick: () => resetImageSize(editor),
|
|
4103
|
+
disabled: !isImageSelected
|
|
4104
|
+
}
|
|
4105
|
+
),
|
|
4106
|
+
/* @__PURE__ */ jsx7(
|
|
4107
|
+
DropdownMenuItem,
|
|
4108
|
+
{
|
|
4109
|
+
icon: Trash2,
|
|
4110
|
+
label: t("toolbar.imageDelete"),
|
|
4111
|
+
onClick: () => deleteSelectedImage(editor),
|
|
4112
|
+
disabled: !isImageSelected,
|
|
4113
|
+
destructive: true
|
|
4114
|
+
}
|
|
4115
|
+
)
|
|
4116
|
+
] })
|
|
4117
|
+
}
|
|
4118
|
+
),
|
|
4119
|
+
(isMediumFull || isFull) && /* @__PURE__ */ jsx7(
|
|
4120
|
+
DropdownMenu,
|
|
4121
|
+
{
|
|
4122
|
+
isOpen: isTableMenuOpen,
|
|
4123
|
+
onOpenChange: setIsTableMenuOpen,
|
|
4124
|
+
trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
|
|
4125
|
+
}, title: t("toolbar.table"), children: /* @__PURE__ */ jsx7(FigmaTableIcon, { className: "h-4 w-4" }) }),
|
|
4126
|
+
contentClassName: "p-2 min-w-56",
|
|
4127
|
+
children: /* @__PURE__ */ jsx7(
|
|
4128
|
+
TableInsertGrid,
|
|
4129
|
+
{
|
|
4130
|
+
insertLabel: t("tableMenu.insertTable"),
|
|
4131
|
+
previewTemplate: t("tableMenu.gridPreview"),
|
|
4132
|
+
onInsert: (rows, cols) => {
|
|
4133
|
+
editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
|
|
4134
|
+
setIsTableMenuOpen(false);
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
)
|
|
4138
|
+
}
|
|
4139
|
+
),
|
|
4140
|
+
/* @__PURE__ */ jsx7(ToolbarDivider, {}),
|
|
4141
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().undo().run(), disabled: !editor.can().undo(), title: t("toolbar.undo"), children: /* @__PURE__ */ jsx7(FigmaUndoIcon, { className: "h-4 w-4" }) }),
|
|
4142
|
+
/* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().redo().run(), disabled: !editor.can().redo(), title: t("toolbar.redo"), children: /* @__PURE__ */ jsx7(FigmaRedoIcon, { className: "h-4 w-4" }) }),
|
|
4143
|
+
hasTableContext && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
4144
|
+
/* @__PURE__ */ jsx7(ToolbarDivider, {}),
|
|
4145
|
+
/* @__PURE__ */ jsx7(
|
|
4146
|
+
ToolbarButton,
|
|
4147
|
+
{
|
|
4148
|
+
onClick: () => editor.chain().focus().addColumnBefore().run(),
|
|
4149
|
+
disabled: !editor.can().addColumnBefore(),
|
|
4150
|
+
title: t("tableMenu.addColumnBefore"),
|
|
4151
|
+
children: /* @__PURE__ */ jsx7(ArrowLeft, { className: "w-4 h-4" })
|
|
4152
|
+
}
|
|
4153
|
+
),
|
|
4154
|
+
/* @__PURE__ */ jsx7(
|
|
4155
|
+
ToolbarButton,
|
|
4156
|
+
{
|
|
4157
|
+
onClick: () => editor.chain().focus().addColumnAfter().run(),
|
|
4158
|
+
disabled: !editor.can().addColumnAfter(),
|
|
4159
|
+
title: t("tableMenu.addColumnAfter"),
|
|
4160
|
+
children: /* @__PURE__ */ jsx7(ArrowRight, { className: "w-4 h-4" })
|
|
4161
|
+
}
|
|
4162
|
+
),
|
|
4163
|
+
/* @__PURE__ */ jsx7(
|
|
4164
|
+
ToolbarButton,
|
|
4165
|
+
{
|
|
4166
|
+
onClick: () => editor.chain().focus().addRowBefore().run(),
|
|
4167
|
+
disabled: !editor.can().addRowBefore(),
|
|
4168
|
+
title: t("tableMenu.addRowBefore"),
|
|
4169
|
+
children: /* @__PURE__ */ jsx7(ArrowUp, { className: "w-4 h-4" })
|
|
4170
|
+
}
|
|
4171
|
+
),
|
|
4172
|
+
/* @__PURE__ */ jsx7(
|
|
4173
|
+
ToolbarButton,
|
|
4174
|
+
{
|
|
4175
|
+
onClick: () => editor.chain().focus().addRowAfter().run(),
|
|
4176
|
+
disabled: !editor.can().addRowAfter(),
|
|
4177
|
+
title: t("tableMenu.addRowAfter"),
|
|
4178
|
+
children: /* @__PURE__ */ jsx7(ArrowDown, { className: "w-4 h-4" })
|
|
4179
|
+
}
|
|
4180
|
+
),
|
|
4181
|
+
/* @__PURE__ */ jsx7(
|
|
4182
|
+
ToolbarButton,
|
|
4183
|
+
{
|
|
4184
|
+
onClick: () => {
|
|
4185
|
+
if (canSplitCell) {
|
|
4186
|
+
editor.chain().focus().splitCell().run();
|
|
4187
|
+
return;
|
|
4188
|
+
}
|
|
4189
|
+
mergeTableCellsPreservingColumnWidths(editor);
|
|
4190
|
+
},
|
|
4191
|
+
active: canSplitCell,
|
|
4192
|
+
disabled: !canMergeCells && !canSplitCell,
|
|
4193
|
+
title: canSplitCell ? t("tableMenu.splitCell") : t("tableMenu.mergeCells"),
|
|
4194
|
+
children: /* @__PURE__ */ jsx7(TableCellsMerge, { className: "w-4 h-4" })
|
|
4195
|
+
}
|
|
4196
|
+
)
|
|
4197
|
+
] })
|
|
4198
|
+
]
|
|
4199
|
+
}
|
|
4200
|
+
);
|
|
4201
|
+
};
|
|
4202
|
+
|
|
4203
|
+
// src/components/UEditor/editor-styles.ts
|
|
4204
|
+
var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
|
|
4205
|
+
"prose prose-sm sm:prose dark:prose-invert max-w-none",
|
|
4206
|
+
"focus:outline-none",
|
|
4207
|
+
"px-4 py-4",
|
|
4208
|
+
"[&_.is-editor-empty]:before:content-[attr(data-placeholder)]",
|
|
4209
|
+
"[&_.is-editor-empty]:before:text-muted-foreground/50",
|
|
4210
|
+
"[&_.is-editor-empty]:before:float-left",
|
|
4211
|
+
"[&_.is-editor-empty]:before:pointer-events-none",
|
|
4212
|
+
"[&_.is-editor-empty]:before:h-0",
|
|
4213
|
+
"[&_.ProseMirror-gapcursor]:pointer-events-none",
|
|
4214
|
+
"[&_.ProseMirror-gapcursor]:absolute",
|
|
4215
|
+
"[&_.ProseMirror-gapcursor]:hidden",
|
|
4216
|
+
"[&_.ProseMirror-gapcursor:after]:content-['']",
|
|
4217
|
+
"[&_.ProseMirror-gapcursor:after]:block",
|
|
4218
|
+
"[&_.ProseMirror-gapcursor:after]:absolute",
|
|
4219
|
+
"[&_.ProseMirror-gapcursor:after]:top-[-2px]",
|
|
4220
|
+
"[&_.ProseMirror-gapcursor:after]:w-8",
|
|
4221
|
+
"[&_.ProseMirror-gapcursor:after]:border-t-2",
|
|
4222
|
+
"[&_.ProseMirror-gapcursor:after]:border-primary",
|
|
4223
|
+
"[&.ProseMirror-focused_.ProseMirror-gapcursor]:block",
|
|
4224
|
+
"[&_ul[data-type='taskList']]:list-none",
|
|
4225
|
+
"[&_ul[data-type='taskList']]:pl-0",
|
|
4226
|
+
"[&_ul[data-type='taskList']_li]:flex",
|
|
4227
|
+
"[&_ul[data-type='taskList']_li]:items-start",
|
|
4228
|
+
"[&_ul[data-type='taskList']_li]:gap-2",
|
|
4229
|
+
"[&_ul[data-type='taskList']_li>label]:mt-0.5",
|
|
4230
|
+
"[&_ul[data-type='taskList']_li>label>input]:w-4",
|
|
4231
|
+
"[&_ul[data-type='taskList']_li>label>input]:h-4",
|
|
4232
|
+
"[&_ul[data-type='taskList']_li>label>input]:rounded",
|
|
4233
|
+
"[&_ul[data-type='taskList']_li>label>input]:border-2",
|
|
4234
|
+
"[&_ul[data-type='taskList']_li>label>input]:border-primary/50",
|
|
4235
|
+
"[&_ul[data-type='taskList']_li>label>input]:accent-primary",
|
|
4236
|
+
"[&_pre]:bg-muted/40!",
|
|
4237
|
+
"[&_pre]:text-foreground!",
|
|
4238
|
+
"[&_pre]:border!",
|
|
4239
|
+
"[&_pre]:border-border/60!",
|
|
4240
|
+
"[&_pre_code]:bg-transparent!",
|
|
4241
|
+
"[&_.tableWrapper]:overflow-x-auto",
|
|
4242
|
+
"[&_.tableWrapper]:pb-1.5",
|
|
4243
|
+
"[&_.tableWrapper]:select-text",
|
|
4244
|
+
"[&_.tableWrapper]:[scrollbar-width:thin]",
|
|
4245
|
+
"[&_.tableWrapper]:[scrollbar-color:hsl(var(--border))_transparent]",
|
|
4246
|
+
"[&_.tableWrapper::-webkit-scrollbar]:h-2",
|
|
4247
|
+
"[&_.tableWrapper::-webkit-scrollbar]:w-2",
|
|
4248
|
+
"[&_.tableWrapper::-webkit-scrollbar-track]:rounded-full",
|
|
4249
|
+
"[&_.tableWrapper::-webkit-scrollbar-track]:bg-transparent",
|
|
4250
|
+
"[&_.tableWrapper::-webkit-scrollbar-thumb]:rounded-full",
|
|
4251
|
+
"[&_.tableWrapper::-webkit-scrollbar-thumb]:border",
|
|
4252
|
+
"[&_.tableWrapper::-webkit-scrollbar-thumb]:border-solid",
|
|
4253
|
+
"[&_.tableWrapper::-webkit-scrollbar-thumb]:border-transparent",
|
|
4254
|
+
"[&_.tableWrapper::-webkit-scrollbar-thumb]:bg-border/70",
|
|
4255
|
+
"[&_.tableWrapper::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/45",
|
|
4256
|
+
"[&_table]:w-auto",
|
|
4257
|
+
"[&_table]:table-fixed",
|
|
4258
|
+
"[&_table]:overflow-hidden",
|
|
4259
|
+
"[&_table]:select-text",
|
|
4260
|
+
"[&_table[data-table-align]]:w-max",
|
|
4261
|
+
"[&_table[data-table-align]]:max-w-full",
|
|
4262
|
+
"[&_table[data-table-align='center']]:mx-auto",
|
|
4263
|
+
"[&_table[data-table-align='right']]:ml-auto",
|
|
4264
|
+
"[&_table[data-table-align='right']]:mr-0",
|
|
4265
|
+
"[&_td]:relative",
|
|
4266
|
+
"[&_td]:align-top",
|
|
4267
|
+
"[&_td]:box-border",
|
|
4268
|
+
"[&_td]:select-text",
|
|
4269
|
+
"[&_td]:px-2",
|
|
4270
|
+
"[&_td]:py-0",
|
|
4271
|
+
"[&_td_p]:my-0",
|
|
4272
|
+
"[&_th]:relative",
|
|
4273
|
+
"[&_th]:align-top",
|
|
4274
|
+
"[&_th]:box-border",
|
|
4275
|
+
"[&_th]:select-text",
|
|
4276
|
+
"[&_th]:px-2",
|
|
4277
|
+
"[&_th]:py-0",
|
|
4278
|
+
"[&_th_p]:my-0",
|
|
4279
|
+
"[&_td[data-formula]]:pr-7",
|
|
4280
|
+
"[&_th[data-formula]]:pr-7",
|
|
4281
|
+
"[&_td[data-formula]]:before:pointer-events-none",
|
|
4282
|
+
"[&_th[data-formula]]:before:pointer-events-none",
|
|
4283
|
+
"[&_td[data-formula]]:before:absolute",
|
|
4284
|
+
"[&_th[data-formula]]:before:absolute",
|
|
4285
|
+
"[&_td[data-formula]]:before:right-1",
|
|
4286
|
+
"[&_th[data-formula]]:before:right-1",
|
|
4287
|
+
"[&_td[data-formula]]:before:top-1",
|
|
4288
|
+
"[&_th[data-formula]]:before:top-1",
|
|
4289
|
+
"[&_td[data-formula]]:before:z-[1]",
|
|
4290
|
+
"[&_th[data-formula]]:before:z-[1]",
|
|
4291
|
+
"[&_td[data-formula]]:before:rounded-sm",
|
|
4292
|
+
"[&_th[data-formula]]:before:rounded-sm",
|
|
4293
|
+
"[&_td[data-formula]]:before:bg-primary/10",
|
|
4294
|
+
"[&_th[data-formula]]:before:bg-primary/10",
|
|
4295
|
+
"[&_td[data-formula]]:before:px-1",
|
|
4296
|
+
"[&_th[data-formula]]:before:px-1",
|
|
4297
|
+
"[&_td[data-formula]]:before:font-mono",
|
|
4298
|
+
"[&_th[data-formula]]:before:font-mono",
|
|
4299
|
+
"[&_td[data-formula]]:before:text-[9px]",
|
|
4300
|
+
"[&_th[data-formula]]:before:text-[9px]",
|
|
4301
|
+
"[&_td[data-formula]]:before:font-semibold",
|
|
4302
|
+
"[&_th[data-formula]]:before:font-semibold",
|
|
4303
|
+
"[&_td[data-formula]]:before:leading-4",
|
|
4304
|
+
"[&_th[data-formula]]:before:leading-4",
|
|
4305
|
+
"[&_td[data-formula]]:before:text-primary",
|
|
4306
|
+
"[&_th[data-formula]]:before:text-primary",
|
|
4307
|
+
"[&_td[data-formula]]:before:content-['fx']",
|
|
4308
|
+
"[&_th[data-formula]]:before:content-['fx']",
|
|
4309
|
+
"[&_td[data-formula-state='error']]:bg-destructive/5",
|
|
4310
|
+
"[&_th[data-formula-state='error']]:bg-destructive/5",
|
|
4311
|
+
"[&_td[data-formula-state='error']]:before:bg-destructive/10",
|
|
4312
|
+
"[&_th[data-formula-state='error']]:before:bg-destructive/10",
|
|
4313
|
+
"[&_td[data-formula-state='error']]:before:text-destructive",
|
|
4314
|
+
"[&_th[data-formula-state='error']]:before:text-destructive",
|
|
4315
|
+
"[&_td[colwidth]]:min-w-0",
|
|
4316
|
+
"[&_th[colwidth]]:min-w-0",
|
|
4317
|
+
"[&_td[data-colwidth]]:min-w-0",
|
|
4318
|
+
"[&_th[data-colwidth]]:min-w-0",
|
|
4319
|
+
"[&_.selectedCell]:after:content-['']",
|
|
4320
|
+
"[&_.selectedCell]:after:absolute",
|
|
4321
|
+
"[&_.selectedCell]:after:inset-0",
|
|
4322
|
+
"[&_.selectedCell]:after:z-[2]",
|
|
4323
|
+
"[&_.selectedCell]:after:bg-primary/15",
|
|
4324
|
+
"[&_.selectedCell]:after:pointer-events-none",
|
|
4325
|
+
"[&_.column-resize-handle]:pointer-events-auto",
|
|
4326
|
+
"[&_.column-resize-handle]:cursor-col-resize",
|
|
4327
|
+
"[&_.column-resize-handle]:absolute",
|
|
4328
|
+
"[&_.column-resize-handle]:top-[-1px]",
|
|
4329
|
+
"[&_.column-resize-handle]:bottom-[-1px]",
|
|
4330
|
+
"[&_.column-resize-handle]:right-[-5px]",
|
|
4331
|
+
"[&_.column-resize-handle]:z-30",
|
|
4332
|
+
"[&_.column-resize-handle]:w-2.5",
|
|
4333
|
+
"[&_.column-resize-handle]:bg-transparent",
|
|
4334
|
+
"[&_.column-resize-handle]:rounded-none",
|
|
4335
|
+
"[&_.column-resize-handle]:opacity-0",
|
|
4336
|
+
"[&_.column-resize-handle]:transition-opacity",
|
|
4337
|
+
"[&_.column-resize-handle]:after:absolute",
|
|
4338
|
+
"[&_.column-resize-handle]:after:top-0",
|
|
4339
|
+
"[&_.column-resize-handle]:after:bottom-0",
|
|
4340
|
+
"[&_.column-resize-handle]:after:left-1/2",
|
|
4341
|
+
"[&_.column-resize-handle]:after:w-0.5",
|
|
4342
|
+
"[&_.column-resize-handle]:after:-translate-x-1/2",
|
|
4343
|
+
"[&_.column-resize-handle]:after:rounded-full",
|
|
4344
|
+
"[&_.column-resize-handle]:after:bg-primary/75",
|
|
4345
|
+
"[&_.column-resize-handle]:after:content-['']",
|
|
4346
|
+
"[&.resize-cursor_.column-resize-handle]:opacity-100",
|
|
4347
|
+
"[&.resize-cursor_.column-resize-handle]:after:bg-primary",
|
|
4348
|
+
"[&_.column-resize-dragging]:min-w-0",
|
|
4349
|
+
"[&.resize-cursor]:cursor-col-resize",
|
|
4350
|
+
"[&.resize-row-cursor]:cursor-row-resize",
|
|
4351
|
+
"[&_img.ProseMirror-selectednode]:ring-2",
|
|
4352
|
+
"[&_img.ProseMirror-selectednode]:ring-primary/60",
|
|
4353
|
+
"[&_img.ProseMirror-selectednode]:ring-offset-2",
|
|
4354
|
+
"[&_img.ProseMirror-selectednode]:ring-offset-background",
|
|
4355
|
+
"[&_hr]:border-t-2",
|
|
4356
|
+
"[&_hr]:border-primary/30",
|
|
4357
|
+
"[&_hr]:my-8",
|
|
4358
|
+
"[&_h1]:text-3xl",
|
|
4359
|
+
"[&_h1]:font-bold",
|
|
4360
|
+
"[&_h1]:mt-6",
|
|
4361
|
+
"[&_h1]:mb-4",
|
|
4362
|
+
"[&_h1]:text-foreground",
|
|
4363
|
+
"[&_h2]:text-2xl",
|
|
4364
|
+
"[&_h2]:font-semibold",
|
|
4365
|
+
"[&_h2]:mt-5",
|
|
4366
|
+
"[&_h2]:mb-3",
|
|
4367
|
+
"[&_h2]:text-foreground",
|
|
4368
|
+
"[&_h3]:text-xl",
|
|
4369
|
+
"[&_h3]:font-semibold",
|
|
4370
|
+
"[&_h3]:mt-4",
|
|
4371
|
+
"[&_h3]:mb-2",
|
|
4372
|
+
"[&_h3]:text-foreground",
|
|
4373
|
+
"[&_ul:not([data-type='taskList'])]:list-disc",
|
|
4374
|
+
"[&_ul:not([data-type='taskList'])]:pl-6",
|
|
4375
|
+
"[&_ul:not([data-type='taskList'])]:my-3",
|
|
4376
|
+
"[&_ol]:list-decimal",
|
|
4377
|
+
"[&_ol]:pl-6",
|
|
4378
|
+
"[&_ol]:my-3",
|
|
4379
|
+
"[&_li]:my-1",
|
|
4380
|
+
"[&_li]:pl-1",
|
|
4381
|
+
"[&_li_p]:my-0",
|
|
4382
|
+
"[&_blockquote]:border-l-4",
|
|
4383
|
+
"[&_blockquote]:border-primary",
|
|
4384
|
+
"[&_blockquote]:pl-4",
|
|
4385
|
+
"[&_blockquote]:py-2",
|
|
4386
|
+
"[&_blockquote]:my-4",
|
|
4387
|
+
"[&_blockquote]:bg-muted/30",
|
|
4388
|
+
"[&_blockquote]:rounded-r-lg",
|
|
4389
|
+
"[&_blockquote]:italic",
|
|
4390
|
+
"[&_blockquote]:text-muted-foreground",
|
|
4391
|
+
"[&_blockquote_p]:my-0",
|
|
4392
|
+
"[&_[data-image-layout='left']+p]:mt-1",
|
|
4393
|
+
"[&_[data-image-layout='left']+p]:min-h-[5rem]",
|
|
4394
|
+
"[&_[data-image-layout='right']+p]:mt-1",
|
|
4395
|
+
"[&_[data-image-layout='right']+p]:min-h-[5rem]",
|
|
4396
|
+
"max-md:[&_[data-image-layout='left']]:float-none",
|
|
4397
|
+
"max-md:[&_[data-image-layout='left']]:mr-0",
|
|
4398
|
+
"max-md:[&_[data-image-layout='left']]:ml-0",
|
|
4399
|
+
"max-md:[&_[data-image-layout='left']]:max-w-full",
|
|
4400
|
+
"max-md:[&_[data-image-layout='right']]:float-none",
|
|
4401
|
+
"max-md:[&_[data-image-layout='right']]:mr-0",
|
|
4402
|
+
"max-md:[&_[data-image-layout='right']]:ml-0",
|
|
4403
|
+
"max-md:[&_[data-image-layout='right']]:max-w-full",
|
|
4404
|
+
"max-md:[&_[data-image-layout='left']+p]:min-h-0",
|
|
4405
|
+
"max-md:[&_[data-image-layout='right']+p]:min-h-0"
|
|
4406
|
+
);
|
|
4407
|
+
|
|
4408
|
+
export {
|
|
4409
|
+
formControlSizeStyles,
|
|
4410
|
+
formControlFixedClass,
|
|
4411
|
+
formControlValueClass,
|
|
4412
|
+
getBorderRadiusClass,
|
|
4413
|
+
getPanelBorderRadiusClass,
|
|
4414
|
+
useUnderverseUIConfig,
|
|
4415
|
+
UnderverseConfigProvider,
|
|
4416
|
+
shadcnAnimationStyles,
|
|
4417
|
+
useShadCNAnimations,
|
|
4418
|
+
injectAnimationStyles,
|
|
4419
|
+
getAnimationStyles,
|
|
4420
|
+
Popover,
|
|
4421
|
+
useDropdownMenuClose,
|
|
4422
|
+
DropdownMenu,
|
|
4423
|
+
DropdownMenuItem,
|
|
4424
|
+
DropdownMenuSeparator,
|
|
4425
|
+
DropdownMenuSub,
|
|
4426
|
+
SelectDropdown,
|
|
4427
|
+
DropdownMenu_default,
|
|
4428
|
+
isSafeUEditorUrl,
|
|
4429
|
+
sanitizeUEditorUrl,
|
|
4430
|
+
DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
|
|
4431
|
+
DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
|
|
4432
|
+
ClipboardImages,
|
|
4433
|
+
DEFAULT_TABLE_ROW_HEIGHT,
|
|
4434
|
+
MIN_TABLE_ROW_HEIGHT,
|
|
4435
|
+
COLUMN_RESIZE_LINE_THICKNESS,
|
|
4436
|
+
ROW_RESIZE_LINE_THICKNESS,
|
|
4437
|
+
UEDITOR_TABLE_LAYOUT_CHANGE_EVENT,
|
|
4438
|
+
findTableRowNodeInfo,
|
|
4439
|
+
resolveEventElement,
|
|
4440
|
+
getSelectionTableCell,
|
|
4441
|
+
isRowResizeHotspot,
|
|
4442
|
+
isColumnResizeHotspot,
|
|
4443
|
+
getRelativeBoundaryMetrics,
|
|
4444
|
+
getRelativeCellMetrics,
|
|
4445
|
+
getRelativeSelectedCellsMetrics,
|
|
4446
|
+
findTableNodeInfoFromState,
|
|
4447
|
+
applyTableAlignment,
|
|
4448
|
+
dispatchTableLayoutChange,
|
|
4449
|
+
mergeTableCellsPreservingColumnWidths,
|
|
4450
|
+
runTableCommandAtCellPos,
|
|
4451
|
+
duplicateTableRowAt,
|
|
4452
|
+
clearTableRowAt,
|
|
4453
|
+
duplicateTableColumnAt,
|
|
4454
|
+
clearTableColumnAt,
|
|
4455
|
+
expandTableFromCell,
|
|
4456
|
+
TextColorIcon,
|
|
4457
|
+
HighlightColorIcon,
|
|
4458
|
+
CellBgColorIcon,
|
|
4459
|
+
CellBorderIcon,
|
|
4460
|
+
useEditorColors,
|
|
4461
|
+
EditorColorPalette,
|
|
4462
|
+
applyImageLayout,
|
|
4463
|
+
applyImageWidthPreset,
|
|
4464
|
+
resetImageSize,
|
|
4465
|
+
deleteSelectedImage,
|
|
4466
|
+
LinkInput,
|
|
4467
|
+
ImageInput,
|
|
4468
|
+
applyEditorLink,
|
|
4469
|
+
normalizeStyleValue,
|
|
4470
|
+
getDefaultFontSizes,
|
|
4471
|
+
getDefaultLineHeights,
|
|
4472
|
+
getTableAnchorPos,
|
|
4473
|
+
EditorUiRenderStateProvider,
|
|
4474
|
+
useSharedEditorUiRenderState,
|
|
4475
|
+
fileToDataUrl2 as fileToDataUrl,
|
|
4476
|
+
ToolbarButton,
|
|
4477
|
+
TableInsertGrid,
|
|
4478
|
+
EditorToolbar,
|
|
4479
|
+
UEDITOR_PROSEMIRROR_CLASS_NAME
|
|
4480
|
+
};
|
|
4481
|
+
//# sourceMappingURL=chunk-KFTYWTJR.js.map
|