@sofereditor/react 0.1.0
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/LICENSE +661 -0
- package/README.md +27 -0
- package/dist/index.cjs +3759 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +353 -0
- package/dist/index.d.ts +353 -0
- package/dist/index.js +3778 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3759 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
A4_PAGE: () => A4_PAGE,
|
|
24
|
+
Editor: () => Editor,
|
|
25
|
+
EditorProvider: () => EditorProvider,
|
|
26
|
+
ImageCaptionDialog: () => ImageCaptionDialog,
|
|
27
|
+
LinkDialog: () => LinkDialog,
|
|
28
|
+
NodeView: () => NodeView,
|
|
29
|
+
PageConfigDialog: () => PageConfigDialog,
|
|
30
|
+
Toolbar: () => Toolbar,
|
|
31
|
+
applyDomSelection: () => applyDomSelection,
|
|
32
|
+
contentHeight: () => contentHeight,
|
|
33
|
+
contentWidth: () => contentWidth,
|
|
34
|
+
defaultPageLayout: () => defaultPageLayout,
|
|
35
|
+
deltaLength: () => deltaLength,
|
|
36
|
+
findBlockIndex: () => findBlockIndex,
|
|
37
|
+
getBlockElement: () => getBlockElement,
|
|
38
|
+
getCellElement: () => getCellElement,
|
|
39
|
+
getFragmentForOffset: () => getFragmentForOffset,
|
|
40
|
+
isTableRectSelection: () => isTableRectSelection,
|
|
41
|
+
readDomSelection: () => readDomSelection,
|
|
42
|
+
renderInline: () => renderInline,
|
|
43
|
+
selectionsEqual: () => selectionsEqual,
|
|
44
|
+
sliceDelta: () => sliceDelta,
|
|
45
|
+
textOffsetWithin: () => textOffsetWithin,
|
|
46
|
+
useEditor: () => useEditor,
|
|
47
|
+
useEditorContext: () => useEditorContext,
|
|
48
|
+
usePageLayout: () => usePageLayout,
|
|
49
|
+
usePageSettings: () => usePageSettings
|
|
50
|
+
});
|
|
51
|
+
module.exports = __toCommonJS(index_exports);
|
|
52
|
+
|
|
53
|
+
// src/Editor.tsx
|
|
54
|
+
var import_react12 = require("react");
|
|
55
|
+
var import_core5 = require("@sofereditor/core");
|
|
56
|
+
|
|
57
|
+
// src/dom-bridge.ts
|
|
58
|
+
function findContainingBlockElement(node, root) {
|
|
59
|
+
let cur = node;
|
|
60
|
+
while (cur && cur !== root) {
|
|
61
|
+
if (cur.nodeType === Node.ELEMENT_NODE) {
|
|
62
|
+
const el = cur;
|
|
63
|
+
if (el.dataset.blockIndex != null) return el;
|
|
64
|
+
}
|
|
65
|
+
cur = cur.parentNode;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function findContainingCellElement(node, blockEl) {
|
|
70
|
+
let cur = node;
|
|
71
|
+
while (cur && cur !== blockEl) {
|
|
72
|
+
if (cur.nodeType === Node.ELEMENT_NODE) {
|
|
73
|
+
const el = cur;
|
|
74
|
+
if (el.dataset.cellIndex != null) return el;
|
|
75
|
+
}
|
|
76
|
+
cur = cur.parentNode;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
function findBlockIndex(node, root) {
|
|
81
|
+
const el = findContainingBlockElement(node, root);
|
|
82
|
+
if (!el) return null;
|
|
83
|
+
return Number.parseInt(el.dataset.blockIndex, 10);
|
|
84
|
+
}
|
|
85
|
+
function getBlockElement(root, blockIndex) {
|
|
86
|
+
return root.querySelector(`[data-block-index="${blockIndex}"]`);
|
|
87
|
+
}
|
|
88
|
+
function readFragmentStart(el) {
|
|
89
|
+
const s = el.dataset.fragmentStart;
|
|
90
|
+
if (s == null) return 0;
|
|
91
|
+
const n = Number.parseInt(s, 10);
|
|
92
|
+
return Number.isFinite(n) ? n : 0;
|
|
93
|
+
}
|
|
94
|
+
function readFragmentEnd(el) {
|
|
95
|
+
const s = el.dataset.fragmentEnd;
|
|
96
|
+
if (s == null) return void 0;
|
|
97
|
+
const n = Number.parseInt(s, 10);
|
|
98
|
+
return Number.isFinite(n) ? n : void 0;
|
|
99
|
+
}
|
|
100
|
+
function getFragmentForOffset(root, blockIndex, offset) {
|
|
101
|
+
const candidates = root.querySelectorAll(`[data-block-index="${blockIndex}"]`);
|
|
102
|
+
if (candidates.length === 0) return null;
|
|
103
|
+
if (candidates.length === 1) return candidates[0];
|
|
104
|
+
let last = null;
|
|
105
|
+
for (const el of candidates) {
|
|
106
|
+
last = el;
|
|
107
|
+
const start = readFragmentStart(el);
|
|
108
|
+
const end = readFragmentEnd(el);
|
|
109
|
+
if (end == null) return el;
|
|
110
|
+
if (offset >= start && offset < end) return el;
|
|
111
|
+
}
|
|
112
|
+
return last;
|
|
113
|
+
}
|
|
114
|
+
function getCellElement(root, blockIndex, cellIndex) {
|
|
115
|
+
const blockEl = getBlockElement(root, blockIndex);
|
|
116
|
+
if (!blockEl) return null;
|
|
117
|
+
return blockEl.querySelector(`[data-cell-index="${cellIndex}"]`);
|
|
118
|
+
}
|
|
119
|
+
function isImgEmbed(n) {
|
|
120
|
+
return n.nodeType === Node.ELEMENT_NODE && n.dataset.embed === "image";
|
|
121
|
+
}
|
|
122
|
+
function isWrapAnchorImg(n) {
|
|
123
|
+
return n.nodeType === Node.ELEMENT_NODE && n.dataset.wrapAnchor === "true";
|
|
124
|
+
}
|
|
125
|
+
function isPhantomEmbed(n) {
|
|
126
|
+
return n.nodeType === Node.ELEMENT_NODE && n.dataset.embedPhantom === "true";
|
|
127
|
+
}
|
|
128
|
+
function indexInParent(n) {
|
|
129
|
+
const parent = n.parentNode;
|
|
130
|
+
if (!parent) return 0;
|
|
131
|
+
let i = 0;
|
|
132
|
+
for (const c of Array.from(parent.childNodes)) {
|
|
133
|
+
if (c === n) return i;
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
function isRejectedSubtree(el, container) {
|
|
139
|
+
if (el === container) return false;
|
|
140
|
+
if (el.dataset.blockIndex != null) return true;
|
|
141
|
+
if (container.dataset.cellIndex != null && el.dataset.cellIndex != null) return true;
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
function textOffsetWithin(container, node, offsetInNode) {
|
|
145
|
+
let target = node;
|
|
146
|
+
let targetOff = offsetInNode;
|
|
147
|
+
if (isImgEmbed(target) && target.parentNode) {
|
|
148
|
+
targetOff = indexInParent(target);
|
|
149
|
+
target = target.parentNode;
|
|
150
|
+
}
|
|
151
|
+
let offset = 0;
|
|
152
|
+
let done = false;
|
|
153
|
+
function visit(n) {
|
|
154
|
+
if (done) return;
|
|
155
|
+
if (n === target) {
|
|
156
|
+
if (n.nodeType === Node.TEXT_NODE) {
|
|
157
|
+
offset += targetOff;
|
|
158
|
+
done = true;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const children = n.childNodes;
|
|
162
|
+
for (let i = 0; i < targetOff && i < children.length; i++) {
|
|
163
|
+
visit(children[i]);
|
|
164
|
+
if (done) return;
|
|
165
|
+
}
|
|
166
|
+
done = true;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (n.nodeType === Node.TEXT_NODE) {
|
|
170
|
+
offset += (n.textContent ?? "").length;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (n.nodeType !== Node.ELEMENT_NODE) return;
|
|
174
|
+
const el = n;
|
|
175
|
+
if (isRejectedSubtree(el, container)) return;
|
|
176
|
+
if (isImgEmbed(el) && isWrapAnchorImg(el)) return;
|
|
177
|
+
if (isImgEmbed(el) || isPhantomEmbed(el)) {
|
|
178
|
+
offset += 1;
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
for (const child of Array.from(el.childNodes)) {
|
|
182
|
+
visit(child);
|
|
183
|
+
if (done) return;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
visit(container);
|
|
187
|
+
return offset;
|
|
188
|
+
}
|
|
189
|
+
function resolveAnchorElement(node, root) {
|
|
190
|
+
const blockEl = findContainingBlockElement(node, root);
|
|
191
|
+
if (!blockEl) return null;
|
|
192
|
+
const blockIdx = Number.parseInt(blockEl.dataset.blockIndex, 10);
|
|
193
|
+
const blockType = blockEl.dataset.blockType;
|
|
194
|
+
if (blockType === "table") {
|
|
195
|
+
const cellEl = findContainingCellElement(node, blockEl);
|
|
196
|
+
if (!cellEl) {
|
|
197
|
+
const firstCell = blockEl.querySelector("[data-cell-index]");
|
|
198
|
+
if (!firstCell) return null;
|
|
199
|
+
return {
|
|
200
|
+
blockIdx,
|
|
201
|
+
cellIdx: 0,
|
|
202
|
+
container: firstCell,
|
|
203
|
+
containerStart: 0
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const cellIdx = Number.parseInt(cellEl.dataset.cellIndex, 10);
|
|
207
|
+
return {
|
|
208
|
+
blockIdx,
|
|
209
|
+
cellIdx,
|
|
210
|
+
container: cellEl,
|
|
211
|
+
containerStart: 0
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
blockIdx,
|
|
216
|
+
cellIdx: void 0,
|
|
217
|
+
container: blockEl,
|
|
218
|
+
containerStart: readFragmentStart(blockEl)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function readDomSelection(root) {
|
|
222
|
+
const sel = window.getSelection();
|
|
223
|
+
if (!sel || sel.rangeCount === 0) return null;
|
|
224
|
+
if (!sel.anchorNode || !sel.focusNode) return null;
|
|
225
|
+
if (!root.contains(sel.anchorNode) || !root.contains(sel.focusNode)) return null;
|
|
226
|
+
const a = resolveAnchorElement(sel.anchorNode, root);
|
|
227
|
+
const f = resolveAnchorElement(sel.focusNode, root);
|
|
228
|
+
if (!a || !f) return null;
|
|
229
|
+
const anchor = {
|
|
230
|
+
blockIndex: a.blockIdx,
|
|
231
|
+
cellIndex: a.cellIdx,
|
|
232
|
+
offset: a.containerStart + textOffsetWithin(a.container, sel.anchorNode, sel.anchorOffset)
|
|
233
|
+
};
|
|
234
|
+
const focus = {
|
|
235
|
+
blockIndex: f.blockIdx,
|
|
236
|
+
cellIndex: f.cellIdx,
|
|
237
|
+
offset: f.containerStart + textOffsetWithin(f.container, sel.focusNode, sel.focusOffset)
|
|
238
|
+
};
|
|
239
|
+
return { anchor, focus };
|
|
240
|
+
}
|
|
241
|
+
function locatePoint(root, pos) {
|
|
242
|
+
let container;
|
|
243
|
+
let containerStart = 0;
|
|
244
|
+
if (pos.cellIndex != null) {
|
|
245
|
+
container = getCellElement(root, pos.blockIndex, pos.cellIndex);
|
|
246
|
+
if (!container) return null;
|
|
247
|
+
} else {
|
|
248
|
+
container = getFragmentForOffset(root, pos.blockIndex, pos.offset);
|
|
249
|
+
if (!container) return null;
|
|
250
|
+
containerStart = readFragmentStart(container);
|
|
251
|
+
}
|
|
252
|
+
let remaining = Math.max(0, pos.offset - containerStart);
|
|
253
|
+
let result = null;
|
|
254
|
+
let lastImg = null;
|
|
255
|
+
function visit(n) {
|
|
256
|
+
if (result) return;
|
|
257
|
+
if (n.nodeType === Node.TEXT_NODE) {
|
|
258
|
+
const len = (n.textContent ?? "").length;
|
|
259
|
+
if (remaining <= len) {
|
|
260
|
+
result = { node: n, offset: remaining };
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
remaining -= len;
|
|
264
|
+
lastImg = null;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (n.nodeType !== Node.ELEMENT_NODE) return;
|
|
268
|
+
const el = n;
|
|
269
|
+
if (isRejectedSubtree(el, container)) return;
|
|
270
|
+
if (isImgEmbed(el) && isWrapAnchorImg(el)) return;
|
|
271
|
+
if (isImgEmbed(el) || isPhantomEmbed(el)) {
|
|
272
|
+
if (remaining === 0) {
|
|
273
|
+
const parent = el.parentNode;
|
|
274
|
+
if (parent) {
|
|
275
|
+
result = { node: parent, offset: indexInParent(el) };
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
remaining -= 1;
|
|
280
|
+
lastImg = el;
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
for (const child of Array.from(el.childNodes)) {
|
|
284
|
+
visit(child);
|
|
285
|
+
if (result) return;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
visit(container);
|
|
289
|
+
if (result) return result;
|
|
290
|
+
if (lastImg && remaining === 0) {
|
|
291
|
+
const parent = lastImg.parentNode;
|
|
292
|
+
if (parent) return { node: parent, offset: indexInParent(lastImg) + 1 };
|
|
293
|
+
}
|
|
294
|
+
return { node: container, offset: 0 };
|
|
295
|
+
}
|
|
296
|
+
function selectionsEqual(a, b) {
|
|
297
|
+
return a.anchor.blockIndex === b.anchor.blockIndex && (a.anchor.cellIndex ?? -1) === (b.anchor.cellIndex ?? -1) && a.anchor.offset === b.anchor.offset && a.focus.blockIndex === b.focus.blockIndex && (a.focus.cellIndex ?? -1) === (b.focus.cellIndex ?? -1) && a.focus.offset === b.focus.offset;
|
|
298
|
+
}
|
|
299
|
+
function isTableRectSelection(sel) {
|
|
300
|
+
if (sel.anchor.blockIndex !== sel.focus.blockIndex) return false;
|
|
301
|
+
if (sel.anchor.cellIndex == null || sel.focus.cellIndex == null) return false;
|
|
302
|
+
return sel.anchor.cellIndex !== sel.focus.cellIndex;
|
|
303
|
+
}
|
|
304
|
+
function applyDomSelection(root, modelSel) {
|
|
305
|
+
const domSel = window.getSelection();
|
|
306
|
+
if (!domSel) return;
|
|
307
|
+
if (isTableRectSelection(modelSel)) {
|
|
308
|
+
domSel.removeAllRanges();
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const anchorPt = locatePoint(root, modelSel.anchor);
|
|
312
|
+
const focusPt = locatePoint(root, modelSel.focus);
|
|
313
|
+
if (!anchorPt || !focusPt) return;
|
|
314
|
+
try {
|
|
315
|
+
domSel.removeAllRanges();
|
|
316
|
+
domSel.setBaseAndExtent(anchorPt.node, anchorPt.offset, focusPt.node, focusPt.offset);
|
|
317
|
+
} catch {
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/EditorContext.tsx
|
|
322
|
+
var import_react4 = require("react");
|
|
323
|
+
|
|
324
|
+
// src/LinkDialog.tsx
|
|
325
|
+
var import_react = require("react");
|
|
326
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
327
|
+
function LinkDialog() {
|
|
328
|
+
const { linkRequest, resolveLinkRequest } = useEditorContext();
|
|
329
|
+
const dialogRef = (0, import_react.useRef)(null);
|
|
330
|
+
const inputRef = (0, import_react.useRef)(null);
|
|
331
|
+
const [href, setHref] = (0, import_react.useState)("");
|
|
332
|
+
(0, import_react.useEffect)(() => {
|
|
333
|
+
const dialog = dialogRef.current;
|
|
334
|
+
if (!dialog) return;
|
|
335
|
+
if (linkRequest) {
|
|
336
|
+
setHref(linkRequest.initialHref);
|
|
337
|
+
if (!dialog.open) dialog.showModal();
|
|
338
|
+
queueMicrotask(() => {
|
|
339
|
+
const input = inputRef.current;
|
|
340
|
+
if (input) {
|
|
341
|
+
input.focus();
|
|
342
|
+
input.select();
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
} else if (dialog.open) {
|
|
346
|
+
dialog.close();
|
|
347
|
+
}
|
|
348
|
+
}, [linkRequest]);
|
|
349
|
+
if (!linkRequest) return null;
|
|
350
|
+
const onSubmit = (e) => {
|
|
351
|
+
e.preventDefault();
|
|
352
|
+
resolveLinkRequest(href.trim());
|
|
353
|
+
};
|
|
354
|
+
const onRemove = () => {
|
|
355
|
+
resolveLinkRequest("");
|
|
356
|
+
};
|
|
357
|
+
const onCancel = () => {
|
|
358
|
+
resolveLinkRequest(null);
|
|
359
|
+
};
|
|
360
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
361
|
+
"dialog",
|
|
362
|
+
{
|
|
363
|
+
ref: dialogRef,
|
|
364
|
+
className: "ed-link-dialog",
|
|
365
|
+
onClose: onCancel,
|
|
366
|
+
onCancel: (e) => {
|
|
367
|
+
e.preventDefault();
|
|
368
|
+
onCancel();
|
|
369
|
+
},
|
|
370
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit, className: "ed-link-dialog-form", children: [
|
|
371
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ed-link-dialog-row", children: [
|
|
372
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "ed-link-href", children: "URL" }),
|
|
373
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
374
|
+
"input",
|
|
375
|
+
{
|
|
376
|
+
id: "ed-link-href",
|
|
377
|
+
ref: inputRef,
|
|
378
|
+
type: "url",
|
|
379
|
+
inputMode: "url",
|
|
380
|
+
value: href,
|
|
381
|
+
onChange: (e) => setHref(e.target.value),
|
|
382
|
+
placeholder: "https://exemplo.com",
|
|
383
|
+
autoComplete: "off"
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
] }),
|
|
387
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ed-link-dialog-actions", children: [
|
|
388
|
+
linkRequest.initialHref ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: onRemove, className: "ed-link-dialog-remove", children: "Remover link" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
389
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ed-link-dialog-confirm", children: [
|
|
390
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: onCancel, children: "Cancelar" }),
|
|
391
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", className: "ed-link-dialog-apply", children: "Aplicar" })
|
|
392
|
+
] })
|
|
393
|
+
] })
|
|
394
|
+
] })
|
|
395
|
+
}
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// src/PageConfigDialog.tsx
|
|
400
|
+
var import_react2 = require("react");
|
|
401
|
+
var import_core = require("@sofereditor/core");
|
|
402
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
403
|
+
function PageConfigDialog() {
|
|
404
|
+
const { pageConfigRequest, resolvePageConfigRequest, setPageSettings } = useEditorContext();
|
|
405
|
+
const dialogRef = (0, import_react2.useRef)(null);
|
|
406
|
+
const [sizePreset, setSizePreset] = (0, import_react2.useState)("a4");
|
|
407
|
+
const [widthMm, setWidthMm] = (0, import_react2.useState)(210);
|
|
408
|
+
const [heightMm, setHeightMm] = (0, import_react2.useState)(297);
|
|
409
|
+
const [marginPreset, setMarginPreset] = (0, import_react2.useState)("normal");
|
|
410
|
+
const [marginTopMm, setMarginTopMm] = (0, import_react2.useState)(25.4);
|
|
411
|
+
const [marginBottomMm, setMarginBottomMm] = (0, import_react2.useState)(25.4);
|
|
412
|
+
const [marginLeftMm, setMarginLeftMm] = (0, import_react2.useState)(25.4);
|
|
413
|
+
const [marginRightMm, setMarginRightMm] = (0, import_react2.useState)(25.4);
|
|
414
|
+
(0, import_react2.useEffect)(() => {
|
|
415
|
+
const dialog = dialogRef.current;
|
|
416
|
+
if (!dialog) return;
|
|
417
|
+
if (pageConfigRequest) {
|
|
418
|
+
hydrateFromSettings(pageConfigRequest.initial, {
|
|
419
|
+
setSizePreset,
|
|
420
|
+
setWidthMm,
|
|
421
|
+
setHeightMm,
|
|
422
|
+
setMarginPreset,
|
|
423
|
+
setMarginTopMm,
|
|
424
|
+
setMarginBottomMm,
|
|
425
|
+
setMarginLeftMm,
|
|
426
|
+
setMarginRightMm
|
|
427
|
+
});
|
|
428
|
+
if (!dialog.open) dialog.showModal();
|
|
429
|
+
} else if (dialog.open) {
|
|
430
|
+
dialog.close();
|
|
431
|
+
}
|
|
432
|
+
}, [pageConfigRequest]);
|
|
433
|
+
(0, import_react2.useEffect)(() => {
|
|
434
|
+
if (sizePreset === "custom") return;
|
|
435
|
+
const p = import_core.PAGE_PRESETS[sizePreset];
|
|
436
|
+
setWidthMm(roundMm((0, import_core.pxToMm)(p.width)));
|
|
437
|
+
setHeightMm(roundMm((0, import_core.pxToMm)(p.height)));
|
|
438
|
+
}, [sizePreset]);
|
|
439
|
+
(0, import_react2.useEffect)(() => {
|
|
440
|
+
if (marginPreset === "custom") return;
|
|
441
|
+
const px = import_core.MARGIN_PRESETS[marginPreset];
|
|
442
|
+
const mm = roundMm((0, import_core.pxToMm)(px));
|
|
443
|
+
setMarginTopMm(mm);
|
|
444
|
+
setMarginBottomMm(mm);
|
|
445
|
+
setMarginLeftMm(mm);
|
|
446
|
+
setMarginRightMm(mm);
|
|
447
|
+
}, [marginPreset]);
|
|
448
|
+
const customWidth = sizePreset === "custom";
|
|
449
|
+
const previewPx = (0, import_react2.useMemo)(
|
|
450
|
+
() => ({
|
|
451
|
+
width: Math.round((0, import_core.mmToPx)(widthMm)),
|
|
452
|
+
height: Math.round((0, import_core.mmToPx)(heightMm))
|
|
453
|
+
}),
|
|
454
|
+
[widthMm, heightMm]
|
|
455
|
+
);
|
|
456
|
+
if (!pageConfigRequest) return null;
|
|
457
|
+
const onApply = (e) => {
|
|
458
|
+
e.preventDefault();
|
|
459
|
+
const next = {
|
|
460
|
+
width: (0, import_core.mmToPx)(widthMm),
|
|
461
|
+
height: (0, import_core.mmToPx)(heightMm),
|
|
462
|
+
marginTop: (0, import_core.mmToPx)(marginTopMm),
|
|
463
|
+
marginBottom: (0, import_core.mmToPx)(marginBottomMm),
|
|
464
|
+
marginLeft: (0, import_core.mmToPx)(marginLeftMm),
|
|
465
|
+
marginRight: (0, import_core.mmToPx)(marginRightMm)
|
|
466
|
+
};
|
|
467
|
+
setPageSettings(next);
|
|
468
|
+
resolvePageConfigRequest(true);
|
|
469
|
+
};
|
|
470
|
+
const onCancel = () => resolvePageConfigRequest(false);
|
|
471
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
472
|
+
"dialog",
|
|
473
|
+
{
|
|
474
|
+
ref: dialogRef,
|
|
475
|
+
className: "ed-page-config-dialog",
|
|
476
|
+
onClose: onCancel,
|
|
477
|
+
onCancel: (e) => {
|
|
478
|
+
e.preventDefault();
|
|
479
|
+
onCancel();
|
|
480
|
+
},
|
|
481
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: onApply, className: "ed-page-config-form", children: [
|
|
482
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: "Configurar p\xE1gina" }),
|
|
483
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("fieldset", { children: [
|
|
484
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("legend", { children: "Tamanho" }),
|
|
485
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ed-page-config-row", children: [
|
|
486
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
487
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
488
|
+
"input",
|
|
489
|
+
{
|
|
490
|
+
type: "radio",
|
|
491
|
+
name: "size",
|
|
492
|
+
checked: sizePreset === "a4",
|
|
493
|
+
onChange: () => setSizePreset("a4")
|
|
494
|
+
}
|
|
495
|
+
),
|
|
496
|
+
" ",
|
|
497
|
+
"A4 (210 \xD7 297 mm)"
|
|
498
|
+
] }),
|
|
499
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
500
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
501
|
+
"input",
|
|
502
|
+
{
|
|
503
|
+
type: "radio",
|
|
504
|
+
name: "size",
|
|
505
|
+
checked: sizePreset === "letter",
|
|
506
|
+
onChange: () => setSizePreset("letter")
|
|
507
|
+
}
|
|
508
|
+
),
|
|
509
|
+
" ",
|
|
510
|
+
"Carta (216 \xD7 279 mm)"
|
|
511
|
+
] }),
|
|
512
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
513
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
514
|
+
"input",
|
|
515
|
+
{
|
|
516
|
+
type: "radio",
|
|
517
|
+
name: "size",
|
|
518
|
+
checked: sizePreset === "oficio",
|
|
519
|
+
onChange: () => setSizePreset("oficio")
|
|
520
|
+
}
|
|
521
|
+
),
|
|
522
|
+
" ",
|
|
523
|
+
"Of\xEDcio (216 \xD7 330 mm)"
|
|
524
|
+
] }),
|
|
525
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
526
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
527
|
+
"input",
|
|
528
|
+
{
|
|
529
|
+
type: "radio",
|
|
530
|
+
name: "size",
|
|
531
|
+
checked: sizePreset === "custom",
|
|
532
|
+
onChange: () => setSizePreset("custom")
|
|
533
|
+
}
|
|
534
|
+
),
|
|
535
|
+
" ",
|
|
536
|
+
"Personalizado"
|
|
537
|
+
] })
|
|
538
|
+
] }),
|
|
539
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ed-page-config-row", children: [
|
|
540
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
541
|
+
"Largura (mm)",
|
|
542
|
+
" ",
|
|
543
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
544
|
+
"input",
|
|
545
|
+
{
|
|
546
|
+
type: "number",
|
|
547
|
+
step: 0.1,
|
|
548
|
+
min: 50,
|
|
549
|
+
max: 1e3,
|
|
550
|
+
value: widthMm,
|
|
551
|
+
onChange: (e) => setWidthMm(Number(e.target.value)),
|
|
552
|
+
disabled: !customWidth
|
|
553
|
+
}
|
|
554
|
+
)
|
|
555
|
+
] }),
|
|
556
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
557
|
+
"Altura (mm)",
|
|
558
|
+
" ",
|
|
559
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
560
|
+
"input",
|
|
561
|
+
{
|
|
562
|
+
type: "number",
|
|
563
|
+
step: 0.1,
|
|
564
|
+
min: 50,
|
|
565
|
+
max: 1e3,
|
|
566
|
+
value: heightMm,
|
|
567
|
+
onChange: (e) => setHeightMm(Number(e.target.value)),
|
|
568
|
+
disabled: !customWidth
|
|
569
|
+
}
|
|
570
|
+
)
|
|
571
|
+
] }),
|
|
572
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "ed-page-config-preview", children: [
|
|
573
|
+
previewPx.width,
|
|
574
|
+
" \xD7 ",
|
|
575
|
+
previewPx.height,
|
|
576
|
+
" px"
|
|
577
|
+
] })
|
|
578
|
+
] })
|
|
579
|
+
] }),
|
|
580
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("fieldset", { children: [
|
|
581
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("legend", { children: "Margens" }),
|
|
582
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ed-page-config-row", children: [
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
584
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
585
|
+
"input",
|
|
586
|
+
{
|
|
587
|
+
type: "radio",
|
|
588
|
+
name: "margin",
|
|
589
|
+
checked: marginPreset === "normal",
|
|
590
|
+
onChange: () => setMarginPreset("normal")
|
|
591
|
+
}
|
|
592
|
+
),
|
|
593
|
+
" ",
|
|
594
|
+
"Normal (25,4 mm)"
|
|
595
|
+
] }),
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
597
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
598
|
+
"input",
|
|
599
|
+
{
|
|
600
|
+
type: "radio",
|
|
601
|
+
name: "margin",
|
|
602
|
+
checked: marginPreset === "narrow",
|
|
603
|
+
onChange: () => setMarginPreset("narrow")
|
|
604
|
+
}
|
|
605
|
+
),
|
|
606
|
+
" ",
|
|
607
|
+
"Estreita (12,7 mm)"
|
|
608
|
+
] }),
|
|
609
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
611
|
+
"input",
|
|
612
|
+
{
|
|
613
|
+
type: "radio",
|
|
614
|
+
name: "margin",
|
|
615
|
+
checked: marginPreset === "wide",
|
|
616
|
+
onChange: () => setMarginPreset("wide")
|
|
617
|
+
}
|
|
618
|
+
),
|
|
619
|
+
" ",
|
|
620
|
+
"Larga (50,8 mm)"
|
|
621
|
+
] }),
|
|
622
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
623
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
624
|
+
"input",
|
|
625
|
+
{
|
|
626
|
+
type: "radio",
|
|
627
|
+
name: "margin",
|
|
628
|
+
checked: marginPreset === "custom",
|
|
629
|
+
onChange: () => setMarginPreset("custom")
|
|
630
|
+
}
|
|
631
|
+
),
|
|
632
|
+
" ",
|
|
633
|
+
"Personalizada"
|
|
634
|
+
] })
|
|
635
|
+
] }),
|
|
636
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ed-page-config-row", children: [
|
|
637
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
638
|
+
"Topo",
|
|
639
|
+
" ",
|
|
640
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
641
|
+
"input",
|
|
642
|
+
{
|
|
643
|
+
type: "number",
|
|
644
|
+
step: 0.1,
|
|
645
|
+
min: 0,
|
|
646
|
+
max: 200,
|
|
647
|
+
value: marginTopMm,
|
|
648
|
+
onChange: (e) => {
|
|
649
|
+
setMarginTopMm(Number(e.target.value));
|
|
650
|
+
setMarginPreset("custom");
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
)
|
|
654
|
+
] }),
|
|
655
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
656
|
+
"Inferior",
|
|
657
|
+
" ",
|
|
658
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
659
|
+
"input",
|
|
660
|
+
{
|
|
661
|
+
type: "number",
|
|
662
|
+
step: 0.1,
|
|
663
|
+
min: 0,
|
|
664
|
+
max: 200,
|
|
665
|
+
value: marginBottomMm,
|
|
666
|
+
onChange: (e) => {
|
|
667
|
+
setMarginBottomMm(Number(e.target.value));
|
|
668
|
+
setMarginPreset("custom");
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
)
|
|
672
|
+
] }),
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
674
|
+
"Esquerda",
|
|
675
|
+
" ",
|
|
676
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
677
|
+
"input",
|
|
678
|
+
{
|
|
679
|
+
type: "number",
|
|
680
|
+
step: 0.1,
|
|
681
|
+
min: 0,
|
|
682
|
+
max: 200,
|
|
683
|
+
value: marginLeftMm,
|
|
684
|
+
onChange: (e) => {
|
|
685
|
+
setMarginLeftMm(Number(e.target.value));
|
|
686
|
+
setMarginPreset("custom");
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
)
|
|
690
|
+
] }),
|
|
691
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
|
|
692
|
+
"Direita",
|
|
693
|
+
" ",
|
|
694
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
695
|
+
"input",
|
|
696
|
+
{
|
|
697
|
+
type: "number",
|
|
698
|
+
step: 0.1,
|
|
699
|
+
min: 0,
|
|
700
|
+
max: 200,
|
|
701
|
+
value: marginRightMm,
|
|
702
|
+
onChange: (e) => {
|
|
703
|
+
setMarginRightMm(Number(e.target.value));
|
|
704
|
+
setMarginPreset("custom");
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
)
|
|
708
|
+
] })
|
|
709
|
+
] })
|
|
710
|
+
] }),
|
|
711
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ed-page-config-actions", children: [
|
|
712
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", onClick: onCancel, children: "Cancelar" }),
|
|
713
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "submit", className: "ed-page-config-apply", children: "Aplicar" })
|
|
714
|
+
] })
|
|
715
|
+
] })
|
|
716
|
+
}
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
function roundMm(mm) {
|
|
720
|
+
return Math.round(mm * 10) / 10;
|
|
721
|
+
}
|
|
722
|
+
function hydrateFromSettings(s, setters) {
|
|
723
|
+
const preset = (0, import_core.detectPreset)(s.width, s.height);
|
|
724
|
+
setters.setSizePreset(preset);
|
|
725
|
+
setters.setWidthMm(roundMm((0, import_core.pxToMm)(s.width)));
|
|
726
|
+
setters.setHeightMm(roundMm((0, import_core.pxToMm)(s.height)));
|
|
727
|
+
const top = roundMm((0, import_core.pxToMm)(s.marginTop));
|
|
728
|
+
const bottom = roundMm((0, import_core.pxToMm)(s.marginBottom));
|
|
729
|
+
const left = roundMm((0, import_core.pxToMm)(s.marginLeft));
|
|
730
|
+
const right = roundMm((0, import_core.pxToMm)(s.marginRight));
|
|
731
|
+
setters.setMarginTopMm(top);
|
|
732
|
+
setters.setMarginBottomMm(bottom);
|
|
733
|
+
setters.setMarginLeftMm(left);
|
|
734
|
+
setters.setMarginRightMm(right);
|
|
735
|
+
setters.setMarginPreset(detectMarginPreset(s));
|
|
736
|
+
}
|
|
737
|
+
function detectMarginPreset(s) {
|
|
738
|
+
const TOL = 1;
|
|
739
|
+
const allEqual = Math.abs(s.marginTop - s.marginBottom) <= TOL && Math.abs(s.marginTop - s.marginLeft) <= TOL && Math.abs(s.marginTop - s.marginRight) <= TOL;
|
|
740
|
+
if (!allEqual) return "custom";
|
|
741
|
+
for (const [name, px] of Object.entries(import_core.MARGIN_PRESETS)) {
|
|
742
|
+
if (Math.abs(s.marginTop - px) <= TOL) return name;
|
|
743
|
+
}
|
|
744
|
+
return "custom";
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/ImageCaptionDialog.tsx
|
|
748
|
+
var import_react3 = require("react");
|
|
749
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
750
|
+
function ImageCaptionDialog() {
|
|
751
|
+
const { imageCaptionRequest, resolveImageCaptionRequest } = useEditorContext();
|
|
752
|
+
const dialogRef = (0, import_react3.useRef)(null);
|
|
753
|
+
const inputRef = (0, import_react3.useRef)(null);
|
|
754
|
+
const [caption, setCaption] = (0, import_react3.useState)("");
|
|
755
|
+
const [align, setAlign] = (0, import_react3.useState)("center");
|
|
756
|
+
(0, import_react3.useEffect)(() => {
|
|
757
|
+
const dialog = dialogRef.current;
|
|
758
|
+
if (!dialog) return;
|
|
759
|
+
if (imageCaptionRequest) {
|
|
760
|
+
setCaption(imageCaptionRequest.initialCaption);
|
|
761
|
+
setAlign(imageCaptionRequest.initialAlign);
|
|
762
|
+
if (!dialog.open) dialog.showModal();
|
|
763
|
+
queueMicrotask(() => {
|
|
764
|
+
const input = inputRef.current;
|
|
765
|
+
if (input) {
|
|
766
|
+
input.focus();
|
|
767
|
+
input.select();
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
} else if (dialog.open) {
|
|
771
|
+
dialog.close();
|
|
772
|
+
}
|
|
773
|
+
}, [imageCaptionRequest]);
|
|
774
|
+
if (!imageCaptionRequest) return null;
|
|
775
|
+
const onSubmit = (e) => {
|
|
776
|
+
e.preventDefault();
|
|
777
|
+
resolveImageCaptionRequest({ caption, align });
|
|
778
|
+
};
|
|
779
|
+
const onRemove = () => resolveImageCaptionRequest({ caption: "", align });
|
|
780
|
+
const onCancel = () => resolveImageCaptionRequest(null);
|
|
781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
782
|
+
"dialog",
|
|
783
|
+
{
|
|
784
|
+
ref: dialogRef,
|
|
785
|
+
className: "ed-caption-dialog ed-link-dialog",
|
|
786
|
+
onClose: onCancel,
|
|
787
|
+
onCancel: (e) => {
|
|
788
|
+
e.preventDefault();
|
|
789
|
+
onCancel();
|
|
790
|
+
},
|
|
791
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit, className: "ed-link-dialog-form", children: [
|
|
792
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ed-link-dialog-row", children: [
|
|
793
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { htmlFor: "ed-caption-input", children: "Legenda" }),
|
|
794
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
795
|
+
"input",
|
|
796
|
+
{
|
|
797
|
+
id: "ed-caption-input",
|
|
798
|
+
ref: inputRef,
|
|
799
|
+
type: "text",
|
|
800
|
+
value: caption,
|
|
801
|
+
onChange: (e) => setCaption(e.target.value),
|
|
802
|
+
placeholder: "Ex.: Figura 1 \u2014 Mapa do Brasil",
|
|
803
|
+
autoComplete: "off"
|
|
804
|
+
}
|
|
805
|
+
)
|
|
806
|
+
] }),
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ed-link-dialog-row", children: [
|
|
808
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Alinhamento" }),
|
|
809
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ed-caption-align-group", role: "radiogroup", "aria-label": "Alinhamento da legenda", children: ["left", "center", "right"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "ed-caption-align-option", children: [
|
|
810
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
811
|
+
"input",
|
|
812
|
+
{
|
|
813
|
+
type: "radio",
|
|
814
|
+
name: "caption-align",
|
|
815
|
+
value,
|
|
816
|
+
checked: align === value,
|
|
817
|
+
onChange: () => setAlign(value)
|
|
818
|
+
}
|
|
819
|
+
),
|
|
820
|
+
value === "left" ? "Esquerda" : value === "center" ? "Centro" : "Direita"
|
|
821
|
+
] }, value)) })
|
|
822
|
+
] }),
|
|
823
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ed-link-dialog-actions", children: [
|
|
824
|
+
imageCaptionRequest.initialCaption ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: onRemove, className: "ed-link-dialog-remove", children: "Remover legenda" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", {}),
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ed-link-dialog-confirm", children: [
|
|
826
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: onCancel, children: "Cancelar" }),
|
|
827
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", className: "ed-link-dialog-apply", children: "Aplicar" })
|
|
828
|
+
] })
|
|
829
|
+
] })
|
|
830
|
+
] })
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// src/EditorContext.tsx
|
|
836
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
837
|
+
var EditorContext = (0, import_react4.createContext)(null);
|
|
838
|
+
function EditorProvider({ editor, children }) {
|
|
839
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(EditorContext.Provider, { value: editor, children: [
|
|
840
|
+
children,
|
|
841
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LinkDialog, {}),
|
|
842
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PageConfigDialog, {}),
|
|
843
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ImageCaptionDialog, {})
|
|
844
|
+
] });
|
|
845
|
+
}
|
|
846
|
+
function useEditorContext() {
|
|
847
|
+
const ctx = (0, import_react4.useContext)(EditorContext);
|
|
848
|
+
if (!ctx) {
|
|
849
|
+
throw new Error("useEditorContext must be used inside an <EditorProvider>");
|
|
850
|
+
}
|
|
851
|
+
return ctx;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// src/ImageResizeOverlay.tsx
|
|
855
|
+
var import_react5 = require("react");
|
|
856
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
857
|
+
function ImageResizeOverlay({
|
|
858
|
+
rootRef,
|
|
859
|
+
blockIndex,
|
|
860
|
+
offset,
|
|
861
|
+
cellIndex,
|
|
862
|
+
embed,
|
|
863
|
+
onCommit,
|
|
864
|
+
onCommitMove
|
|
865
|
+
}) {
|
|
866
|
+
const [rect, setRect] = (0, import_react5.useState)(null);
|
|
867
|
+
const imgRef = (0, import_react5.useRef)(null);
|
|
868
|
+
const locateImage = (0, import_react5.useCallback)(() => {
|
|
869
|
+
const root = rootRef.current;
|
|
870
|
+
if (!root) return null;
|
|
871
|
+
let container = null;
|
|
872
|
+
if (cellIndex != null) {
|
|
873
|
+
container = root.querySelector(
|
|
874
|
+
`[data-block-index="${blockIndex}"] [data-cell-index="${cellIndex}"]`
|
|
875
|
+
);
|
|
876
|
+
} else {
|
|
877
|
+
container = getFragmentForOffset(root, blockIndex, offset);
|
|
878
|
+
}
|
|
879
|
+
if (!container) return null;
|
|
880
|
+
const fragmentStart = container.dataset.fragmentStart ? Number.parseInt(container.dataset.fragmentStart, 10) || 0 : 0;
|
|
881
|
+
const localOffset = offset - fragmentStart;
|
|
882
|
+
return container.querySelector(
|
|
883
|
+
`img[data-embed="image"][data-embed-offset="${localOffset}"]`
|
|
884
|
+
);
|
|
885
|
+
}, [rootRef, blockIndex, offset, cellIndex]);
|
|
886
|
+
const measure = (0, import_react5.useCallback)(() => {
|
|
887
|
+
const img = locateImage();
|
|
888
|
+
if (!img) {
|
|
889
|
+
setRect((prev) => prev === null ? prev : null);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
imgRef.current = img;
|
|
893
|
+
const root = rootRef.current;
|
|
894
|
+
if (!root) return;
|
|
895
|
+
const r = img.getBoundingClientRect();
|
|
896
|
+
const rr = root.getBoundingClientRect();
|
|
897
|
+
const next = {
|
|
898
|
+
top: r.top - rr.top + root.scrollTop,
|
|
899
|
+
left: r.left - rr.left + root.scrollLeft,
|
|
900
|
+
width: r.width,
|
|
901
|
+
height: r.height
|
|
902
|
+
};
|
|
903
|
+
setRect(
|
|
904
|
+
(prev) => prev && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
|
|
905
|
+
);
|
|
906
|
+
}, [locateImage, rootRef]);
|
|
907
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
908
|
+
measure();
|
|
909
|
+
let cancelled = false;
|
|
910
|
+
const remeasure = () => {
|
|
911
|
+
if (cancelled) return;
|
|
912
|
+
measure();
|
|
913
|
+
};
|
|
914
|
+
const id = typeof requestAnimationFrame === "function" ? requestAnimationFrame(remeasure) : (queueMicrotask(remeasure), 0);
|
|
915
|
+
return () => {
|
|
916
|
+
cancelled = true;
|
|
917
|
+
if (typeof cancelAnimationFrame === "function" && typeof id === "number" && id > 0) {
|
|
918
|
+
cancelAnimationFrame(id);
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
}, [
|
|
922
|
+
measure,
|
|
923
|
+
embed.width,
|
|
924
|
+
embed.height,
|
|
925
|
+
embed.align,
|
|
926
|
+
embed.src,
|
|
927
|
+
embed.layout,
|
|
928
|
+
embed.offsetX,
|
|
929
|
+
embed.offsetY
|
|
930
|
+
]);
|
|
931
|
+
(0, import_react5.useEffect)(() => {
|
|
932
|
+
const root = rootRef.current;
|
|
933
|
+
if (!root) return;
|
|
934
|
+
const onScroll = () => measure();
|
|
935
|
+
window.addEventListener("resize", onScroll);
|
|
936
|
+
root.addEventListener("scroll", onScroll);
|
|
937
|
+
return () => {
|
|
938
|
+
window.removeEventListener("resize", onScroll);
|
|
939
|
+
root.removeEventListener("scroll", onScroll);
|
|
940
|
+
};
|
|
941
|
+
}, [measure, rootRef]);
|
|
942
|
+
(0, import_react5.useEffect)(() => {
|
|
943
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
944
|
+
const img = locateImage();
|
|
945
|
+
const root = rootRef.current;
|
|
946
|
+
if (!img || !root) return;
|
|
947
|
+
const ro = new ResizeObserver(() => measure());
|
|
948
|
+
ro.observe(img);
|
|
949
|
+
ro.observe(root);
|
|
950
|
+
return () => ro.disconnect();
|
|
951
|
+
}, [locateImage, measure, rootRef, embed.layout, embed.align, embed.src]);
|
|
952
|
+
if (!rect) return null;
|
|
953
|
+
const layout = embed.layout ?? "inline";
|
|
954
|
+
const movable = layout !== "inline" && onCommitMove !== void 0;
|
|
955
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
956
|
+
"div",
|
|
957
|
+
{
|
|
958
|
+
className: "ed-image-overlay",
|
|
959
|
+
style: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },
|
|
960
|
+
contentEditable: false,
|
|
961
|
+
children: [
|
|
962
|
+
movable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
963
|
+
MoveHandle,
|
|
964
|
+
{
|
|
965
|
+
layout,
|
|
966
|
+
startOffsetX: embed.offsetX ?? 0,
|
|
967
|
+
startOffsetY: embed.offsetY ?? 0,
|
|
968
|
+
imgRef,
|
|
969
|
+
onCommit: onCommitMove,
|
|
970
|
+
onLiveChange: measure
|
|
971
|
+
}
|
|
972
|
+
),
|
|
973
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
974
|
+
Handle,
|
|
975
|
+
{
|
|
976
|
+
corner: "nw",
|
|
977
|
+
startW: embed.width,
|
|
978
|
+
startH: embed.height,
|
|
979
|
+
imgRef,
|
|
980
|
+
onCommit,
|
|
981
|
+
onLiveChange: measure
|
|
982
|
+
}
|
|
983
|
+
),
|
|
984
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
985
|
+
Handle,
|
|
986
|
+
{
|
|
987
|
+
corner: "ne",
|
|
988
|
+
startW: embed.width,
|
|
989
|
+
startH: embed.height,
|
|
990
|
+
imgRef,
|
|
991
|
+
onCommit,
|
|
992
|
+
onLiveChange: measure
|
|
993
|
+
}
|
|
994
|
+
),
|
|
995
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
996
|
+
Handle,
|
|
997
|
+
{
|
|
998
|
+
corner: "sw",
|
|
999
|
+
startW: embed.width,
|
|
1000
|
+
startH: embed.height,
|
|
1001
|
+
imgRef,
|
|
1002
|
+
onCommit,
|
|
1003
|
+
onLiveChange: measure
|
|
1004
|
+
}
|
|
1005
|
+
),
|
|
1006
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1007
|
+
Handle,
|
|
1008
|
+
{
|
|
1009
|
+
corner: "se",
|
|
1010
|
+
startW: embed.width,
|
|
1011
|
+
startH: embed.height,
|
|
1012
|
+
imgRef,
|
|
1013
|
+
onCommit,
|
|
1014
|
+
onLiveChange: measure
|
|
1015
|
+
}
|
|
1016
|
+
)
|
|
1017
|
+
]
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
function MoveHandle({
|
|
1022
|
+
layout,
|
|
1023
|
+
startOffsetX,
|
|
1024
|
+
startOffsetY,
|
|
1025
|
+
imgRef,
|
|
1026
|
+
onCommit,
|
|
1027
|
+
onLiveChange
|
|
1028
|
+
}) {
|
|
1029
|
+
const dragRef = (0, import_react5.useRef)(null);
|
|
1030
|
+
const onPointerDown = (e) => {
|
|
1031
|
+
if (e.button !== 0) return;
|
|
1032
|
+
if (!imgRef.current) return;
|
|
1033
|
+
e.preventDefault();
|
|
1034
|
+
e.stopPropagation();
|
|
1035
|
+
e.target.setPointerCapture(e.pointerId);
|
|
1036
|
+
dragRef.current = {
|
|
1037
|
+
startX: e.clientX,
|
|
1038
|
+
startY: e.clientY,
|
|
1039
|
+
liveOX: startOffsetX,
|
|
1040
|
+
liveOY: startOffsetY
|
|
1041
|
+
};
|
|
1042
|
+
};
|
|
1043
|
+
const onPointerMove = (e) => {
|
|
1044
|
+
const d = dragRef.current;
|
|
1045
|
+
if (!d || !imgRef.current) return;
|
|
1046
|
+
const dx = e.clientX - d.startX;
|
|
1047
|
+
const dy = e.clientY - d.startY;
|
|
1048
|
+
const img = imgRef.current;
|
|
1049
|
+
let nox = startOffsetX;
|
|
1050
|
+
let noy = startOffsetY;
|
|
1051
|
+
if (layout === "wrap-left") {
|
|
1052
|
+
nox = Math.max(8, startOffsetX + dx);
|
|
1053
|
+
noy = Math.max(0, startOffsetY + dy);
|
|
1054
|
+
img.style.marginRight = `${nox}px`;
|
|
1055
|
+
img.style.marginTop = `${noy}px`;
|
|
1056
|
+
} else if (layout === "wrap-right") {
|
|
1057
|
+
nox = Math.max(8, startOffsetX - dx);
|
|
1058
|
+
noy = Math.max(0, startOffsetY + dy);
|
|
1059
|
+
img.style.marginLeft = `${nox}px`;
|
|
1060
|
+
img.style.marginTop = `${noy}px`;
|
|
1061
|
+
} else if (layout === "behind" || layout === "front") {
|
|
1062
|
+
nox = startOffsetX + dx;
|
|
1063
|
+
noy = startOffsetY + dy;
|
|
1064
|
+
img.style.left = `${nox}px`;
|
|
1065
|
+
img.style.top = `${noy}px`;
|
|
1066
|
+
}
|
|
1067
|
+
d.liveOX = nox;
|
|
1068
|
+
d.liveOY = noy;
|
|
1069
|
+
onLiveChange();
|
|
1070
|
+
};
|
|
1071
|
+
const finish = (e) => {
|
|
1072
|
+
const d = dragRef.current;
|
|
1073
|
+
if (!d) return;
|
|
1074
|
+
dragRef.current = null;
|
|
1075
|
+
try {
|
|
1076
|
+
e.target.releasePointerCapture(e.pointerId);
|
|
1077
|
+
} catch {
|
|
1078
|
+
}
|
|
1079
|
+
onCommit(Math.round(d.liveOX), Math.round(d.liveOY));
|
|
1080
|
+
};
|
|
1081
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1082
|
+
"div",
|
|
1083
|
+
{
|
|
1084
|
+
className: "ed-image-move",
|
|
1085
|
+
onPointerDown,
|
|
1086
|
+
onPointerMove,
|
|
1087
|
+
onPointerUp: finish,
|
|
1088
|
+
onPointerCancel: finish
|
|
1089
|
+
}
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
var MIN_W = 20;
|
|
1093
|
+
function Handle({
|
|
1094
|
+
corner,
|
|
1095
|
+
startW,
|
|
1096
|
+
startH,
|
|
1097
|
+
imgRef,
|
|
1098
|
+
onCommit,
|
|
1099
|
+
onLiveChange
|
|
1100
|
+
}) {
|
|
1101
|
+
const dragRef = (0, import_react5.useRef)(null);
|
|
1102
|
+
const onPointerDown = (e) => {
|
|
1103
|
+
if (e.button !== 0) return;
|
|
1104
|
+
if (!imgRef.current) return;
|
|
1105
|
+
e.preventDefault();
|
|
1106
|
+
e.stopPropagation();
|
|
1107
|
+
e.target.setPointerCapture(e.pointerId);
|
|
1108
|
+
const aspect = startW > 0 && startH > 0 ? startW / startH : 1;
|
|
1109
|
+
dragRef.current = {
|
|
1110
|
+
startX: e.clientX,
|
|
1111
|
+
startY: e.clientY,
|
|
1112
|
+
startW,
|
|
1113
|
+
startH,
|
|
1114
|
+
aspect,
|
|
1115
|
+
liveW: startW,
|
|
1116
|
+
liveH: startH
|
|
1117
|
+
};
|
|
1118
|
+
};
|
|
1119
|
+
const onPointerMove = (e) => {
|
|
1120
|
+
const d = dragRef.current;
|
|
1121
|
+
if (!d || !imgRef.current) return;
|
|
1122
|
+
const dxRaw = e.clientX - d.startX;
|
|
1123
|
+
const dx = corner === "se" || corner === "ne" ? dxRaw : -dxRaw;
|
|
1124
|
+
let w = Math.max(MIN_W, d.startW + dx);
|
|
1125
|
+
let h = w / d.aspect;
|
|
1126
|
+
imgRef.current.style.width = `${w}px`;
|
|
1127
|
+
imgRef.current.style.height = `${h}px`;
|
|
1128
|
+
imgRef.current.width = Math.round(w);
|
|
1129
|
+
imgRef.current.height = Math.round(h);
|
|
1130
|
+
d.liveW = w;
|
|
1131
|
+
d.liveH = h;
|
|
1132
|
+
onLiveChange();
|
|
1133
|
+
};
|
|
1134
|
+
const finish = (e) => {
|
|
1135
|
+
const d = dragRef.current;
|
|
1136
|
+
if (!d) return;
|
|
1137
|
+
dragRef.current = null;
|
|
1138
|
+
try {
|
|
1139
|
+
e.target.releasePointerCapture(e.pointerId);
|
|
1140
|
+
} catch {
|
|
1141
|
+
}
|
|
1142
|
+
onCommit(Math.round(d.liveW), Math.round(d.liveH));
|
|
1143
|
+
};
|
|
1144
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1145
|
+
"div",
|
|
1146
|
+
{
|
|
1147
|
+
className: `ed-image-handle ${corner}`,
|
|
1148
|
+
onPointerDown,
|
|
1149
|
+
onPointerMove,
|
|
1150
|
+
onPointerUp: finish,
|
|
1151
|
+
onPointerCancel: finish
|
|
1152
|
+
}
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// src/NodeView.tsx
|
|
1157
|
+
var import_react8 = require("react");
|
|
1158
|
+
var import_core3 = require("@sofereditor/core");
|
|
1159
|
+
|
|
1160
|
+
// src/renderInline.tsx
|
|
1161
|
+
var import_react6 = require("react");
|
|
1162
|
+
var import_core2 = require("@sofereditor/core");
|
|
1163
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1164
|
+
function renderInline(delta, keyPrefix) {
|
|
1165
|
+
if (delta.length === 0 || delta.length === 1 && typeof delta[0].insert === "string" && delta[0].insert === "") {
|
|
1166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("br", { "data-empty": "true" });
|
|
1167
|
+
}
|
|
1168
|
+
let offset = 0;
|
|
1169
|
+
const wrapAnchors = [];
|
|
1170
|
+
const body = [];
|
|
1171
|
+
for (const op of delta) {
|
|
1172
|
+
if (typeof op.insert === "string") {
|
|
1173
|
+
const text = op.insert;
|
|
1174
|
+
if (text.length === 0) continue;
|
|
1175
|
+
const key = `${keyPrefix}-${offset}`;
|
|
1176
|
+
body.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react6.Fragment, { children: wrap(text, op.attributes) }, key));
|
|
1177
|
+
offset += text.length;
|
|
1178
|
+
} else if ((0, import_core2.isImageEmbed)(op.insert)) {
|
|
1179
|
+
const key = `${keyPrefix}-${offset}`;
|
|
1180
|
+
const layout = op.insert.layout ?? "inline";
|
|
1181
|
+
if (layout === "wrap-left" || layout === "wrap-right") {
|
|
1182
|
+
wrapAnchors.push(
|
|
1183
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ImageEmbedView, { embed: op.insert, offset, wrapAnchor: true }, `${key}-anchor`)
|
|
1184
|
+
);
|
|
1185
|
+
body.push(
|
|
1186
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1187
|
+
"span",
|
|
1188
|
+
{
|
|
1189
|
+
"data-embed-phantom": "true",
|
|
1190
|
+
"data-embed-offset": offset,
|
|
1191
|
+
contentEditable: false,
|
|
1192
|
+
style: PHANTOM_STYLE
|
|
1193
|
+
},
|
|
1194
|
+
`${key}-phantom`
|
|
1195
|
+
)
|
|
1196
|
+
);
|
|
1197
|
+
} else {
|
|
1198
|
+
body.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ImageEmbedView, { embed: op.insert, offset }, key));
|
|
1199
|
+
}
|
|
1200
|
+
offset += 1;
|
|
1201
|
+
} else if (op.insert != null) {
|
|
1202
|
+
offset += 1;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
if (wrapAnchors.length === 0 && body.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("br", { "data-empty": "true" });
|
|
1206
|
+
return [...wrapAnchors, ...body];
|
|
1207
|
+
}
|
|
1208
|
+
var PHANTOM_STYLE = {
|
|
1209
|
+
display: "inline-block",
|
|
1210
|
+
width: 0,
|
|
1211
|
+
height: 0,
|
|
1212
|
+
overflow: "hidden",
|
|
1213
|
+
verticalAlign: "baseline"
|
|
1214
|
+
};
|
|
1215
|
+
function imageEmbedPropsEqual(a, b) {
|
|
1216
|
+
if (a.offset !== b.offset || a.wrapAnchor !== b.wrapAnchor) return false;
|
|
1217
|
+
const x = a.embed;
|
|
1218
|
+
const y = b.embed;
|
|
1219
|
+
if (x === y) return true;
|
|
1220
|
+
return x.width === y.width && x.height === y.height && x.layout === y.layout && x.align === y.align && x.offsetX === y.offsetX && x.offsetY === y.offsetY && x.caption === y.caption && x.captionAlign === y.captionAlign && // src is the expensive compare — only fall back to ref equality. Fresh
|
|
1221
|
+
// toDelta() objects share the same string reference for embed payloads
|
|
1222
|
+
// because Y.js stores the embed in its content map and re-exposes it.
|
|
1223
|
+
x.src === y.src;
|
|
1224
|
+
}
|
|
1225
|
+
var ImageEmbedView = (0, import_react6.memo)(ImageEmbedViewImpl, imageEmbedPropsEqual);
|
|
1226
|
+
function ImageEmbedViewImpl({
|
|
1227
|
+
embed,
|
|
1228
|
+
offset,
|
|
1229
|
+
wrapAnchor
|
|
1230
|
+
}) {
|
|
1231
|
+
const layout = embed.layout ?? "inline";
|
|
1232
|
+
const align = embed.align;
|
|
1233
|
+
const ox = embed.offsetX ?? 0;
|
|
1234
|
+
const oy = embed.offsetY ?? 0;
|
|
1235
|
+
const hasCaption = !!embed.caption && embed.caption.length > 0;
|
|
1236
|
+
let outerStyle;
|
|
1237
|
+
switch (layout) {
|
|
1238
|
+
case "wrap-left":
|
|
1239
|
+
outerStyle = {
|
|
1240
|
+
float: "left",
|
|
1241
|
+
width: embed.width,
|
|
1242
|
+
marginRight: Math.max(8, ox),
|
|
1243
|
+
marginTop: Math.max(0, oy),
|
|
1244
|
+
marginBottom: 4
|
|
1245
|
+
};
|
|
1246
|
+
break;
|
|
1247
|
+
case "wrap-right":
|
|
1248
|
+
outerStyle = {
|
|
1249
|
+
float: "right",
|
|
1250
|
+
width: embed.width,
|
|
1251
|
+
marginLeft: Math.max(8, ox),
|
|
1252
|
+
marginTop: Math.max(0, oy),
|
|
1253
|
+
marginBottom: 4
|
|
1254
|
+
};
|
|
1255
|
+
break;
|
|
1256
|
+
case "behind":
|
|
1257
|
+
case "front":
|
|
1258
|
+
outerStyle = {
|
|
1259
|
+
position: "absolute",
|
|
1260
|
+
left: ox,
|
|
1261
|
+
top: oy,
|
|
1262
|
+
width: embed.width,
|
|
1263
|
+
zIndex: layout === "behind" ? -1 : 2
|
|
1264
|
+
};
|
|
1265
|
+
break;
|
|
1266
|
+
case "inline":
|
|
1267
|
+
default:
|
|
1268
|
+
outerStyle = align ? {
|
|
1269
|
+
display: "block",
|
|
1270
|
+
width: embed.width,
|
|
1271
|
+
marginLeft: align === "left" ? 0 : "auto",
|
|
1272
|
+
marginRight: align === "right" ? 0 : "auto"
|
|
1273
|
+
} : {
|
|
1274
|
+
display: "inline-block",
|
|
1275
|
+
verticalAlign: "text-bottom",
|
|
1276
|
+
width: embed.width
|
|
1277
|
+
};
|
|
1278
|
+
break;
|
|
1279
|
+
}
|
|
1280
|
+
const imgStyle = hasCaption ? { width: embed.width, height: embed.height, display: "block" } : outerStyle;
|
|
1281
|
+
const img = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1282
|
+
"img",
|
|
1283
|
+
{
|
|
1284
|
+
src: embed.src,
|
|
1285
|
+
width: embed.width,
|
|
1286
|
+
height: embed.height,
|
|
1287
|
+
contentEditable: false,
|
|
1288
|
+
draggable: false,
|
|
1289
|
+
"data-embed": "image",
|
|
1290
|
+
"data-embed-offset": offset,
|
|
1291
|
+
"data-embed-align": align ?? "",
|
|
1292
|
+
"data-embed-layout": layout,
|
|
1293
|
+
"data-wrap-anchor": wrapAnchor ? "true" : void 0,
|
|
1294
|
+
style: imgStyle,
|
|
1295
|
+
alt: ""
|
|
1296
|
+
}
|
|
1297
|
+
);
|
|
1298
|
+
if (!hasCaption) return img;
|
|
1299
|
+
const captionAlign = embed.captionAlign ?? "center";
|
|
1300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1301
|
+
"figure",
|
|
1302
|
+
{
|
|
1303
|
+
className: "ed-figure",
|
|
1304
|
+
contentEditable: false,
|
|
1305
|
+
"data-embed-figure": "true",
|
|
1306
|
+
"data-embed-layout": layout,
|
|
1307
|
+
style: outerStyle,
|
|
1308
|
+
children: [
|
|
1309
|
+
img,
|
|
1310
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1311
|
+
"figcaption",
|
|
1312
|
+
{
|
|
1313
|
+
className: "ed-figcaption",
|
|
1314
|
+
"data-caption-align": captionAlign,
|
|
1315
|
+
style: { textAlign: captionAlign },
|
|
1316
|
+
children: embed.caption
|
|
1317
|
+
}
|
|
1318
|
+
)
|
|
1319
|
+
]
|
|
1320
|
+
}
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
function wrap(text, attrs) {
|
|
1324
|
+
let node = text;
|
|
1325
|
+
if (!attrs) return node;
|
|
1326
|
+
if (attrs.bold) node = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: node });
|
|
1327
|
+
if (attrs.italic) node = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("em", { children: node });
|
|
1328
|
+
if (attrs.underline) node = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("u", { children: node });
|
|
1329
|
+
if (attrs.strike) node = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("s", { children: node });
|
|
1330
|
+
const styleParts = {};
|
|
1331
|
+
if (attrs.color) styleParts.color = attrs.color;
|
|
1332
|
+
if (attrs.fontFamily) styleParts.fontFamily = attrs.fontFamily;
|
|
1333
|
+
if (attrs.fontSize) styleParts.fontSize = attrs.fontSize;
|
|
1334
|
+
if (Object.keys(styleParts).length > 0) {
|
|
1335
|
+
node = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: styleParts, children: node });
|
|
1336
|
+
}
|
|
1337
|
+
if (attrs.link?.href) {
|
|
1338
|
+
node = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1339
|
+
"a",
|
|
1340
|
+
{
|
|
1341
|
+
href: attrs.link.href,
|
|
1342
|
+
title: attrs.link.title,
|
|
1343
|
+
rel: "noopener noreferrer",
|
|
1344
|
+
target: "_blank",
|
|
1345
|
+
onClick: (e) => {
|
|
1346
|
+
if (!(e.metaKey || e.ctrlKey)) e.preventDefault();
|
|
1347
|
+
},
|
|
1348
|
+
children: node
|
|
1349
|
+
}
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
if (attrs.comment?.markId) {
|
|
1353
|
+
node = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "data-comment-id": attrs.comment.markId, className: "ed-comment", children: node });
|
|
1354
|
+
}
|
|
1355
|
+
return node;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// src/sliceDelta.ts
|
|
1359
|
+
function deltaLength(delta) {
|
|
1360
|
+
let sum = 0;
|
|
1361
|
+
for (const op of delta) {
|
|
1362
|
+
if (typeof op.insert === "string") sum += op.insert.length;
|
|
1363
|
+
else if (op.insert != null) sum += 1;
|
|
1364
|
+
}
|
|
1365
|
+
return sum;
|
|
1366
|
+
}
|
|
1367
|
+
function sliceDelta(delta, start, end) {
|
|
1368
|
+
if (end <= start) return [];
|
|
1369
|
+
const out = [];
|
|
1370
|
+
let cursor = 0;
|
|
1371
|
+
for (const op of delta) {
|
|
1372
|
+
if (typeof op.insert === "string") {
|
|
1373
|
+
const opStart = cursor;
|
|
1374
|
+
const opEnd = cursor + op.insert.length;
|
|
1375
|
+
cursor = opEnd;
|
|
1376
|
+
if (opEnd <= start) continue;
|
|
1377
|
+
if (opStart >= end) break;
|
|
1378
|
+
const sliceFrom = Math.max(opStart, start) - opStart;
|
|
1379
|
+
const sliceTo = Math.min(opEnd, end) - opStart;
|
|
1380
|
+
out.push({
|
|
1381
|
+
insert: op.insert.slice(sliceFrom, sliceTo),
|
|
1382
|
+
attributes: op.attributes
|
|
1383
|
+
});
|
|
1384
|
+
} else if (op.insert != null) {
|
|
1385
|
+
const opStart = cursor;
|
|
1386
|
+
const opEnd = cursor + 1;
|
|
1387
|
+
cursor = opEnd;
|
|
1388
|
+
if (opEnd <= start) continue;
|
|
1389
|
+
if (opStart >= end) break;
|
|
1390
|
+
out.push({ insert: op.insert, attributes: op.attributes });
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
return out;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// src/TableResizeOverlay.tsx
|
|
1397
|
+
var import_react7 = require("react");
|
|
1398
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1399
|
+
function TableResizeOverlay({ tableRef, blockIndex, cols }) {
|
|
1400
|
+
const editor = useEditorContext();
|
|
1401
|
+
const [positions, setPositions] = (0, import_react7.useState)(null);
|
|
1402
|
+
const [layout, setLayout] = (0, import_react7.useState)(null);
|
|
1403
|
+
const draggingRef = (0, import_react7.useRef)(null);
|
|
1404
|
+
const measure = (0, import_react7.useCallback)(() => {
|
|
1405
|
+
const table = tableRef.current;
|
|
1406
|
+
if (!table) return;
|
|
1407
|
+
const tableRect = table.getBoundingClientRect();
|
|
1408
|
+
const cgCols = table.querySelectorAll("colgroup > col");
|
|
1409
|
+
let rights = [];
|
|
1410
|
+
if (cgCols.length === cols) {
|
|
1411
|
+
let acc = 0;
|
|
1412
|
+
cgCols.forEach((c) => {
|
|
1413
|
+
acc += c.getBoundingClientRect().width;
|
|
1414
|
+
rights.push(acc);
|
|
1415
|
+
});
|
|
1416
|
+
} else {
|
|
1417
|
+
const firstRowCells = table.querySelectorAll("tbody > tr:first-child > td");
|
|
1418
|
+
let acc = 0;
|
|
1419
|
+
firstRowCells.forEach((td) => {
|
|
1420
|
+
const cs = Math.max(1, td.colSpan || 1);
|
|
1421
|
+
const w = td.getBoundingClientRect().width / cs;
|
|
1422
|
+
for (let i = 0; i < cs; i++) {
|
|
1423
|
+
acc += w;
|
|
1424
|
+
rights.push(acc);
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
if (rights.length !== cols) {
|
|
1429
|
+
setLayout(null);
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
setLayout({ rights, height: tableRect.height });
|
|
1433
|
+
}, [cols, tableRef]);
|
|
1434
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
1435
|
+
measure();
|
|
1436
|
+
}, [measure]);
|
|
1437
|
+
(0, import_react7.useEffect)(() => {
|
|
1438
|
+
const table = tableRef.current;
|
|
1439
|
+
if (!table) return;
|
|
1440
|
+
const ro = new ResizeObserver(() => measure());
|
|
1441
|
+
ro.observe(table);
|
|
1442
|
+
return () => ro.disconnect();
|
|
1443
|
+
}, [measure, tableRef]);
|
|
1444
|
+
const onPointerDown = (0, import_react7.useCallback)(
|
|
1445
|
+
(col) => (e) => {
|
|
1446
|
+
e.preventDefault();
|
|
1447
|
+
e.stopPropagation();
|
|
1448
|
+
const table = tableRef.current;
|
|
1449
|
+
if (!table) return;
|
|
1450
|
+
const cgCols = table.querySelectorAll("colgroup > col");
|
|
1451
|
+
const startWidth = cgCols[col]?.getBoundingClientRect().width ?? 100;
|
|
1452
|
+
draggingRef.current = { col, startX: e.clientX, startWidth };
|
|
1453
|
+
setPositions({ x: e.clientX, height: layout?.height ?? 0 });
|
|
1454
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
1455
|
+
},
|
|
1456
|
+
[layout, tableRef]
|
|
1457
|
+
);
|
|
1458
|
+
const onPointerMove = (0, import_react7.useCallback)(
|
|
1459
|
+
(e) => {
|
|
1460
|
+
const d = draggingRef.current;
|
|
1461
|
+
if (!d) return;
|
|
1462
|
+
const dx = e.clientX - d.startX;
|
|
1463
|
+
const next = Math.max(40, d.startWidth + dx);
|
|
1464
|
+
editor.setColumnWidth(blockIndex, d.col, next);
|
|
1465
|
+
},
|
|
1466
|
+
[blockIndex, editor]
|
|
1467
|
+
);
|
|
1468
|
+
const onPointerUp = (0, import_react7.useCallback)((e) => {
|
|
1469
|
+
draggingRef.current = null;
|
|
1470
|
+
setPositions(null);
|
|
1471
|
+
try {
|
|
1472
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
1473
|
+
} catch {
|
|
1474
|
+
}
|
|
1475
|
+
}, []);
|
|
1476
|
+
if (!layout) return null;
|
|
1477
|
+
const handleStyle = (x) => ({
|
|
1478
|
+
position: "absolute",
|
|
1479
|
+
top: 0,
|
|
1480
|
+
left: `${x - 3}px`,
|
|
1481
|
+
width: "6px",
|
|
1482
|
+
height: `${layout.height}px`,
|
|
1483
|
+
cursor: "col-resize",
|
|
1484
|
+
zIndex: 5
|
|
1485
|
+
});
|
|
1486
|
+
void positions;
|
|
1487
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: layout.rights.map((x, c) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1488
|
+
"div",
|
|
1489
|
+
{
|
|
1490
|
+
contentEditable: false,
|
|
1491
|
+
className: "ed-col-resize-handle",
|
|
1492
|
+
style: handleStyle(x),
|
|
1493
|
+
onPointerDown: onPointerDown(c),
|
|
1494
|
+
onPointerMove,
|
|
1495
|
+
onPointerUp,
|
|
1496
|
+
"aria-label": `Redimensionar coluna ${c + 1}`,
|
|
1497
|
+
role: "separator"
|
|
1498
|
+
},
|
|
1499
|
+
c
|
|
1500
|
+
)) });
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// src/NodeView.tsx
|
|
1504
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1505
|
+
function NodeView({ block, index, children, fragment, tableFragment }) {
|
|
1506
|
+
if (block.type === "table") {
|
|
1507
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TableView, { block, index, tableFragment });
|
|
1508
|
+
}
|
|
1509
|
+
const inline = fragment ? renderInline(sliceDelta(block.delta, fragment.start, fragment.end), index) : renderInline(block.delta, index);
|
|
1510
|
+
const common = commonBlockProps(block.attrs, index, block.type, fragment);
|
|
1511
|
+
switch (block.type) {
|
|
1512
|
+
case "heading": {
|
|
1513
|
+
const level = clampLevel(block.attrs.level);
|
|
1514
|
+
const Tag = `h${level}`;
|
|
1515
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1516
|
+
Tag,
|
|
1517
|
+
{
|
|
1518
|
+
...common,
|
|
1519
|
+
className: `ed-block ed-heading ed-heading-${level}${alignClass(block.attrs.align)}${fragmentClass(fragment)}`,
|
|
1520
|
+
children: inline
|
|
1521
|
+
}
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
case "blockquote":
|
|
1525
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1526
|
+
"blockquote",
|
|
1527
|
+
{
|
|
1528
|
+
...common,
|
|
1529
|
+
className: `ed-block ed-blockquote${alignClass(block.attrs.align)}${fragmentClass(fragment)}`,
|
|
1530
|
+
children: inline
|
|
1531
|
+
}
|
|
1532
|
+
);
|
|
1533
|
+
case "codeBlock":
|
|
1534
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1535
|
+
"pre",
|
|
1536
|
+
{
|
|
1537
|
+
...common,
|
|
1538
|
+
"data-language": block.attrs.language ?? void 0,
|
|
1539
|
+
className: `ed-block ed-codeblock${alignClass(block.attrs.align)}`,
|
|
1540
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: inline })
|
|
1541
|
+
}
|
|
1542
|
+
);
|
|
1543
|
+
case "listItem": {
|
|
1544
|
+
const level = clampListLevel(block.attrs.listLevel);
|
|
1545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1546
|
+
"li",
|
|
1547
|
+
{
|
|
1548
|
+
...common,
|
|
1549
|
+
"data-list-kind": block.attrs.listKind ?? "bullet",
|
|
1550
|
+
"data-list-level": level,
|
|
1551
|
+
className: `ed-block ed-listitem${alignClass(block.attrs.align)}`,
|
|
1552
|
+
children: [
|
|
1553
|
+
inline,
|
|
1554
|
+
children
|
|
1555
|
+
]
|
|
1556
|
+
}
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
case "paragraph":
|
|
1560
|
+
default:
|
|
1561
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1562
|
+
"div",
|
|
1563
|
+
{
|
|
1564
|
+
...common,
|
|
1565
|
+
className: `ed-block ed-paragraph${alignClass(block.attrs.align)}${fragmentClass(fragment)}`,
|
|
1566
|
+
children: inline
|
|
1567
|
+
}
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
function commonBlockProps(attrs, index, type, fragment) {
|
|
1572
|
+
const style = {};
|
|
1573
|
+
if (attrs.align) style.textAlign = attrs.align;
|
|
1574
|
+
const base = {
|
|
1575
|
+
"data-block-index": index,
|
|
1576
|
+
"data-block-type": type,
|
|
1577
|
+
dir: attrs.dir,
|
|
1578
|
+
style: Object.keys(style).length > 0 ? style : void 0
|
|
1579
|
+
};
|
|
1580
|
+
if (!fragment) return base;
|
|
1581
|
+
base["data-fragment-index"] = fragment.index;
|
|
1582
|
+
base["data-fragment-start"] = fragment.start;
|
|
1583
|
+
base["data-fragment-end"] = fragment.end;
|
|
1584
|
+
return base;
|
|
1585
|
+
}
|
|
1586
|
+
function fragmentClass(f) {
|
|
1587
|
+
if (!f) return "";
|
|
1588
|
+
return ` ed-fragment ed-fragment-${f.index}`;
|
|
1589
|
+
}
|
|
1590
|
+
function alignClass(a) {
|
|
1591
|
+
return a ? ` ed-align-${a}` : "";
|
|
1592
|
+
}
|
|
1593
|
+
function clampLevel(l) {
|
|
1594
|
+
if (l === 2 || l === 3 || l === 4 || l === 5 || l === 6) return l;
|
|
1595
|
+
return 1;
|
|
1596
|
+
}
|
|
1597
|
+
function clampListLevel(l) {
|
|
1598
|
+
if (typeof l !== "number" || !Number.isFinite(l)) return 0;
|
|
1599
|
+
return Math.max(0, Math.min(5, Math.trunc(l)));
|
|
1600
|
+
}
|
|
1601
|
+
function TableView({ block, index, tableFragment }) {
|
|
1602
|
+
const rows = clampPositive(block.attrs.rows);
|
|
1603
|
+
const cols = clampPositive(block.attrs.cols);
|
|
1604
|
+
const cells = block.cells ?? [];
|
|
1605
|
+
const widths = readColWidths(block.attrs.colWidths, cols);
|
|
1606
|
+
const tableRef = (0, import_react8.useRef)(null);
|
|
1607
|
+
const rowStart = tableFragment?.rowStart ?? 0;
|
|
1608
|
+
const rowEnd = tableFragment?.rowEnd ?? rows;
|
|
1609
|
+
const isFirstFragment = !tableFragment || tableFragment.index === 0;
|
|
1610
|
+
const editor = useEditorContext();
|
|
1611
|
+
const rect = (0, import_react8.useMemo)(() => {
|
|
1612
|
+
if (editor.selection.anchor.blockIndex !== index) return null;
|
|
1613
|
+
if (editor.selection.focus.blockIndex !== index) return null;
|
|
1614
|
+
return (0, import_core3.tableRectSelection)(editor.doc, editor.selection);
|
|
1615
|
+
}, [editor.doc, editor.selection, index]);
|
|
1616
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ed-table-wrap", children: [
|
|
1617
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1618
|
+
"table",
|
|
1619
|
+
{
|
|
1620
|
+
ref: tableRef,
|
|
1621
|
+
"data-block-index": index,
|
|
1622
|
+
"data-block-type": "table",
|
|
1623
|
+
className: "ed-block ed-table",
|
|
1624
|
+
children: [
|
|
1625
|
+
widths && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("colgroup", { children: widths.map((w, c) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("col", { style: w != null ? { width: `${w}px` } : void 0 }, c)) }),
|
|
1626
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tbody", { children: Array.from({ length: rowEnd - rowStart }, (_, i) => {
|
|
1627
|
+
const r = rowStart + i;
|
|
1628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tr", { "data-cell-row": r, children: Array.from({ length: cols }, (_2, c) => {
|
|
1629
|
+
const flat = r * cols + c;
|
|
1630
|
+
const cell = cells[flat];
|
|
1631
|
+
if (cell?.attrs.covered === true) return null;
|
|
1632
|
+
const rowspan = spanAttr(cell?.attrs.rowspan);
|
|
1633
|
+
const colspan = spanAttr(cell?.attrs.colspan);
|
|
1634
|
+
const delta = cell?.delta ?? [];
|
|
1635
|
+
const inRect = rect !== null && r >= rect.top && r <= rect.bottom && c >= rect.left && c <= rect.right;
|
|
1636
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1637
|
+
"td",
|
|
1638
|
+
{
|
|
1639
|
+
"data-cell-index": flat,
|
|
1640
|
+
"data-cell-row": r,
|
|
1641
|
+
"data-cell-col": c,
|
|
1642
|
+
"data-cell-rowspan": rowspan,
|
|
1643
|
+
"data-cell-colspan": colspan,
|
|
1644
|
+
rowSpan: rowspan > 1 ? rowspan : void 0,
|
|
1645
|
+
colSpan: colspan > 1 ? colspan : void 0,
|
|
1646
|
+
className: inRect ? "ed-cell ed-cell--selected" : "ed-cell",
|
|
1647
|
+
children: renderInline(delta, `t${index}-c${flat}`)
|
|
1648
|
+
},
|
|
1649
|
+
c
|
|
1650
|
+
);
|
|
1651
|
+
}) }, r);
|
|
1652
|
+
}) })
|
|
1653
|
+
]
|
|
1654
|
+
}
|
|
1655
|
+
),
|
|
1656
|
+
isFirstFragment && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TableResizeOverlay, { tableRef, blockIndex: index, cols })
|
|
1657
|
+
] });
|
|
1658
|
+
}
|
|
1659
|
+
function readColWidths(raw, cols) {
|
|
1660
|
+
if (!Array.isArray(raw)) return null;
|
|
1661
|
+
const out = [];
|
|
1662
|
+
for (let i = 0; i < cols; i++) {
|
|
1663
|
+
const v = raw[i];
|
|
1664
|
+
out.push(typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null);
|
|
1665
|
+
}
|
|
1666
|
+
return out;
|
|
1667
|
+
}
|
|
1668
|
+
function spanAttr(v) {
|
|
1669
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 1) return 1;
|
|
1670
|
+
return Math.trunc(v);
|
|
1671
|
+
}
|
|
1672
|
+
function clampPositive(n) {
|
|
1673
|
+
if (typeof n !== "number" || !Number.isFinite(n) || n <= 0) return 1;
|
|
1674
|
+
return Math.trunc(n);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// src/usePagination.ts
|
|
1678
|
+
var import_react9 = require("react");
|
|
1679
|
+
var A4_PAGE = {
|
|
1680
|
+
width: 794,
|
|
1681
|
+
height: 1123,
|
|
1682
|
+
marginTop: 96,
|
|
1683
|
+
marginBottom: 96,
|
|
1684
|
+
marginLeft: 96,
|
|
1685
|
+
marginRight: 96
|
|
1686
|
+
};
|
|
1687
|
+
function contentHeight(g) {
|
|
1688
|
+
return g.height - g.marginTop - g.marginBottom;
|
|
1689
|
+
}
|
|
1690
|
+
function contentWidth(g) {
|
|
1691
|
+
return g.width - g.marginLeft - g.marginRight;
|
|
1692
|
+
}
|
|
1693
|
+
function defaultPageLayout(topLevelCount) {
|
|
1694
|
+
return {
|
|
1695
|
+
pages: [
|
|
1696
|
+
{
|
|
1697
|
+
slots: Array.from({ length: topLevelCount }, (_, i) => ({ topLevelIndex: i }))
|
|
1698
|
+
}
|
|
1699
|
+
]
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function usePageLayout(rootRef, geometry, topLevelCount, deps) {
|
|
1703
|
+
const [phase, setPhase] = (0, import_react9.useState)("measure");
|
|
1704
|
+
const [layout, setLayout] = (0, import_react9.useState)(() => defaultPageLayout(topLevelCount));
|
|
1705
|
+
const lastDepsRef = (0, import_react9.useRef)([]);
|
|
1706
|
+
const currentDeps = [...deps, topLevelCount, geometry];
|
|
1707
|
+
if (!shallowArrayEqual(lastDepsRef.current, currentDeps)) {
|
|
1708
|
+
lastDepsRef.current = currentDeps;
|
|
1709
|
+
if (phase !== "measure") setPhase("measure");
|
|
1710
|
+
const next = defaultPageLayout(topLevelCount);
|
|
1711
|
+
if (!layoutsDeepEqual(layout, next)) setLayout(next);
|
|
1712
|
+
}
|
|
1713
|
+
(0, import_react9.useLayoutEffect)(() => {
|
|
1714
|
+
if (phase !== "measure") return;
|
|
1715
|
+
const root = rootRef.current;
|
|
1716
|
+
if (!root) return;
|
|
1717
|
+
if (!isUnfragmented(layout)) return;
|
|
1718
|
+
const next = computeLayout(root, geometry, topLevelCount);
|
|
1719
|
+
if (!layoutsDeepEqual(layout, next)) setLayout(next);
|
|
1720
|
+
setPhase("stable");
|
|
1721
|
+
}, [phase, layout, geometry, topLevelCount, rootRef]);
|
|
1722
|
+
return layout;
|
|
1723
|
+
}
|
|
1724
|
+
function isUnfragmented(layout) {
|
|
1725
|
+
return layout.pages.length === 1 && layout.pages[0].slots.every(
|
|
1726
|
+
(s) => s.fragment === void 0 && s.tableFragment === void 0
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
function computeLayout(root, geometry, topLevelCount) {
|
|
1730
|
+
const tops = collectTopLevelElements(root);
|
|
1731
|
+
if (tops.length === 0) return defaultPageLayout(topLevelCount);
|
|
1732
|
+
const limit = contentHeight(geometry);
|
|
1733
|
+
const pages = [{ slots: [] }];
|
|
1734
|
+
let currentPage = pages[0];
|
|
1735
|
+
let used = 0;
|
|
1736
|
+
const cap = Math.min(tops.length, topLevelCount);
|
|
1737
|
+
for (let i = 0; i < cap; i++) {
|
|
1738
|
+
const el = tops[i];
|
|
1739
|
+
const h = elTotalHeight(el);
|
|
1740
|
+
if (used + h <= limit) {
|
|
1741
|
+
currentPage.slots.push({ topLevelIndex: i });
|
|
1742
|
+
used += h;
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
if (isFragmentable(el)) {
|
|
1746
|
+
const result = placeFragmentedBlock(el, i, pages, used, limit);
|
|
1747
|
+
currentPage = pages[pages.length - 1];
|
|
1748
|
+
used = result.lastUsed;
|
|
1749
|
+
continue;
|
|
1750
|
+
}
|
|
1751
|
+
if (isTableTopLevel(el)) {
|
|
1752
|
+
const result = placeFragmentedTable(el, i, pages, used, limit);
|
|
1753
|
+
currentPage = pages[pages.length - 1];
|
|
1754
|
+
used = result.lastUsed;
|
|
1755
|
+
continue;
|
|
1756
|
+
}
|
|
1757
|
+
if (currentPage.slots.length === 0) {
|
|
1758
|
+
currentPage.slots.push({ topLevelIndex: i });
|
|
1759
|
+
pages.push({ slots: [] });
|
|
1760
|
+
currentPage = pages[pages.length - 1];
|
|
1761
|
+
used = 0;
|
|
1762
|
+
} else {
|
|
1763
|
+
pages.push({ slots: [] });
|
|
1764
|
+
currentPage = pages[pages.length - 1];
|
|
1765
|
+
currentPage.slots.push({ topLevelIndex: i });
|
|
1766
|
+
used = h;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
if (pages.length > 1 && pages[pages.length - 1].slots.length === 0) pages.pop();
|
|
1770
|
+
return { pages };
|
|
1771
|
+
}
|
|
1772
|
+
function placeFragmentedBlock(el, topLevelIndex, pages, startUsed, limit) {
|
|
1773
|
+
const totalChars = getBlockTextLength(el);
|
|
1774
|
+
if (totalChars === 0) {
|
|
1775
|
+
const h = elTotalHeight(el);
|
|
1776
|
+
let currentPage2 = pages[pages.length - 1];
|
|
1777
|
+
if (startUsed + h > limit && currentPage2.slots.length > 0) {
|
|
1778
|
+
pages.push({ slots: [] });
|
|
1779
|
+
currentPage2 = pages[pages.length - 1];
|
|
1780
|
+
startUsed = 0;
|
|
1781
|
+
}
|
|
1782
|
+
currentPage2.slots.push({ topLevelIndex });
|
|
1783
|
+
return { lastUsed: startUsed + h };
|
|
1784
|
+
}
|
|
1785
|
+
const lines = measureLines(el);
|
|
1786
|
+
if (lines.length === 0) {
|
|
1787
|
+
const h = elTotalHeight(el);
|
|
1788
|
+
let currentPage2 = pages[pages.length - 1];
|
|
1789
|
+
if (startUsed + h > limit && currentPage2.slots.length > 0) {
|
|
1790
|
+
pages.push({ slots: [] });
|
|
1791
|
+
currentPage2 = pages[pages.length - 1];
|
|
1792
|
+
startUsed = 0;
|
|
1793
|
+
}
|
|
1794
|
+
currentPage2.slots.push({ topLevelIndex });
|
|
1795
|
+
return { lastUsed: startUsed + h };
|
|
1796
|
+
}
|
|
1797
|
+
let lineIdx = 0;
|
|
1798
|
+
let currentPage = pages[pages.length - 1];
|
|
1799
|
+
let used = startUsed;
|
|
1800
|
+
let fragIndex = 0;
|
|
1801
|
+
while (lineIdx < lines.length) {
|
|
1802
|
+
const available = Math.max(0, limit - used);
|
|
1803
|
+
if (available <= 0) {
|
|
1804
|
+
pages.push({ slots: [] });
|
|
1805
|
+
currentPage = pages[pages.length - 1];
|
|
1806
|
+
used = 0;
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
const startTop = lines[lineIdx].top;
|
|
1810
|
+
let bestK = -1;
|
|
1811
|
+
for (let k = lineIdx; k < lines.length; k++) {
|
|
1812
|
+
const h = lines[k].bottom - startTop;
|
|
1813
|
+
if (h <= available) bestK = k;
|
|
1814
|
+
else break;
|
|
1815
|
+
}
|
|
1816
|
+
if (bestK < 0) {
|
|
1817
|
+
if (currentPage.slots.length === 0) {
|
|
1818
|
+
bestK = lineIdx;
|
|
1819
|
+
} else {
|
|
1820
|
+
pages.push({ slots: [] });
|
|
1821
|
+
currentPage = pages[pages.length - 1];
|
|
1822
|
+
used = 0;
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
const fragStart = lines[lineIdx].start;
|
|
1827
|
+
const fragEnd = bestK + 1 < lines.length ? lines[bestK + 1].start : totalChars;
|
|
1828
|
+
const fragHeight = lines[bestK].bottom - startTop;
|
|
1829
|
+
currentPage.slots.push({
|
|
1830
|
+
topLevelIndex,
|
|
1831
|
+
fragment: { index: fragIndex, start: fragStart, end: fragEnd }
|
|
1832
|
+
});
|
|
1833
|
+
used += fragHeight;
|
|
1834
|
+
fragIndex++;
|
|
1835
|
+
lineIdx = bestK + 1;
|
|
1836
|
+
if (lineIdx < lines.length) {
|
|
1837
|
+
pages.push({ slots: [] });
|
|
1838
|
+
currentPage = pages[pages.length - 1];
|
|
1839
|
+
used = 0;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
return { lastUsed: used };
|
|
1843
|
+
}
|
|
1844
|
+
var lineMetricsCache = /* @__PURE__ */ new Map();
|
|
1845
|
+
var LINE_METRICS_CACHE_LIMIT = 200;
|
|
1846
|
+
function measureLines(el) {
|
|
1847
|
+
const text = el.textContent ?? "";
|
|
1848
|
+
const cacheKey = `${el.offsetWidth}|${text}`;
|
|
1849
|
+
const cached = lineMetricsCache.get(cacheKey);
|
|
1850
|
+
if (cached) {
|
|
1851
|
+
lineMetricsCache.delete(cacheKey);
|
|
1852
|
+
lineMetricsCache.set(cacheKey, cached);
|
|
1853
|
+
return cached;
|
|
1854
|
+
}
|
|
1855
|
+
const fresh = measureLinesUncached(el);
|
|
1856
|
+
lineMetricsCache.set(cacheKey, fresh);
|
|
1857
|
+
if (lineMetricsCache.size > LINE_METRICS_CACHE_LIMIT) {
|
|
1858
|
+
const oldest = lineMetricsCache.keys().next().value;
|
|
1859
|
+
if (oldest !== void 0) lineMetricsCache.delete(oldest);
|
|
1860
|
+
}
|
|
1861
|
+
return fresh;
|
|
1862
|
+
}
|
|
1863
|
+
function measureLinesUncached(el) {
|
|
1864
|
+
const range = document.createRange();
|
|
1865
|
+
const lines = [];
|
|
1866
|
+
const walker = document.createTreeWalker(
|
|
1867
|
+
el,
|
|
1868
|
+
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,
|
|
1869
|
+
blockBoundaryFilter(el)
|
|
1870
|
+
);
|
|
1871
|
+
let n;
|
|
1872
|
+
let charOffset = 0;
|
|
1873
|
+
while (n = walker.nextNode()) {
|
|
1874
|
+
if (n.nodeType === Node.TEXT_NODE) {
|
|
1875
|
+
const text = n.textContent ?? "";
|
|
1876
|
+
for (let i = 0; i < text.length; i++) {
|
|
1877
|
+
try {
|
|
1878
|
+
range.setStart(n, i);
|
|
1879
|
+
range.setEnd(n, i + 1);
|
|
1880
|
+
} catch {
|
|
1881
|
+
continue;
|
|
1882
|
+
}
|
|
1883
|
+
const rect = range.getBoundingClientRect();
|
|
1884
|
+
if (rect.width === 0 && rect.height === 0) continue;
|
|
1885
|
+
const last = lines[lines.length - 1];
|
|
1886
|
+
if (!last || rect.top >= last.bottom) {
|
|
1887
|
+
lines.push({ start: charOffset + i, top: rect.top, bottom: rect.bottom });
|
|
1888
|
+
} else {
|
|
1889
|
+
last.top = Math.min(last.top, rect.top);
|
|
1890
|
+
last.bottom = Math.max(last.bottom, rect.bottom);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
charOffset += text.length;
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1896
|
+
if (n.nodeType === Node.ELEMENT_NODE && n.dataset.embed === "image") {
|
|
1897
|
+
const rect = n.getBoundingClientRect();
|
|
1898
|
+
if (rect.height > 0) {
|
|
1899
|
+
const last = lines[lines.length - 1];
|
|
1900
|
+
if (!last || rect.top >= last.bottom) {
|
|
1901
|
+
lines.push({ start: charOffset, top: rect.top, bottom: rect.bottom });
|
|
1902
|
+
} else {
|
|
1903
|
+
last.top = Math.min(last.top, rect.top);
|
|
1904
|
+
last.bottom = Math.max(last.bottom, rect.bottom);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
charOffset += 1;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
return lines;
|
|
1911
|
+
}
|
|
1912
|
+
function elTotalHeight(el) {
|
|
1913
|
+
const cs = getComputedStyle(el);
|
|
1914
|
+
const m = parseFloat(cs.marginTop || "0") + parseFloat(cs.marginBottom || "0");
|
|
1915
|
+
return el.offsetHeight + m;
|
|
1916
|
+
}
|
|
1917
|
+
function isFragmentable(el) {
|
|
1918
|
+
const type = el.getAttribute("data-block-type");
|
|
1919
|
+
if (type === "paragraph") return true;
|
|
1920
|
+
if (type === "heading") return true;
|
|
1921
|
+
if (type === "blockquote") return true;
|
|
1922
|
+
return false;
|
|
1923
|
+
}
|
|
1924
|
+
function isTableTopLevel(el) {
|
|
1925
|
+
return el.querySelector(':scope > table[data-block-type="table"]') !== null;
|
|
1926
|
+
}
|
|
1927
|
+
function placeFragmentedTable(el, topLevelIndex, pages, startUsed, limit) {
|
|
1928
|
+
const table = el.querySelector(':scope > table[data-block-type="table"]');
|
|
1929
|
+
const tbody = table?.querySelector(":scope > tbody");
|
|
1930
|
+
const trs = tbody ? Array.from(tbody.children) : [];
|
|
1931
|
+
if (trs.length === 0) {
|
|
1932
|
+
const h = elTotalHeight(el);
|
|
1933
|
+
let currentPage2 = pages[pages.length - 1];
|
|
1934
|
+
if (startUsed + h > limit && currentPage2.slots.length > 0) {
|
|
1935
|
+
pages.push({ slots: [] });
|
|
1936
|
+
currentPage2 = pages[pages.length - 1];
|
|
1937
|
+
startUsed = 0;
|
|
1938
|
+
}
|
|
1939
|
+
currentPage2.slots.push({ topLevelIndex });
|
|
1940
|
+
return { lastUsed: startUsed + h };
|
|
1941
|
+
}
|
|
1942
|
+
const rowHeights = trs.map((tr) => tr.getBoundingClientRect().height);
|
|
1943
|
+
const invalid = /* @__PURE__ */ new Set();
|
|
1944
|
+
for (let r = 0; r < trs.length; r++) {
|
|
1945
|
+
const tds = Array.from(trs[r].children);
|
|
1946
|
+
for (const td of tds) {
|
|
1947
|
+
const rs = td.rowSpan || 1;
|
|
1948
|
+
if (rs > 1) {
|
|
1949
|
+
for (let k = r; k <= r + rs - 2 && k < trs.length - 1; k++) {
|
|
1950
|
+
invalid.add(k);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
const wrapHeight = elTotalHeight(el);
|
|
1956
|
+
const tableHeight = table ? table.getBoundingClientRect().height : 0;
|
|
1957
|
+
const chrome = Math.max(0, wrapHeight - tableHeight);
|
|
1958
|
+
let rowIdx = 0;
|
|
1959
|
+
let used = startUsed;
|
|
1960
|
+
let fragIndex = 0;
|
|
1961
|
+
let currentPage = pages[pages.length - 1];
|
|
1962
|
+
while (rowIdx < trs.length) {
|
|
1963
|
+
const overhead = fragIndex === 0 ? chrome : 0;
|
|
1964
|
+
const available = Math.max(0, limit - used - overhead);
|
|
1965
|
+
if (available <= 0) {
|
|
1966
|
+
pages.push({ slots: [] });
|
|
1967
|
+
currentPage = pages[pages.length - 1];
|
|
1968
|
+
used = 0;
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
let bestEnd = -1;
|
|
1972
|
+
let accH = 0;
|
|
1973
|
+
for (let k = rowIdx; k < trs.length; k++) {
|
|
1974
|
+
accH += rowHeights[k];
|
|
1975
|
+
if (accH > available) break;
|
|
1976
|
+
if (k === trs.length - 1 || !invalid.has(k)) bestEnd = k + 1;
|
|
1977
|
+
}
|
|
1978
|
+
if (bestEnd < 0) {
|
|
1979
|
+
if (currentPage.slots.length === 0) {
|
|
1980
|
+
let k = rowIdx;
|
|
1981
|
+
while (k < trs.length - 1 && invalid.has(k)) k++;
|
|
1982
|
+
bestEnd = k + 1;
|
|
1983
|
+
} else {
|
|
1984
|
+
pages.push({ slots: [] });
|
|
1985
|
+
currentPage = pages[pages.length - 1];
|
|
1986
|
+
used = 0;
|
|
1987
|
+
continue;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
let fragHeight = 0;
|
|
1991
|
+
for (let k = rowIdx; k < bestEnd; k++) fragHeight += rowHeights[k];
|
|
1992
|
+
if (fragIndex === 0) fragHeight += chrome;
|
|
1993
|
+
currentPage.slots.push({
|
|
1994
|
+
topLevelIndex,
|
|
1995
|
+
tableFragment: { index: fragIndex, rowStart: rowIdx, rowEnd: bestEnd }
|
|
1996
|
+
});
|
|
1997
|
+
used += fragHeight;
|
|
1998
|
+
fragIndex++;
|
|
1999
|
+
rowIdx = bestEnd;
|
|
2000
|
+
if (rowIdx < trs.length) {
|
|
2001
|
+
pages.push({ slots: [] });
|
|
2002
|
+
currentPage = pages[pages.length - 1];
|
|
2003
|
+
used = 0;
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
return { lastUsed: used };
|
|
2007
|
+
}
|
|
2008
|
+
function collectTopLevelElements(root) {
|
|
2009
|
+
const pageContents = root.querySelectorAll(":scope > .ed-page > .ed-page-content");
|
|
2010
|
+
if (pageContents.length > 0) {
|
|
2011
|
+
const out = [];
|
|
2012
|
+
pageContents.forEach((pc) => {
|
|
2013
|
+
Array.from(pc.children).forEach((c) => out.push(c));
|
|
2014
|
+
});
|
|
2015
|
+
return out;
|
|
2016
|
+
}
|
|
2017
|
+
return Array.from(root.children);
|
|
2018
|
+
}
|
|
2019
|
+
function getBlockTextLength(el) {
|
|
2020
|
+
const walker = document.createTreeWalker(
|
|
2021
|
+
el,
|
|
2022
|
+
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,
|
|
2023
|
+
blockBoundaryFilter(el)
|
|
2024
|
+
);
|
|
2025
|
+
let n;
|
|
2026
|
+
let sum = 0;
|
|
2027
|
+
while (n = walker.nextNode()) {
|
|
2028
|
+
if (n.nodeType === Node.TEXT_NODE) {
|
|
2029
|
+
sum += (n.textContent ?? "").length;
|
|
2030
|
+
} else if (n.nodeType === Node.ELEMENT_NODE && n.dataset.embed === "image") {
|
|
2031
|
+
sum += 1;
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
return sum;
|
|
2035
|
+
}
|
|
2036
|
+
function blockBoundaryFilter(blockEl) {
|
|
2037
|
+
return {
|
|
2038
|
+
acceptNode(n) {
|
|
2039
|
+
let p = n.parentNode;
|
|
2040
|
+
while (p && p !== blockEl) {
|
|
2041
|
+
if (p.nodeType === Node.ELEMENT_NODE) {
|
|
2042
|
+
const e = p;
|
|
2043
|
+
if (e.dataset.blockIndex != null) return NodeFilter.FILTER_REJECT;
|
|
2044
|
+
}
|
|
2045
|
+
p = p.parentNode;
|
|
2046
|
+
}
|
|
2047
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
2048
|
+
}
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
function shallowArrayEqual(a, b) {
|
|
2052
|
+
if (a.length !== b.length) return false;
|
|
2053
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
2054
|
+
return true;
|
|
2055
|
+
}
|
|
2056
|
+
function layoutsDeepEqual(a, b) {
|
|
2057
|
+
if (a.pages.length !== b.pages.length) return false;
|
|
2058
|
+
for (let i = 0; i < a.pages.length; i++) {
|
|
2059
|
+
const sA = a.pages[i].slots;
|
|
2060
|
+
const sB = b.pages[i].slots;
|
|
2061
|
+
if (sA.length !== sB.length) return false;
|
|
2062
|
+
for (let j = 0; j < sA.length; j++) {
|
|
2063
|
+
if (sA[j].topLevelIndex !== sB[j].topLevelIndex) return false;
|
|
2064
|
+
const fA = sA[j].fragment;
|
|
2065
|
+
const fB = sB[j].fragment;
|
|
2066
|
+
if (fA || fB) {
|
|
2067
|
+
if (!fA || !fB) return false;
|
|
2068
|
+
if (fA.index !== fB.index || fA.start !== fB.start || fA.end !== fB.end) return false;
|
|
2069
|
+
}
|
|
2070
|
+
const tA = sA[j].tableFragment;
|
|
2071
|
+
const tB = sB[j].tableFragment;
|
|
2072
|
+
if (tA || tB) {
|
|
2073
|
+
if (!tA || !tB) return false;
|
|
2074
|
+
if (tA.index !== tB.index || tA.rowStart !== tB.rowStart || tA.rowEnd !== tB.rowEnd) {
|
|
2075
|
+
return false;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
return true;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// src/useEditor.ts
|
|
2084
|
+
var import_react11 = require("react");
|
|
2085
|
+
|
|
2086
|
+
// src/usePageSettings.ts
|
|
2087
|
+
var import_react10 = require("react");
|
|
2088
|
+
function usePageSettings(doc) {
|
|
2089
|
+
const cacheRef = (0, import_react10.useRef)(null);
|
|
2090
|
+
const subscribe = (0, import_react10.useCallback)(
|
|
2091
|
+
(notify) => doc.observePageSettings(notify),
|
|
2092
|
+
[doc]
|
|
2093
|
+
);
|
|
2094
|
+
const getSnapshot = (0, import_react10.useCallback)(() => {
|
|
2095
|
+
const next = doc.getPageSettings();
|
|
2096
|
+
const prev = cacheRef.current;
|
|
2097
|
+
if (prev && shallowEqual(prev, next)) return prev;
|
|
2098
|
+
cacheRef.current = next;
|
|
2099
|
+
return next;
|
|
2100
|
+
}, [doc]);
|
|
2101
|
+
return (0, import_react10.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
2102
|
+
}
|
|
2103
|
+
function shallowEqual(a, b) {
|
|
2104
|
+
return a.width === b.width && a.height === b.height && a.marginTop === b.marginTop && a.marginBottom === b.marginBottom && a.marginLeft === b.marginLeft && a.marginRight === b.marginRight && a.preset === b.preset;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
// src/useEditor.ts
|
|
2108
|
+
var import_core4 = require("@sofereditor/core");
|
|
2109
|
+
var MAX_INSERT_WIDTH = 600;
|
|
2110
|
+
function useEditor(opts = {}) {
|
|
2111
|
+
const doc = (0, import_react11.useMemo)(() => opts.document ?? new import_core4.EditorDocument(), [opts.document]);
|
|
2112
|
+
const history = (0, import_react11.useMemo)(() => new import_core4.EditorHistory(doc), [doc]);
|
|
2113
|
+
const [snapshot, setSnapshot] = (0, import_react11.useState)(() => doc.toJSON());
|
|
2114
|
+
const [selection, setSelectionState] = (0, import_react11.useState)(
|
|
2115
|
+
() => (0, import_core4.collapsedSelection)({ blockIndex: 0, offset: 0 })
|
|
2116
|
+
);
|
|
2117
|
+
const selectionRef = (0, import_react11.useRef)(selection);
|
|
2118
|
+
selectionRef.current = selection;
|
|
2119
|
+
const pendingMarksRef = (0, import_react11.useRef)({});
|
|
2120
|
+
const [pendingMarks, setPendingMarksState] = (0, import_react11.useState)({});
|
|
2121
|
+
const setPendingMarks = (0, import_react11.useCallback)((next) => {
|
|
2122
|
+
pendingMarksRef.current = next;
|
|
2123
|
+
setPendingMarksState(next);
|
|
2124
|
+
}, []);
|
|
2125
|
+
(0, import_react11.useEffect)(() => {
|
|
2126
|
+
let scheduled = false;
|
|
2127
|
+
let cancelled = false;
|
|
2128
|
+
let frame = 0;
|
|
2129
|
+
const flush = () => {
|
|
2130
|
+
scheduled = false;
|
|
2131
|
+
if (cancelled) return;
|
|
2132
|
+
setSnapshot(doc.toJSON());
|
|
2133
|
+
};
|
|
2134
|
+
const schedule = () => {
|
|
2135
|
+
if (scheduled) return;
|
|
2136
|
+
scheduled = true;
|
|
2137
|
+
if (typeof requestAnimationFrame === "function") {
|
|
2138
|
+
frame = requestAnimationFrame(flush);
|
|
2139
|
+
} else {
|
|
2140
|
+
queueMicrotask(flush);
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
doc.ydoc.on("afterTransaction", schedule);
|
|
2144
|
+
return () => {
|
|
2145
|
+
cancelled = true;
|
|
2146
|
+
if (scheduled && typeof requestAnimationFrame === "function" && typeof cancelAnimationFrame === "function") {
|
|
2147
|
+
cancelAnimationFrame(frame);
|
|
2148
|
+
}
|
|
2149
|
+
doc.ydoc.off("afterTransaction", schedule);
|
|
2150
|
+
};
|
|
2151
|
+
}, [doc]);
|
|
2152
|
+
const getSelection = (0, import_react11.useCallback)(() => selectionRef.current, []);
|
|
2153
|
+
const setSelection = (0, import_react11.useCallback)(
|
|
2154
|
+
(sel) => {
|
|
2155
|
+
const prev = selectionRef.current;
|
|
2156
|
+
selectionRef.current = sel;
|
|
2157
|
+
setSelectionState(sel);
|
|
2158
|
+
if (Object.keys(pendingMarksRef.current).length > 0 && (prev.focus.blockIndex !== sel.focus.blockIndex || prev.focus.offset !== sel.focus.offset)) {
|
|
2159
|
+
const advancedByOne = prev.focus.blockIndex === sel.focus.blockIndex && sel.focus.offset === prev.focus.offset + 1;
|
|
2160
|
+
if (!advancedByOne) {
|
|
2161
|
+
pendingMarksRef.current = {};
|
|
2162
|
+
setPendingMarksState({});
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
},
|
|
2166
|
+
[]
|
|
2167
|
+
);
|
|
2168
|
+
const ctxRef = (0, import_react11.useRef)({ doc, getSelection, setSelection });
|
|
2169
|
+
ctxRef.current = { doc, getSelection, setSelection };
|
|
2170
|
+
const consumePendingMarks = (0, import_react11.useCallback)(() => {
|
|
2171
|
+
const current = pendingMarksRef.current;
|
|
2172
|
+
if (Object.keys(current).length === 0) return void 0;
|
|
2173
|
+
pendingMarksRef.current = {};
|
|
2174
|
+
setPendingMarksState({});
|
|
2175
|
+
return current;
|
|
2176
|
+
}, []);
|
|
2177
|
+
const isMarkActive = (0, import_react11.useCallback)(
|
|
2178
|
+
(name, value) => {
|
|
2179
|
+
const sel = selectionRef.current;
|
|
2180
|
+
const { start, end } = (0, import_core4.orderedRange)(sel);
|
|
2181
|
+
if (start.blockIndex === end.blockIndex && start.offset === end.offset) {
|
|
2182
|
+
const pending = pendingMarksRef.current[name];
|
|
2183
|
+
if (pending !== void 0) {
|
|
2184
|
+
if (pending === null) return false;
|
|
2185
|
+
return value === void 0 ? true : sameValue(pending, value);
|
|
2186
|
+
}
|
|
2187
|
+
const yText2 = doc.getBlockText(start.blockIndex);
|
|
2188
|
+
if (!yText2 || start.offset === 0) return false;
|
|
2189
|
+
return (0, import_core4.isMarkUniformInRange)(yText2, start.offset - 1, start.offset, name, value);
|
|
2190
|
+
}
|
|
2191
|
+
if (start.blockIndex !== end.blockIndex) {
|
|
2192
|
+
for (let i = start.blockIndex; i <= end.blockIndex; i++) {
|
|
2193
|
+
const yText2 = doc.getBlockText(i);
|
|
2194
|
+
if (!yText2) return false;
|
|
2195
|
+
const s = i === start.blockIndex ? start.offset : 0;
|
|
2196
|
+
const e = i === end.blockIndex ? end.offset : yText2.length;
|
|
2197
|
+
if (s >= e) continue;
|
|
2198
|
+
if (!(0, import_core4.isMarkUniformInRange)(yText2, s, e, name, value)) return false;
|
|
2199
|
+
}
|
|
2200
|
+
return true;
|
|
2201
|
+
}
|
|
2202
|
+
const yText = doc.getBlockText(start.blockIndex);
|
|
2203
|
+
if (!yText) return false;
|
|
2204
|
+
return (0, import_core4.isMarkUniformInRange)(yText, start.offset, end.offset, name, value);
|
|
2205
|
+
},
|
|
2206
|
+
[doc]
|
|
2207
|
+
);
|
|
2208
|
+
const getActiveMarks = (0, import_react11.useCallback)(() => {
|
|
2209
|
+
const sel = selectionRef.current;
|
|
2210
|
+
const { start, end } = (0, import_core4.orderedRange)(sel);
|
|
2211
|
+
if (start.blockIndex !== end.blockIndex) {
|
|
2212
|
+
return {};
|
|
2213
|
+
}
|
|
2214
|
+
const yText = doc.getBlockText(start.blockIndex);
|
|
2215
|
+
if (!yText) return {};
|
|
2216
|
+
if (start.offset === end.offset) {
|
|
2217
|
+
if (start.offset === 0) return {};
|
|
2218
|
+
return (0, import_core4.getMarksInRange)(yText, start.offset - 1, start.offset);
|
|
2219
|
+
}
|
|
2220
|
+
return (0, import_core4.getMarksInRange)(yText, start.offset, end.offset);
|
|
2221
|
+
}, [doc]);
|
|
2222
|
+
const toggleMark = (0, import_react11.useCallback)(
|
|
2223
|
+
(name, value) => {
|
|
2224
|
+
const sel = selectionRef.current;
|
|
2225
|
+
const { start, end } = (0, import_core4.orderedRange)(sel);
|
|
2226
|
+
const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
|
|
2227
|
+
if (isCollapsed) {
|
|
2228
|
+
const next = { ...pendingMarksRef.current };
|
|
2229
|
+
const currentlyActive = isMarkActive(name, value);
|
|
2230
|
+
if (currentlyActive) {
|
|
2231
|
+
next[name] = null;
|
|
2232
|
+
} else {
|
|
2233
|
+
next[name] = value ?? (booleanMarks.has(name) ? true : null);
|
|
2234
|
+
}
|
|
2235
|
+
setPendingMarks(next);
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
(0, import_core4.toggleMark)(ctxRef.current, name, value);
|
|
2239
|
+
},
|
|
2240
|
+
[isMarkActive, setPendingMarks]
|
|
2241
|
+
);
|
|
2242
|
+
const setMark = (0, import_react11.useCallback)(
|
|
2243
|
+
(name, value) => {
|
|
2244
|
+
const sel = selectionRef.current;
|
|
2245
|
+
const { start, end } = (0, import_core4.orderedRange)(sel);
|
|
2246
|
+
const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
|
|
2247
|
+
if (isCollapsed) {
|
|
2248
|
+
setPendingMarks({ ...pendingMarksRef.current, [name]: value });
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
(0, import_core4.setMark)(ctxRef.current, name, value);
|
|
2252
|
+
},
|
|
2253
|
+
[setPendingMarks]
|
|
2254
|
+
);
|
|
2255
|
+
const removeMark = (0, import_react11.useCallback)(
|
|
2256
|
+
(name) => {
|
|
2257
|
+
const sel = selectionRef.current;
|
|
2258
|
+
const { start, end } = (0, import_core4.orderedRange)(sel);
|
|
2259
|
+
const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;
|
|
2260
|
+
if (isCollapsed) {
|
|
2261
|
+
setPendingMarks({ ...pendingMarksRef.current, [name]: null });
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
(0, import_core4.removeMark)(ctxRef.current, name);
|
|
2265
|
+
},
|
|
2266
|
+
[setPendingMarks]
|
|
2267
|
+
);
|
|
2268
|
+
const getBlockType = (0, import_react11.useCallback)(() => {
|
|
2269
|
+
const sel = selectionRef.current;
|
|
2270
|
+
const { start, end } = (0, import_core4.orderedRange)(sel);
|
|
2271
|
+
const first = doc.getBlockType(start.blockIndex);
|
|
2272
|
+
if (first === void 0) return "paragraph";
|
|
2273
|
+
for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {
|
|
2274
|
+
if (doc.getBlockType(i) !== first) return "mixed";
|
|
2275
|
+
}
|
|
2276
|
+
return first;
|
|
2277
|
+
}, [doc]);
|
|
2278
|
+
const getBlockAttr = (0, import_react11.useCallback)(
|
|
2279
|
+
(key) => {
|
|
2280
|
+
const sel = selectionRef.current;
|
|
2281
|
+
const { start, end } = (0, import_core4.orderedRange)(sel);
|
|
2282
|
+
const first = doc.getBlockAttrs(start.blockIndex)[key];
|
|
2283
|
+
for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {
|
|
2284
|
+
const next = doc.getBlockAttrs(i)[key];
|
|
2285
|
+
if (next !== first) return "mixed";
|
|
2286
|
+
}
|
|
2287
|
+
return first;
|
|
2288
|
+
},
|
|
2289
|
+
[doc]
|
|
2290
|
+
);
|
|
2291
|
+
const setBlockType = (0, import_react11.useCallback)((type, attrs) => {
|
|
2292
|
+
(0, import_core4.setBlockType)(ctxRef.current, type, attrs);
|
|
2293
|
+
}, []);
|
|
2294
|
+
const setBlockAttr = (0, import_react11.useCallback)(
|
|
2295
|
+
(key, value) => {
|
|
2296
|
+
(0, import_core4.setBlockAttr)(ctxRef.current, key, value);
|
|
2297
|
+
},
|
|
2298
|
+
[]
|
|
2299
|
+
);
|
|
2300
|
+
const isListActive = (0, import_react11.useCallback)(
|
|
2301
|
+
(kind) => {
|
|
2302
|
+
const sel = selectionRef.current;
|
|
2303
|
+
const { start, end } = (0, import_core4.orderedRange)(sel);
|
|
2304
|
+
for (let i = start.blockIndex; i <= end.blockIndex; i++) {
|
|
2305
|
+
if (doc.getBlockType(i) !== "listItem") return false;
|
|
2306
|
+
if (doc.getBlockAttrs(i).listKind !== kind) return false;
|
|
2307
|
+
}
|
|
2308
|
+
return true;
|
|
2309
|
+
},
|
|
2310
|
+
[doc]
|
|
2311
|
+
);
|
|
2312
|
+
const toggleList = (0, import_react11.useCallback)((kind) => {
|
|
2313
|
+
(0, import_core4.toggleList)(ctxRef.current, kind);
|
|
2314
|
+
}, []);
|
|
2315
|
+
const indentList = (0, import_react11.useCallback)(() => {
|
|
2316
|
+
(0, import_core4.indentList)(ctxRef.current);
|
|
2317
|
+
}, []);
|
|
2318
|
+
const dedentList = (0, import_react11.useCallback)(() => {
|
|
2319
|
+
(0, import_core4.dedentList)(ctxRef.current);
|
|
2320
|
+
}, []);
|
|
2321
|
+
const splitListItem = (0, import_react11.useCallback)(() => {
|
|
2322
|
+
return (0, import_core4.splitListItem)(ctxRef.current);
|
|
2323
|
+
}, []);
|
|
2324
|
+
const isInTable = (0, import_react11.useCallback)(() => {
|
|
2325
|
+
return selectionRef.current.focus.cellIndex != null;
|
|
2326
|
+
}, []);
|
|
2327
|
+
const getTableLocation = (0, import_react11.useCallback)(() => {
|
|
2328
|
+
return (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2329
|
+
}, [doc]);
|
|
2330
|
+
const insertTable = (0, import_react11.useCallback)((rows, cols) => {
|
|
2331
|
+
(0, import_core4.insertTable)(ctxRef.current, rows, cols);
|
|
2332
|
+
}, []);
|
|
2333
|
+
const deleteTable = (0, import_react11.useCallback)((blockIndex) => {
|
|
2334
|
+
const idx = blockIndex ?? selectionRef.current.focus.blockIndex;
|
|
2335
|
+
(0, import_core4.deleteTable)(ctxRef.current, idx);
|
|
2336
|
+
}, []);
|
|
2337
|
+
const insertRowAbove = (0, import_react11.useCallback)(() => {
|
|
2338
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2339
|
+
if (!loc) return;
|
|
2340
|
+
(0, import_core4.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "before");
|
|
2341
|
+
}, [doc]);
|
|
2342
|
+
const insertRowBelow = (0, import_react11.useCallback)(() => {
|
|
2343
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2344
|
+
if (!loc) return;
|
|
2345
|
+
(0, import_core4.insertTableRow)(ctxRef.current, loc.blockIndex, loc.row, "after");
|
|
2346
|
+
}, [doc]);
|
|
2347
|
+
const deleteCurrentRow = (0, import_react11.useCallback)(() => {
|
|
2348
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2349
|
+
if (!loc) return;
|
|
2350
|
+
(0, import_core4.deleteTableRow)(ctxRef.current, loc.blockIndex, loc.row);
|
|
2351
|
+
}, [doc]);
|
|
2352
|
+
const insertColumnLeft = (0, import_react11.useCallback)(() => {
|
|
2353
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2354
|
+
if (!loc) return;
|
|
2355
|
+
(0, import_core4.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "before");
|
|
2356
|
+
}, [doc]);
|
|
2357
|
+
const insertColumnRight = (0, import_react11.useCallback)(() => {
|
|
2358
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2359
|
+
if (!loc) return;
|
|
2360
|
+
(0, import_core4.insertTableColumn)(ctxRef.current, loc.blockIndex, loc.col, "after");
|
|
2361
|
+
}, [doc]);
|
|
2362
|
+
const deleteCurrentColumn = (0, import_react11.useCallback)(() => {
|
|
2363
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2364
|
+
if (!loc) return;
|
|
2365
|
+
(0, import_core4.deleteTableColumn)(ctxRef.current, loc.blockIndex, loc.col);
|
|
2366
|
+
}, [doc]);
|
|
2367
|
+
const moveToNextCell = (0, import_react11.useCallback)(() => {
|
|
2368
|
+
(0, import_core4.moveToNextCell)(ctxRef.current);
|
|
2369
|
+
}, []);
|
|
2370
|
+
const moveToPrevCell = (0, import_react11.useCallback)(() => {
|
|
2371
|
+
(0, import_core4.moveToPrevCell)(ctxRef.current);
|
|
2372
|
+
}, []);
|
|
2373
|
+
const mergeRight = (0, import_react11.useCallback)(() => {
|
|
2374
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2375
|
+
if (!loc) return false;
|
|
2376
|
+
return (0, import_core4.mergeRight)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
|
|
2377
|
+
}, [doc]);
|
|
2378
|
+
const mergeDown = (0, import_react11.useCallback)(() => {
|
|
2379
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2380
|
+
if (!loc) return false;
|
|
2381
|
+
return (0, import_core4.mergeDown)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
|
|
2382
|
+
}, [doc]);
|
|
2383
|
+
const splitCell = (0, import_react11.useCallback)(() => {
|
|
2384
|
+
const loc = (0, import_core4.tableLocationOf)(doc, selectionRef.current.focus);
|
|
2385
|
+
if (!loc) return false;
|
|
2386
|
+
return (0, import_core4.splitCell)(ctxRef.current, loc.blockIndex, loc.row, loc.col);
|
|
2387
|
+
}, [doc]);
|
|
2388
|
+
const setColumnWidth = (0, import_react11.useCallback)(
|
|
2389
|
+
(blockIndex, col, widthPx) => {
|
|
2390
|
+
(0, import_core4.setColumnWidth)(ctxRef.current, blockIndex, col, widthPx);
|
|
2391
|
+
},
|
|
2392
|
+
[]
|
|
2393
|
+
);
|
|
2394
|
+
const getTableRect = (0, import_react11.useCallback)(
|
|
2395
|
+
() => (0, import_core4.tableRectSelection)(doc, selectionRef.current),
|
|
2396
|
+
[doc]
|
|
2397
|
+
);
|
|
2398
|
+
const hasTableRectSelection = (0, import_react11.useCallback)(() => {
|
|
2399
|
+
return (0, import_core4.tableRectSelection)(doc, selectionRef.current) !== null;
|
|
2400
|
+
}, [doc]);
|
|
2401
|
+
const mergeSelection = (0, import_react11.useCallback)(() => {
|
|
2402
|
+
return (0, import_core4.mergeSelection)(ctxRef.current);
|
|
2403
|
+
}, []);
|
|
2404
|
+
const insertImage = (0, import_react11.useCallback)((embed) => {
|
|
2405
|
+
(0, import_core4.insertImage)(ctxRef.current, embed);
|
|
2406
|
+
}, []);
|
|
2407
|
+
const setImageAttrs = (0, import_react11.useCallback)(
|
|
2408
|
+
(blockIndex, offset, partial, cellIndex, newOffset) => {
|
|
2409
|
+
(0, import_core4.setImageAttrs)(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);
|
|
2410
|
+
},
|
|
2411
|
+
[]
|
|
2412
|
+
);
|
|
2413
|
+
const uploadImageRef = (0, import_react11.useRef)(opts.uploadImage);
|
|
2414
|
+
uploadImageRef.current = opts.uploadImage;
|
|
2415
|
+
const insertImageFromFile = (0, import_react11.useCallback)(async (file) => {
|
|
2416
|
+
if (!file.type.startsWith("image/")) return;
|
|
2417
|
+
const dataUrl = await readAsDataURL(file);
|
|
2418
|
+
const dims = await readImageDimensions(dataUrl);
|
|
2419
|
+
let w = dims.width;
|
|
2420
|
+
let h = dims.height;
|
|
2421
|
+
if (w > MAX_INSERT_WIDTH) {
|
|
2422
|
+
h = Math.round(h * MAX_INSERT_WIDTH / w);
|
|
2423
|
+
w = MAX_INSERT_WIDTH;
|
|
2424
|
+
}
|
|
2425
|
+
const upload = uploadImageRef.current;
|
|
2426
|
+
const src = upload ? await upload(file) : dataUrl;
|
|
2427
|
+
(0, import_core4.insertImage)(ctxRef.current, { type: "image", src, width: w, height: h });
|
|
2428
|
+
}, []);
|
|
2429
|
+
const getSelectedEmbed = (0, import_react11.useCallback)(() => {
|
|
2430
|
+
const sel = selectionRef.current;
|
|
2431
|
+
if (sel.anchor.blockIndex !== sel.focus.blockIndex) return null;
|
|
2432
|
+
if ((sel.anchor.cellIndex ?? -1) !== (sel.focus.cellIndex ?? -1)) return null;
|
|
2433
|
+
const a = sel.anchor.offset;
|
|
2434
|
+
const b = sel.focus.offset;
|
|
2435
|
+
if (Math.abs(a - b) !== 1) return null;
|
|
2436
|
+
const start = Math.min(a, b);
|
|
2437
|
+
const yText = doc.textAt(sel.anchor.blockIndex, sel.anchor.cellIndex);
|
|
2438
|
+
if (!yText) return null;
|
|
2439
|
+
const delta = yText.toDelta();
|
|
2440
|
+
let cursor = 0;
|
|
2441
|
+
for (const op of delta) {
|
|
2442
|
+
if (typeof op.insert === "string") {
|
|
2443
|
+
cursor += op.insert.length;
|
|
2444
|
+
continue;
|
|
2445
|
+
}
|
|
2446
|
+
if (cursor === start && (0, import_core4.isImageEmbed)(op.insert)) {
|
|
2447
|
+
return {
|
|
2448
|
+
blockIndex: sel.anchor.blockIndex,
|
|
2449
|
+
cellIndex: sel.anchor.cellIndex,
|
|
2450
|
+
offset: start,
|
|
2451
|
+
embed: op.insert
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
cursor += 1;
|
|
2455
|
+
}
|
|
2456
|
+
return null;
|
|
2457
|
+
}, [doc]);
|
|
2458
|
+
const [linkRequest, setLinkRequest] = (0, import_react11.useState)(null);
|
|
2459
|
+
const requestLink = (0, import_react11.useCallback)((initialHref = "") => {
|
|
2460
|
+
return new Promise((resolve) => {
|
|
2461
|
+
setLinkRequest({ initialHref, resolve });
|
|
2462
|
+
});
|
|
2463
|
+
}, []);
|
|
2464
|
+
const resolveLinkRequest = (0, import_react11.useCallback)((href) => {
|
|
2465
|
+
setLinkRequest((prev) => {
|
|
2466
|
+
if (prev) prev.resolve(href);
|
|
2467
|
+
return null;
|
|
2468
|
+
});
|
|
2469
|
+
}, []);
|
|
2470
|
+
const pageSettings = usePageSettings(doc);
|
|
2471
|
+
const setPageSettings = (0, import_react11.useCallback)(
|
|
2472
|
+
(partial) => {
|
|
2473
|
+
doc.setPageSettings(partial);
|
|
2474
|
+
},
|
|
2475
|
+
[doc]
|
|
2476
|
+
);
|
|
2477
|
+
const [pageConfigRequest, setPageConfigRequest] = (0, import_react11.useState)(null);
|
|
2478
|
+
const requestPageConfig = (0, import_react11.useCallback)(() => {
|
|
2479
|
+
return new Promise((resolve) => {
|
|
2480
|
+
setPageConfigRequest({ initial: doc.getPageSettings(), resolve });
|
|
2481
|
+
});
|
|
2482
|
+
}, [doc]);
|
|
2483
|
+
const resolvePageConfigRequest = (0, import_react11.useCallback)((applied) => {
|
|
2484
|
+
setPageConfigRequest((prev) => {
|
|
2485
|
+
if (prev) prev.resolve(applied);
|
|
2486
|
+
return null;
|
|
2487
|
+
});
|
|
2488
|
+
}, []);
|
|
2489
|
+
const [imageCaptionRequest, setImageCaptionRequest] = (0, import_react11.useState)(null);
|
|
2490
|
+
const requestImageCaption = (0, import_react11.useCallback)(
|
|
2491
|
+
(initialCaption = "", initialAlign = "center") => {
|
|
2492
|
+
return new Promise((resolve) => {
|
|
2493
|
+
setImageCaptionRequest({ initialCaption, initialAlign, resolve });
|
|
2494
|
+
});
|
|
2495
|
+
},
|
|
2496
|
+
[]
|
|
2497
|
+
);
|
|
2498
|
+
const resolveImageCaptionRequest = (0, import_react11.useCallback)((result) => {
|
|
2499
|
+
setImageCaptionRequest((prev) => {
|
|
2500
|
+
if (prev) prev.resolve(result);
|
|
2501
|
+
return null;
|
|
2502
|
+
});
|
|
2503
|
+
}, []);
|
|
2504
|
+
return {
|
|
2505
|
+
doc,
|
|
2506
|
+
history,
|
|
2507
|
+
snapshot,
|
|
2508
|
+
selection,
|
|
2509
|
+
setSelection,
|
|
2510
|
+
getSelection,
|
|
2511
|
+
isMarkActive,
|
|
2512
|
+
getActiveMarks,
|
|
2513
|
+
toggleMark,
|
|
2514
|
+
setMark,
|
|
2515
|
+
removeMark,
|
|
2516
|
+
pendingMarks,
|
|
2517
|
+
consumePendingMarks,
|
|
2518
|
+
getBlockType,
|
|
2519
|
+
getBlockAttr,
|
|
2520
|
+
setBlockType,
|
|
2521
|
+
setBlockAttr,
|
|
2522
|
+
isListActive,
|
|
2523
|
+
toggleList,
|
|
2524
|
+
indentList,
|
|
2525
|
+
dedentList,
|
|
2526
|
+
splitListItem,
|
|
2527
|
+
linkRequest,
|
|
2528
|
+
requestLink,
|
|
2529
|
+
resolveLinkRequest,
|
|
2530
|
+
isInTable,
|
|
2531
|
+
getTableLocation,
|
|
2532
|
+
insertTable,
|
|
2533
|
+
deleteTable,
|
|
2534
|
+
insertRowAbove,
|
|
2535
|
+
insertRowBelow,
|
|
2536
|
+
deleteCurrentRow,
|
|
2537
|
+
insertColumnLeft,
|
|
2538
|
+
insertColumnRight,
|
|
2539
|
+
deleteCurrentColumn,
|
|
2540
|
+
moveToNextCell,
|
|
2541
|
+
moveToPrevCell,
|
|
2542
|
+
mergeRight,
|
|
2543
|
+
mergeDown,
|
|
2544
|
+
splitCell,
|
|
2545
|
+
setColumnWidth,
|
|
2546
|
+
getTableRect,
|
|
2547
|
+
hasTableRectSelection,
|
|
2548
|
+
mergeSelection,
|
|
2549
|
+
insertImage,
|
|
2550
|
+
setImageAttrs,
|
|
2551
|
+
insertImageFromFile,
|
|
2552
|
+
getSelectedEmbed,
|
|
2553
|
+
pageSettings,
|
|
2554
|
+
setPageSettings,
|
|
2555
|
+
pageConfigRequest,
|
|
2556
|
+
requestPageConfig,
|
|
2557
|
+
resolvePageConfigRequest,
|
|
2558
|
+
imageCaptionRequest,
|
|
2559
|
+
requestImageCaption,
|
|
2560
|
+
resolveImageCaptionRequest
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
function readAsDataURL(file) {
|
|
2564
|
+
return new Promise((resolve, reject) => {
|
|
2565
|
+
const reader = new FileReader();
|
|
2566
|
+
reader.onerror = () => reject(reader.error ?? new Error("FileReader error"));
|
|
2567
|
+
reader.onload = () => resolve(reader.result);
|
|
2568
|
+
reader.readAsDataURL(file);
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
function readImageDimensions(src) {
|
|
2572
|
+
return new Promise((resolve, reject) => {
|
|
2573
|
+
const img = new Image();
|
|
2574
|
+
img.onerror = () => reject(new Error("image load failed"));
|
|
2575
|
+
img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
|
|
2576
|
+
img.src = src;
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
var booleanMarks = /* @__PURE__ */ new Set(["bold", "italic", "underline", "strike"]);
|
|
2580
|
+
function sameValue(a, b) {
|
|
2581
|
+
if (a === b) return true;
|
|
2582
|
+
if (typeof a === "object" && a && b && typeof b === "object") {
|
|
2583
|
+
const ak = Object.keys(a);
|
|
2584
|
+
const bk = Object.keys(b);
|
|
2585
|
+
if (ak.length !== bk.length) return false;
|
|
2586
|
+
return ak.every((k) => a[k] === b[k]);
|
|
2587
|
+
}
|
|
2588
|
+
return false;
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
// src/Editor.tsx
|
|
2592
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2593
|
+
var defaultFooter = ({ pageNumber, pageCount }) => `${pageNumber} / ${pageCount}`;
|
|
2594
|
+
function Editor({
|
|
2595
|
+
editor: providedEditor,
|
|
2596
|
+
className,
|
|
2597
|
+
pageGeometry,
|
|
2598
|
+
renderPageHeader,
|
|
2599
|
+
renderPageFooter = defaultFooter
|
|
2600
|
+
}) {
|
|
2601
|
+
const internal = useEditor();
|
|
2602
|
+
const editor = providedEditor ?? internal;
|
|
2603
|
+
const { doc, history, snapshot, getSelection, setSelection } = editor;
|
|
2604
|
+
const rootRef = (0, import_react12.useRef)(null);
|
|
2605
|
+
const isComposingRef = (0, import_react12.useRef)(false);
|
|
2606
|
+
const suppressSelectionSyncRef = (0, import_react12.useRef)(false);
|
|
2607
|
+
const ctxRef = (0, import_react12.useRef)({ doc, getSelection, setSelection });
|
|
2608
|
+
ctxRef.current = { doc, getSelection, setSelection };
|
|
2609
|
+
const historyRef = (0, import_react12.useRef)(history);
|
|
2610
|
+
historyRef.current = history;
|
|
2611
|
+
const editorRef = (0, import_react12.useRef)(editor);
|
|
2612
|
+
editorRef.current = editor;
|
|
2613
|
+
(0, import_react12.useLayoutEffect)(() => {
|
|
2614
|
+
const root = rootRef.current;
|
|
2615
|
+
if (!root) return;
|
|
2616
|
+
if (isComposingRef.current) return;
|
|
2617
|
+
if (document.activeElement !== root) return;
|
|
2618
|
+
const current = readDomSelection(root);
|
|
2619
|
+
if (current && selectionsEqual(current, editor.selection)) return;
|
|
2620
|
+
suppressSelectionSyncRef.current = true;
|
|
2621
|
+
applyDomSelection(root, editor.selection);
|
|
2622
|
+
queueMicrotask(() => {
|
|
2623
|
+
suppressSelectionSyncRef.current = false;
|
|
2624
|
+
});
|
|
2625
|
+
});
|
|
2626
|
+
(0, import_react12.useEffect)(() => {
|
|
2627
|
+
const root = rootRef.current;
|
|
2628
|
+
if (!root) return;
|
|
2629
|
+
const handler = () => {
|
|
2630
|
+
if (suppressSelectionSyncRef.current) return;
|
|
2631
|
+
if (isComposingRef.current) return;
|
|
2632
|
+
if (document.activeElement !== root) return;
|
|
2633
|
+
const modelSel = readDomSelection(root);
|
|
2634
|
+
if (modelSel) setSelection(modelSel);
|
|
2635
|
+
};
|
|
2636
|
+
document.addEventListener("selectionchange", handler);
|
|
2637
|
+
return () => document.removeEventListener("selectionchange", handler);
|
|
2638
|
+
}, [setSelection]);
|
|
2639
|
+
(0, import_react12.useEffect)(() => {
|
|
2640
|
+
const root = rootRef.current;
|
|
2641
|
+
if (!root) return;
|
|
2642
|
+
let drag = null;
|
|
2643
|
+
function cellFromTarget(target) {
|
|
2644
|
+
if (!(target instanceof Node)) return null;
|
|
2645
|
+
let n = target;
|
|
2646
|
+
let cellEl = null;
|
|
2647
|
+
let tableEl = null;
|
|
2648
|
+
while (n && n !== root) {
|
|
2649
|
+
if (n.nodeType === Node.ELEMENT_NODE) {
|
|
2650
|
+
const e = n;
|
|
2651
|
+
if (!cellEl && e.dataset.cellIndex != null) cellEl = e;
|
|
2652
|
+
if (e.tagName === "TABLE" && e.dataset.blockIndex != null) {
|
|
2653
|
+
tableEl = e;
|
|
2654
|
+
break;
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
n = n.parentNode;
|
|
2658
|
+
}
|
|
2659
|
+
if (!cellEl || !tableEl) return null;
|
|
2660
|
+
const cellIndex = Number.parseInt(cellEl.dataset.cellIndex, 10);
|
|
2661
|
+
const blockIndex = Number.parseInt(tableEl.dataset.blockIndex, 10);
|
|
2662
|
+
return { blockIndex, cellIndex, tableEl };
|
|
2663
|
+
}
|
|
2664
|
+
const onPointerDown = (e) => {
|
|
2665
|
+
if (e.button !== 0) return;
|
|
2666
|
+
const hit = cellFromTarget(e.target);
|
|
2667
|
+
if (!hit) {
|
|
2668
|
+
drag = null;
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2671
|
+
drag = {
|
|
2672
|
+
blockIndex: hit.blockIndex,
|
|
2673
|
+
anchorCellIndex: hit.cellIndex,
|
|
2674
|
+
anchorTableEl: hit.tableEl,
|
|
2675
|
+
rectActive: false
|
|
2676
|
+
};
|
|
2677
|
+
};
|
|
2678
|
+
const onPointerMove = (e) => {
|
|
2679
|
+
if (!drag) return;
|
|
2680
|
+
if ((e.buttons & 1) === 0) {
|
|
2681
|
+
drag = null;
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
const hit = cellFromTarget(e.target);
|
|
2685
|
+
if (!hit) return;
|
|
2686
|
+
if (hit.blockIndex !== drag.blockIndex) return;
|
|
2687
|
+
if (hit.cellIndex === drag.anchorCellIndex && !drag.rectActive) return;
|
|
2688
|
+
if (!drag.rectActive) {
|
|
2689
|
+
drag.rectActive = true;
|
|
2690
|
+
window.getSelection()?.removeAllRanges();
|
|
2691
|
+
}
|
|
2692
|
+
e.preventDefault();
|
|
2693
|
+
ctxRef.current.setSelection({
|
|
2694
|
+
anchor: { blockIndex: drag.blockIndex, cellIndex: drag.anchorCellIndex, offset: 0 },
|
|
2695
|
+
focus: { blockIndex: drag.blockIndex, cellIndex: hit.cellIndex, offset: 0 }
|
|
2696
|
+
});
|
|
2697
|
+
};
|
|
2698
|
+
const onPointerUp = () => {
|
|
2699
|
+
drag = null;
|
|
2700
|
+
};
|
|
2701
|
+
root.addEventListener("pointerdown", onPointerDown);
|
|
2702
|
+
document.addEventListener("pointermove", onPointerMove);
|
|
2703
|
+
document.addEventListener("pointerup", onPointerUp);
|
|
2704
|
+
return () => {
|
|
2705
|
+
root.removeEventListener("pointerdown", onPointerDown);
|
|
2706
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
2707
|
+
document.removeEventListener("pointerup", onPointerUp);
|
|
2708
|
+
};
|
|
2709
|
+
}, []);
|
|
2710
|
+
(0, import_react12.useEffect)(() => {
|
|
2711
|
+
const root = rootRef.current;
|
|
2712
|
+
if (!root) return;
|
|
2713
|
+
const onDragOver = (e) => {
|
|
2714
|
+
if (!e.dataTransfer) return;
|
|
2715
|
+
const hasFiles = Array.from(e.dataTransfer.items ?? []).some(
|
|
2716
|
+
(i) => i.kind === "file"
|
|
2717
|
+
);
|
|
2718
|
+
if (!hasFiles) return;
|
|
2719
|
+
e.preventDefault();
|
|
2720
|
+
e.dataTransfer.dropEffect = "copy";
|
|
2721
|
+
};
|
|
2722
|
+
const onDrop = (e) => {
|
|
2723
|
+
if (!e.dataTransfer) return;
|
|
2724
|
+
const images = Array.from(e.dataTransfer.files).filter(
|
|
2725
|
+
(f) => f.type.startsWith("image/")
|
|
2726
|
+
);
|
|
2727
|
+
if (images.length === 0) return;
|
|
2728
|
+
e.preventDefault();
|
|
2729
|
+
const range = caretRangeFromPoint(e.clientX, e.clientY);
|
|
2730
|
+
if (range) {
|
|
2731
|
+
const dom = window.getSelection();
|
|
2732
|
+
dom?.removeAllRanges();
|
|
2733
|
+
dom?.addRange(range);
|
|
2734
|
+
const sel = readDomSelection(root);
|
|
2735
|
+
if (sel) ctxRef.current.setSelection(sel);
|
|
2736
|
+
}
|
|
2737
|
+
void (async () => {
|
|
2738
|
+
for (const f of images) await editorRef.current.insertImageFromFile(f);
|
|
2739
|
+
})();
|
|
2740
|
+
};
|
|
2741
|
+
root.addEventListener("dragover", onDragOver);
|
|
2742
|
+
root.addEventListener("drop", onDrop);
|
|
2743
|
+
return () => {
|
|
2744
|
+
root.removeEventListener("dragover", onDragOver);
|
|
2745
|
+
root.removeEventListener("drop", onDrop);
|
|
2746
|
+
};
|
|
2747
|
+
}, []);
|
|
2748
|
+
(0, import_react12.useEffect)(() => {
|
|
2749
|
+
const root = rootRef.current;
|
|
2750
|
+
if (!root) return;
|
|
2751
|
+
const onPointerDown = (e) => {
|
|
2752
|
+
if (e.button !== 0) return;
|
|
2753
|
+
const target = e.target;
|
|
2754
|
+
if (!(target instanceof Element)) return;
|
|
2755
|
+
const img = target.closest('img[data-embed="image"]');
|
|
2756
|
+
if (!img) return;
|
|
2757
|
+
const blockEl = img.closest("[data-block-index]");
|
|
2758
|
+
if (!blockEl) return;
|
|
2759
|
+
const blockIndex = Number.parseInt(blockEl.dataset.blockIndex, 10);
|
|
2760
|
+
const offsetAttr = img.dataset.embedOffset;
|
|
2761
|
+
if (offsetAttr == null) return;
|
|
2762
|
+
const localOffset = Number.parseInt(offsetAttr, 10);
|
|
2763
|
+
const fragmentStart = Number.parseInt(blockEl.dataset.fragmentStart ?? "0", 10) || 0;
|
|
2764
|
+
const cellEl = img.closest("[data-cell-index]");
|
|
2765
|
+
const cellIndex = cellEl ? Number.parseInt(cellEl.dataset.cellIndex, 10) : void 0;
|
|
2766
|
+
const offset = fragmentStart + localOffset;
|
|
2767
|
+
e.preventDefault();
|
|
2768
|
+
ctxRef.current.setSelection({
|
|
2769
|
+
anchor: { blockIndex, cellIndex, offset },
|
|
2770
|
+
focus: { blockIndex, cellIndex, offset: offset + 1 }
|
|
2771
|
+
});
|
|
2772
|
+
};
|
|
2773
|
+
root.addEventListener("pointerdown", onPointerDown);
|
|
2774
|
+
return () => root.removeEventListener("pointerdown", onPointerDown);
|
|
2775
|
+
}, []);
|
|
2776
|
+
(0, import_react12.useEffect)(() => {
|
|
2777
|
+
const root = rootRef.current;
|
|
2778
|
+
if (!root) return;
|
|
2779
|
+
const handler = (e) => {
|
|
2780
|
+
const ctx = ctxRef.current;
|
|
2781
|
+
const hist = historyRef.current;
|
|
2782
|
+
const ed = editorRef.current;
|
|
2783
|
+
const type = e.inputType;
|
|
2784
|
+
if (isComposingRef.current && type === "insertCompositionText") return;
|
|
2785
|
+
switch (type) {
|
|
2786
|
+
case "insertText": {
|
|
2787
|
+
const data = e.data ?? "";
|
|
2788
|
+
if (data.length > 0) {
|
|
2789
|
+
e.preventDefault();
|
|
2790
|
+
(0, import_core5.insertText)(ctx, data, ed.consumePendingMarks());
|
|
2791
|
+
}
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
case "insertParagraph":
|
|
2795
|
+
case "insertLineBreak": {
|
|
2796
|
+
e.preventDefault();
|
|
2797
|
+
const selectedEmbed2 = ed.getSelectedEmbed();
|
|
2798
|
+
if (selectedEmbed2) {
|
|
2799
|
+
const off = selectedEmbed2.offset + 1;
|
|
2800
|
+
ctx.setSelection({
|
|
2801
|
+
anchor: {
|
|
2802
|
+
blockIndex: selectedEmbed2.blockIndex,
|
|
2803
|
+
cellIndex: selectedEmbed2.cellIndex,
|
|
2804
|
+
offset: off
|
|
2805
|
+
},
|
|
2806
|
+
focus: {
|
|
2807
|
+
blockIndex: selectedEmbed2.blockIndex,
|
|
2808
|
+
cellIndex: selectedEmbed2.cellIndex,
|
|
2809
|
+
offset: off
|
|
2810
|
+
}
|
|
2811
|
+
});
|
|
2812
|
+
}
|
|
2813
|
+
if (ed.splitListItem()) return;
|
|
2814
|
+
(0, import_core5.insertParagraph)(ctx);
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
case "insertReplacementText": {
|
|
2818
|
+
const text = e.data ?? e.dataTransfer?.getData("text/plain") ?? "";
|
|
2819
|
+
if (text.length > 0) {
|
|
2820
|
+
e.preventDefault();
|
|
2821
|
+
(0, import_core5.insertText)(ctx, text, ed.consumePendingMarks());
|
|
2822
|
+
}
|
|
2823
|
+
return;
|
|
2824
|
+
}
|
|
2825
|
+
case "insertFromPaste": {
|
|
2826
|
+
e.preventDefault();
|
|
2827
|
+
const images = Array.from(e.dataTransfer?.files ?? []).filter(
|
|
2828
|
+
(f) => f.type.startsWith("image/")
|
|
2829
|
+
);
|
|
2830
|
+
if (images.length > 0) {
|
|
2831
|
+
void (async () => {
|
|
2832
|
+
for (const f of images) await editorRef.current.insertImageFromFile(f);
|
|
2833
|
+
})();
|
|
2834
|
+
return;
|
|
2835
|
+
}
|
|
2836
|
+
const text = e.dataTransfer?.getData("text/plain") ?? "";
|
|
2837
|
+
if (text.length === 0) return;
|
|
2838
|
+
const lines = text.split(/\r\n|\r|\n/);
|
|
2839
|
+
lines.forEach((line, i) => {
|
|
2840
|
+
if (i > 0) (0, import_core5.insertParagraph)(ctx);
|
|
2841
|
+
if (line.length > 0) (0, import_core5.insertText)(ctx, line);
|
|
2842
|
+
});
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
case "deleteContentBackward":
|
|
2846
|
+
case "deleteWordBackward":
|
|
2847
|
+
case "deleteSoftLineBackward":
|
|
2848
|
+
case "deleteHardLineBackward": {
|
|
2849
|
+
e.preventDefault();
|
|
2850
|
+
(0, import_core5.deleteBackward)(ctx);
|
|
2851
|
+
return;
|
|
2852
|
+
}
|
|
2853
|
+
case "deleteContentForward":
|
|
2854
|
+
case "deleteWordForward":
|
|
2855
|
+
case "deleteSoftLineForward":
|
|
2856
|
+
case "deleteHardLineForward": {
|
|
2857
|
+
e.preventDefault();
|
|
2858
|
+
(0, import_core5.deleteForward)(ctx);
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
case "historyUndo": {
|
|
2862
|
+
e.preventDefault();
|
|
2863
|
+
hist.undo();
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
case "historyRedo": {
|
|
2867
|
+
e.preventDefault();
|
|
2868
|
+
hist.redo();
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
default: {
|
|
2872
|
+
e.preventDefault();
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
};
|
|
2877
|
+
root.addEventListener("beforeinput", handler);
|
|
2878
|
+
return () => root.removeEventListener("beforeinput", handler);
|
|
2879
|
+
}, []);
|
|
2880
|
+
const onKeyDown = (0, import_react12.useCallback)((e) => {
|
|
2881
|
+
const ed = editorRef.current;
|
|
2882
|
+
const mod = e.metaKey || e.ctrlKey;
|
|
2883
|
+
if (e.key === "Tab") {
|
|
2884
|
+
e.preventDefault();
|
|
2885
|
+
if (ed.isInTable()) {
|
|
2886
|
+
if (e.shiftKey) ed.moveToPrevCell();
|
|
2887
|
+
else ed.moveToNextCell();
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
if (e.shiftKey) ed.dedentList();
|
|
2891
|
+
else ed.indentList();
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
if (!mod) return;
|
|
2895
|
+
const key = e.key.toLowerCase();
|
|
2896
|
+
if (!e.shiftKey && !e.altKey && key === "z") {
|
|
2897
|
+
e.preventDefault();
|
|
2898
|
+
historyRef.current.undo();
|
|
2899
|
+
return;
|
|
2900
|
+
}
|
|
2901
|
+
if (!e.altKey && (key === "y" || e.shiftKey && key === "z")) {
|
|
2902
|
+
e.preventDefault();
|
|
2903
|
+
historyRef.current.redo();
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
if (e.altKey && !e.shiftKey && /^[0-6]$/.test(key)) {
|
|
2907
|
+
e.preventDefault();
|
|
2908
|
+
const n = Number(key);
|
|
2909
|
+
if (n === 0) ed.setBlockType("paragraph");
|
|
2910
|
+
else ed.setBlockType("heading", { level: n });
|
|
2911
|
+
return;
|
|
2912
|
+
}
|
|
2913
|
+
if (e.shiftKey && !e.altKey && (key === "7" || key === "8")) {
|
|
2914
|
+
e.preventDefault();
|
|
2915
|
+
ed.toggleList(key === "7" ? "ordered" : "bullet");
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
const markToggle = !e.shiftKey && !e.altKey ? BOOLEAN_SHORTCUTS[key] : void 0;
|
|
2919
|
+
if (markToggle) {
|
|
2920
|
+
e.preventDefault();
|
|
2921
|
+
ed.toggleMark(markToggle);
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
if (e.shiftKey && !e.altKey && key === "x") {
|
|
2925
|
+
e.preventDefault();
|
|
2926
|
+
ed.toggleMark("strike");
|
|
2927
|
+
return;
|
|
2928
|
+
}
|
|
2929
|
+
if (!e.altKey && !e.shiftKey && key === "k") {
|
|
2930
|
+
e.preventDefault();
|
|
2931
|
+
const active = ed.getActiveMarks();
|
|
2932
|
+
const currentHref = active.link && typeof active.link === "object" && "href" in active.link ? active.link.href : "";
|
|
2933
|
+
void ed.requestLink(currentHref).then((href) => {
|
|
2934
|
+
if (href === null) return;
|
|
2935
|
+
if (href === "") ed.removeMark("link");
|
|
2936
|
+
else ed.setMark("link", { href });
|
|
2937
|
+
});
|
|
2938
|
+
}
|
|
2939
|
+
}, []);
|
|
2940
|
+
const onCompositionStart = (0, import_react12.useCallback)(() => {
|
|
2941
|
+
isComposingRef.current = true;
|
|
2942
|
+
}, []);
|
|
2943
|
+
const onCompositionEnd = (0, import_react12.useCallback)((e) => {
|
|
2944
|
+
isComposingRef.current = false;
|
|
2945
|
+
if (e.data && e.data.length > 0) {
|
|
2946
|
+
(0, import_core5.insertText)(ctxRef.current, e.data);
|
|
2947
|
+
}
|
|
2948
|
+
}, []);
|
|
2949
|
+
const topLevels = (0, import_react12.useMemo)(() => groupTopLevels(snapshot.blocks), [snapshot]);
|
|
2950
|
+
const docSettings = usePageSettings(editor.doc);
|
|
2951
|
+
const propIsSet = pageGeometry !== void 0;
|
|
2952
|
+
const geom = propIsSet ? pageGeometry ?? A4_PAGE : docSettings;
|
|
2953
|
+
const paginated = pageGeometry !== null;
|
|
2954
|
+
const selectedEmbed = editor.getSelectedEmbed();
|
|
2955
|
+
const layout = usePageLayout(
|
|
2956
|
+
rootRef,
|
|
2957
|
+
geom,
|
|
2958
|
+
topLevels.length,
|
|
2959
|
+
[topLevels, pageGeometry]
|
|
2960
|
+
);
|
|
2961
|
+
const effectiveLayout = paginated ? layout : defaultPageLayout(topLevels.length);
|
|
2962
|
+
const pageStyle = paginated ? {
|
|
2963
|
+
"--ed-page-width": `${geom.width}px`,
|
|
2964
|
+
"--ed-page-height": `${geom.height}px`,
|
|
2965
|
+
"--ed-page-margin-top": `${geom.marginTop}px`,
|
|
2966
|
+
"--ed-page-margin-bottom": `${geom.marginBottom}px`,
|
|
2967
|
+
"--ed-page-margin-left": `${geom.marginLeft}px`,
|
|
2968
|
+
"--ed-page-margin-right": `${geom.marginRight}px`
|
|
2969
|
+
} : void 0;
|
|
2970
|
+
const body = /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2971
|
+
"div",
|
|
2972
|
+
{
|
|
2973
|
+
ref: rootRef,
|
|
2974
|
+
contentEditable: true,
|
|
2975
|
+
suppressContentEditableWarning: true,
|
|
2976
|
+
spellCheck: true,
|
|
2977
|
+
autoCorrect: "off",
|
|
2978
|
+
autoCapitalize: "off",
|
|
2979
|
+
role: "textbox",
|
|
2980
|
+
"aria-multiline": "true",
|
|
2981
|
+
className: (className ?? "ed-root") + (paginated ? " ed-root--paged" : " ed-root--flat"),
|
|
2982
|
+
style: pageStyle,
|
|
2983
|
+
onKeyDown,
|
|
2984
|
+
onCompositionStart,
|
|
2985
|
+
onCompositionEnd,
|
|
2986
|
+
children: [
|
|
2987
|
+
paginated ? effectiveLayout.pages.map((page, pi) => {
|
|
2988
|
+
const ctx = {
|
|
2989
|
+
pageNumber: pi + 1,
|
|
2990
|
+
pageCount: effectiveLayout.pages.length
|
|
2991
|
+
};
|
|
2992
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2993
|
+
"div",
|
|
2994
|
+
{
|
|
2995
|
+
className: "ed-page",
|
|
2996
|
+
"data-page-number": ctx.pageNumber,
|
|
2997
|
+
"data-page-total": ctx.pageCount,
|
|
2998
|
+
children: [
|
|
2999
|
+
renderPageHeader && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ed-page-header", contentEditable: false, children: renderPageHeader(ctx) }),
|
|
3000
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ed-page-content", children: page.slots.map((slot) => renderSlot(slot, topLevels)) }),
|
|
3001
|
+
renderPageFooter && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ed-page-footer", contentEditable: false, children: renderPageFooter(ctx) })
|
|
3002
|
+
]
|
|
3003
|
+
},
|
|
3004
|
+
`page-${pi}`
|
|
3005
|
+
);
|
|
3006
|
+
}) : topLevels.map((t) => renderTopLevel(t)),
|
|
3007
|
+
selectedEmbed && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3008
|
+
ImageResizeOverlay,
|
|
3009
|
+
{
|
|
3010
|
+
rootRef,
|
|
3011
|
+
blockIndex: selectedEmbed.blockIndex,
|
|
3012
|
+
offset: selectedEmbed.offset,
|
|
3013
|
+
cellIndex: selectedEmbed.cellIndex,
|
|
3014
|
+
embed: selectedEmbed.embed,
|
|
3015
|
+
onCommit: (w, h) => editor.setImageAttrs(
|
|
3016
|
+
selectedEmbed.blockIndex,
|
|
3017
|
+
selectedEmbed.offset,
|
|
3018
|
+
{ width: w, height: h },
|
|
3019
|
+
selectedEmbed.cellIndex
|
|
3020
|
+
),
|
|
3021
|
+
onCommitMove: (ox, oy) => editor.setImageAttrs(
|
|
3022
|
+
selectedEmbed.blockIndex,
|
|
3023
|
+
selectedEmbed.offset,
|
|
3024
|
+
{ offsetX: ox, offsetY: oy },
|
|
3025
|
+
selectedEmbed.cellIndex
|
|
3026
|
+
)
|
|
3027
|
+
}
|
|
3028
|
+
)
|
|
3029
|
+
]
|
|
3030
|
+
}
|
|
3031
|
+
);
|
|
3032
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditorProvider, { editor, children: body });
|
|
3033
|
+
}
|
|
3034
|
+
function groupTopLevels(snapshot) {
|
|
3035
|
+
const out = [];
|
|
3036
|
+
let i = 0;
|
|
3037
|
+
while (i < snapshot.length) {
|
|
3038
|
+
const block = snapshot[i];
|
|
3039
|
+
if (block.type === "listItem") {
|
|
3040
|
+
const groupStart = i;
|
|
3041
|
+
const items = [];
|
|
3042
|
+
const kind = block.attrs.listKind === "ordered" ? "ordered" : "bullet";
|
|
3043
|
+
while (i < snapshot.length && snapshot[i].type === "listItem" && (snapshot[i].attrs.listKind ?? "bullet") === kind) {
|
|
3044
|
+
items.push({ block: snapshot[i], index: i });
|
|
3045
|
+
i++;
|
|
3046
|
+
}
|
|
3047
|
+
out.push({ kind: "list", items, listKind: kind, key: groupStart });
|
|
3048
|
+
continue;
|
|
3049
|
+
}
|
|
3050
|
+
out.push({ kind: "block", block, blockIndex: i, key: i });
|
|
3051
|
+
i++;
|
|
3052
|
+
}
|
|
3053
|
+
return out;
|
|
3054
|
+
}
|
|
3055
|
+
function renderSlot(slot, topLevels) {
|
|
3056
|
+
const top = topLevels[slot.topLevelIndex];
|
|
3057
|
+
if (!top) return null;
|
|
3058
|
+
if (slot.fragment && top.kind === "block") {
|
|
3059
|
+
const f = {
|
|
3060
|
+
index: slot.fragment.index,
|
|
3061
|
+
start: slot.fragment.start,
|
|
3062
|
+
end: slot.fragment.end
|
|
3063
|
+
};
|
|
3064
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3065
|
+
NodeView,
|
|
3066
|
+
{
|
|
3067
|
+
block: top.block,
|
|
3068
|
+
index: top.blockIndex,
|
|
3069
|
+
fragment: f
|
|
3070
|
+
},
|
|
3071
|
+
`${top.key}-f${f.index}`
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
3074
|
+
if (slot.tableFragment && top.kind === "block" && top.block.type === "table") {
|
|
3075
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3076
|
+
NodeView,
|
|
3077
|
+
{
|
|
3078
|
+
block: top.block,
|
|
3079
|
+
index: top.blockIndex,
|
|
3080
|
+
tableFragment: slot.tableFragment
|
|
3081
|
+
},
|
|
3082
|
+
`${top.key}-tf${slot.tableFragment.index}`
|
|
3083
|
+
);
|
|
3084
|
+
}
|
|
3085
|
+
return renderTopLevel(top);
|
|
3086
|
+
}
|
|
3087
|
+
function renderTopLevel(top) {
|
|
3088
|
+
if (top.kind === "list") {
|
|
3089
|
+
const tree = buildListTree(top.items);
|
|
3090
|
+
return renderListTree(tree, top.listKind, `list-${top.key}`);
|
|
3091
|
+
}
|
|
3092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(NodeView, { block: top.block, index: top.blockIndex }, top.key);
|
|
3093
|
+
}
|
|
3094
|
+
function buildListTree(items) {
|
|
3095
|
+
const roots = [];
|
|
3096
|
+
const stack = [{ children: roots, level: -1 }];
|
|
3097
|
+
for (const it of items) {
|
|
3098
|
+
const level = clampListLevel2(it.block.attrs.listLevel);
|
|
3099
|
+
while (stack.length > 1 && stack[stack.length - 1].level >= level) stack.pop();
|
|
3100
|
+
const node = { entry: it, children: [] };
|
|
3101
|
+
stack[stack.length - 1].children.push(node);
|
|
3102
|
+
stack.push({ children: node.children, level });
|
|
3103
|
+
}
|
|
3104
|
+
return roots;
|
|
3105
|
+
}
|
|
3106
|
+
function renderListTree(tree, kind, key) {
|
|
3107
|
+
const Tag = kind === "ordered" ? "ol" : "ul";
|
|
3108
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderListTree(node.children, kind, `nested-${node.entry.index}`) : null }, node.entry.index)) }, key);
|
|
3109
|
+
}
|
|
3110
|
+
function clampListLevel2(l) {
|
|
3111
|
+
if (typeof l !== "number" || !Number.isFinite(l)) return 0;
|
|
3112
|
+
return Math.max(0, Math.min(5, Math.trunc(l)));
|
|
3113
|
+
}
|
|
3114
|
+
var BOOLEAN_SHORTCUTS = {
|
|
3115
|
+
b: "bold",
|
|
3116
|
+
i: "italic",
|
|
3117
|
+
u: "underline"
|
|
3118
|
+
};
|
|
3119
|
+
function caretRangeFromPoint(x, y) {
|
|
3120
|
+
const doc = document;
|
|
3121
|
+
if (typeof doc.caretRangeFromPoint === "function") {
|
|
3122
|
+
return doc.caretRangeFromPoint(x, y);
|
|
3123
|
+
}
|
|
3124
|
+
if (typeof doc.caretPositionFromPoint === "function") {
|
|
3125
|
+
const pos = doc.caretPositionFromPoint(x, y);
|
|
3126
|
+
if (!pos) return null;
|
|
3127
|
+
const range = document.createRange();
|
|
3128
|
+
range.setStart(pos.offsetNode, pos.offset);
|
|
3129
|
+
range.collapse(true);
|
|
3130
|
+
return range;
|
|
3131
|
+
}
|
|
3132
|
+
return null;
|
|
3133
|
+
}
|
|
3134
|
+
|
|
3135
|
+
// src/Toolbar.tsx
|
|
3136
|
+
var import_react13 = require("react");
|
|
3137
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
3138
|
+
var FONT_FAMILIES = ["Arial"];
|
|
3139
|
+
var FONT_SIZES = ["8pt", "10pt", "11pt", "12pt", "14pt", "16pt", "18pt", "24pt", "32pt", "48pt"];
|
|
3140
|
+
var BLOCK_OPTIONS = [
|
|
3141
|
+
{ value: "paragraph", label: "Par\xE1grafo", type: "paragraph" },
|
|
3142
|
+
{ value: "h1", label: "T\xEDtulo 1", type: "heading", level: 1 },
|
|
3143
|
+
{ value: "h2", label: "T\xEDtulo 2", type: "heading", level: 2 },
|
|
3144
|
+
{ value: "h3", label: "T\xEDtulo 3", type: "heading", level: 3 },
|
|
3145
|
+
{ value: "h4", label: "T\xEDtulo 4", type: "heading", level: 4 },
|
|
3146
|
+
{ value: "h5", label: "T\xEDtulo 5", type: "heading", level: 5 },
|
|
3147
|
+
{ value: "h6", label: "T\xEDtulo 6", type: "heading", level: 6 },
|
|
3148
|
+
{ value: "blockquote", label: "Cita\xE7\xE3o", type: "blockquote" },
|
|
3149
|
+
{ value: "codeBlock", label: "C\xF3digo", type: "codeBlock" }
|
|
3150
|
+
];
|
|
3151
|
+
var ALIGN_OPTIONS = [
|
|
3152
|
+
{ value: "left", label: "\u21E4", title: "Alinhar \xE0 esquerda" },
|
|
3153
|
+
{ value: "center", label: "\u2261", title: "Centralizar" },
|
|
3154
|
+
{ value: "right", label: "\u21E5", title: "Alinhar \xE0 direita" },
|
|
3155
|
+
{ value: "justify", label: "\u2630", title: "Justificar" }
|
|
3156
|
+
];
|
|
3157
|
+
var IMAGE_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right"]);
|
|
3158
|
+
var IMAGE_LAYOUT_OPTIONS = [
|
|
3159
|
+
{ value: "inline", label: "\u2194", title: "Inline (no fluxo do texto)" },
|
|
3160
|
+
{ value: "wrap-left", label: "\u25E7", title: "Texto contorna \xE0 direita" },
|
|
3161
|
+
{ value: "wrap-right", label: "\u25E8", title: "Texto contorna \xE0 esquerda" },
|
|
3162
|
+
{ value: "behind", label: "\u25A2\u2193", title: "Atr\xE1s do texto" },
|
|
3163
|
+
{ value: "front", label: "\u25A2\u2191", title: "Na frente do texto" }
|
|
3164
|
+
];
|
|
3165
|
+
function Toolbar({ className }) {
|
|
3166
|
+
const editor = useEditorContext();
|
|
3167
|
+
const {
|
|
3168
|
+
isMarkActive,
|
|
3169
|
+
getActiveMarks,
|
|
3170
|
+
toggleMark,
|
|
3171
|
+
setMark,
|
|
3172
|
+
removeMark,
|
|
3173
|
+
getBlockType,
|
|
3174
|
+
getBlockAttr,
|
|
3175
|
+
setBlockType,
|
|
3176
|
+
setBlockAttr,
|
|
3177
|
+
isListActive,
|
|
3178
|
+
toggleList,
|
|
3179
|
+
indentList,
|
|
3180
|
+
dedentList
|
|
3181
|
+
} = editor;
|
|
3182
|
+
const active = getActiveMarks();
|
|
3183
|
+
const blockType = getBlockType();
|
|
3184
|
+
const blockLevel = getBlockAttr("level");
|
|
3185
|
+
const blockAlign = getBlockAttr("align");
|
|
3186
|
+
const blockDir = getBlockAttr("dir");
|
|
3187
|
+
const stop = (e) => {
|
|
3188
|
+
const t = e.target;
|
|
3189
|
+
if (t && (t.tagName === "SELECT" || t.tagName === "INPUT" || t.tagName === "OPTION")) {
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
e.preventDefault();
|
|
3193
|
+
};
|
|
3194
|
+
const onToggle = (name) => (e) => {
|
|
3195
|
+
e.preventDefault();
|
|
3196
|
+
toggleMark(name);
|
|
3197
|
+
};
|
|
3198
|
+
const onColorChange = (e) => {
|
|
3199
|
+
setMark("color", e.target.value);
|
|
3200
|
+
};
|
|
3201
|
+
const onFontFamilyChange = (e) => {
|
|
3202
|
+
const v = e.target.value;
|
|
3203
|
+
if (v === "") removeMark("fontFamily");
|
|
3204
|
+
else setMark("fontFamily", v);
|
|
3205
|
+
};
|
|
3206
|
+
const onFontSizeChange = (e) => {
|
|
3207
|
+
const v = e.target.value;
|
|
3208
|
+
if (v === "") removeMark("fontSize");
|
|
3209
|
+
else setMark("fontSize", v);
|
|
3210
|
+
};
|
|
3211
|
+
const onBlockChange = (e) => {
|
|
3212
|
+
const opt = BLOCK_OPTIONS.find((o) => o.value === e.target.value);
|
|
3213
|
+
if (!opt) return;
|
|
3214
|
+
setBlockType(opt.type, opt.level !== void 0 ? { level: opt.level } : void 0);
|
|
3215
|
+
};
|
|
3216
|
+
const onAlign = (value) => (e) => {
|
|
3217
|
+
e.preventDefault();
|
|
3218
|
+
const selectedEmbed2 = editor.getSelectedEmbed();
|
|
3219
|
+
if (selectedEmbed2 && IMAGE_ALIGN_VALUES.has(value)) {
|
|
3220
|
+
editor.setImageAttrs(
|
|
3221
|
+
selectedEmbed2.blockIndex,
|
|
3222
|
+
selectedEmbed2.offset,
|
|
3223
|
+
{ align: value },
|
|
3224
|
+
selectedEmbed2.cellIndex
|
|
3225
|
+
);
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
setBlockAttr("align", value);
|
|
3229
|
+
};
|
|
3230
|
+
const onInsertImageClick = (0, import_react13.useCallback)(
|
|
3231
|
+
(e) => {
|
|
3232
|
+
e.preventDefault();
|
|
3233
|
+
imageInputRef.current?.click();
|
|
3234
|
+
},
|
|
3235
|
+
[]
|
|
3236
|
+
);
|
|
3237
|
+
const onImageInputChange = (0, import_react13.useCallback)(
|
|
3238
|
+
(e) => {
|
|
3239
|
+
const file = e.target.files?.[0];
|
|
3240
|
+
if (file) void editor.insertImageFromFile(file);
|
|
3241
|
+
e.target.value = "";
|
|
3242
|
+
},
|
|
3243
|
+
[editor]
|
|
3244
|
+
);
|
|
3245
|
+
const imageInputRef = (0, import_react13.useRef)(null);
|
|
3246
|
+
const onToggleDir = (e) => {
|
|
3247
|
+
e.preventDefault();
|
|
3248
|
+
const next = blockDir === "rtl" ? "ltr" : "rtl";
|
|
3249
|
+
setBlockAttr("dir", next);
|
|
3250
|
+
};
|
|
3251
|
+
const linkActive = isMarkActive("link");
|
|
3252
|
+
const onLinkClick = (0, import_react13.useCallback)(
|
|
3253
|
+
(e) => {
|
|
3254
|
+
e.preventDefault();
|
|
3255
|
+
const currentHref = active.link && typeof active.link === "object" && "href" in active.link ? active.link.href : "";
|
|
3256
|
+
void editor.requestLink(currentHref).then((href) => {
|
|
3257
|
+
if (href === null) return;
|
|
3258
|
+
if (href === "") removeMark("link");
|
|
3259
|
+
else setMark("link", { href });
|
|
3260
|
+
});
|
|
3261
|
+
},
|
|
3262
|
+
[active.link, editor, removeMark, setMark]
|
|
3263
|
+
);
|
|
3264
|
+
const colorValue = typeof active.color === "string" ? active.color : "#000000";
|
|
3265
|
+
const fontFamilyValue = typeof active.fontFamily === "string" ? active.fontFamily : "";
|
|
3266
|
+
const fontSizeValue = typeof active.fontSize === "string" ? active.fontSize : "";
|
|
3267
|
+
const blockValue = blockSelectValue(blockType, blockLevel);
|
|
3268
|
+
const selectedEmbed = editor.getSelectedEmbed();
|
|
3269
|
+
const currentImageLayout = selectedEmbed ? selectedEmbed.embed.layout ?? "inline" : "inline";
|
|
3270
|
+
const onImageLayoutClick = (layout) => (e) => {
|
|
3271
|
+
e.preventDefault();
|
|
3272
|
+
const sel = editor.getSelectedEmbed();
|
|
3273
|
+
if (!sel) return;
|
|
3274
|
+
const reset = layout !== (sel.embed.layout ?? "inline") ? { offsetX: 0, offsetY: 0 } : {};
|
|
3275
|
+
editor.setImageAttrs(
|
|
3276
|
+
sel.blockIndex,
|
|
3277
|
+
sel.offset,
|
|
3278
|
+
{ layout, ...reset },
|
|
3279
|
+
sel.cellIndex
|
|
3280
|
+
);
|
|
3281
|
+
};
|
|
3282
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
3283
|
+
"div",
|
|
3284
|
+
{
|
|
3285
|
+
className: className ?? "ed-toolbar",
|
|
3286
|
+
role: "toolbar",
|
|
3287
|
+
"aria-label": "Editor toolbar",
|
|
3288
|
+
onMouseDown: stop,
|
|
3289
|
+
children: [
|
|
3290
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Group, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
3291
|
+
"select",
|
|
3292
|
+
{
|
|
3293
|
+
className: "ed-toolbar-select",
|
|
3294
|
+
value: blockValue,
|
|
3295
|
+
onChange: onBlockChange,
|
|
3296
|
+
onMouseDown: stop,
|
|
3297
|
+
"aria-label": "Tipo de bloco",
|
|
3298
|
+
children: [
|
|
3299
|
+
blockValue === "" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: "\u2014 (misto)" }),
|
|
3300
|
+
BLOCK_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: o.value, children: o.label }, o.value))
|
|
3301
|
+
]
|
|
3302
|
+
}
|
|
3303
|
+
) }),
|
|
3304
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Group, { children: [
|
|
3305
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MarkButton, { title: "Negrito (\u2318B)", pressed: isMarkActive("bold"), onClick: onToggle("bold"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "B" }) }),
|
|
3306
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MarkButton, { title: "It\xE1lico (\u2318I)", pressed: isMarkActive("italic"), onClick: onToggle("italic"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("em", { children: "I" }) }),
|
|
3307
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MarkButton, { title: "Sublinhado (\u2318U)", pressed: isMarkActive("underline"), onClick: onToggle("underline"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("u", { children: "U" }) }),
|
|
3308
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MarkButton, { title: "Tachado (\u2318\u21E7X)", pressed: isMarkActive("strike"), onClick: onToggle("strike"), children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("s", { children: "S" }) })
|
|
3309
|
+
] }),
|
|
3310
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Group, { children: [
|
|
3311
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { className: "ed-toolbar-label", children: [
|
|
3312
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ed-toolbar-swatch", "aria-hidden": true, style: { background: colorValue } }),
|
|
3313
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { type: "color", value: colorValue, onChange: onColorChange, onMouseDown: stop, "aria-label": "Cor do texto" })
|
|
3314
|
+
] }),
|
|
3315
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3316
|
+
"button",
|
|
3317
|
+
{
|
|
3318
|
+
type: "button",
|
|
3319
|
+
className: "ed-toolbar-btn",
|
|
3320
|
+
title: "Remover cor",
|
|
3321
|
+
onMouseDown: stop,
|
|
3322
|
+
onClick: (e) => {
|
|
3323
|
+
e.preventDefault();
|
|
3324
|
+
removeMark("color");
|
|
3325
|
+
},
|
|
3326
|
+
children: "\u232B"
|
|
3327
|
+
}
|
|
3328
|
+
)
|
|
3329
|
+
] }),
|
|
3330
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Group, { children: [
|
|
3331
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
3332
|
+
"select",
|
|
3333
|
+
{
|
|
3334
|
+
className: "ed-toolbar-select",
|
|
3335
|
+
value: fontFamilyValue,
|
|
3336
|
+
onChange: onFontFamilyChange,
|
|
3337
|
+
onMouseDown: stop,
|
|
3338
|
+
"aria-label": "Fam\xEDlia da fonte",
|
|
3339
|
+
children: [
|
|
3340
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: "Fonte\u2026" }),
|
|
3341
|
+
FONT_FAMILIES.map((f) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: f, children: f }, f))
|
|
3342
|
+
]
|
|
3343
|
+
}
|
|
3344
|
+
),
|
|
3345
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
3346
|
+
"select",
|
|
3347
|
+
{
|
|
3348
|
+
className: "ed-toolbar-select",
|
|
3349
|
+
value: fontSizeValue,
|
|
3350
|
+
onChange: onFontSizeChange,
|
|
3351
|
+
onMouseDown: stop,
|
|
3352
|
+
"aria-label": "Tamanho da fonte",
|
|
3353
|
+
children: [
|
|
3354
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: "Tamanho\u2026" }),
|
|
3355
|
+
FONT_SIZES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: s, children: s }, s))
|
|
3356
|
+
]
|
|
3357
|
+
}
|
|
3358
|
+
)
|
|
3359
|
+
] }),
|
|
3360
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Group, { children: ALIGN_OPTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3361
|
+
"button",
|
|
3362
|
+
{
|
|
3363
|
+
type: "button",
|
|
3364
|
+
className: "ed-toolbar-btn",
|
|
3365
|
+
title: a.title,
|
|
3366
|
+
"aria-pressed": blockAlign === a.value,
|
|
3367
|
+
onMouseDown: stop,
|
|
3368
|
+
onClick: onAlign(a.value),
|
|
3369
|
+
children: a.label
|
|
3370
|
+
},
|
|
3371
|
+
a.value
|
|
3372
|
+
)) }),
|
|
3373
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Group, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3374
|
+
"button",
|
|
3375
|
+
{
|
|
3376
|
+
type: "button",
|
|
3377
|
+
className: "ed-toolbar-btn",
|
|
3378
|
+
"aria-pressed": blockDir === "rtl",
|
|
3379
|
+
title: blockDir === "rtl" ? "Mudar para LTR" : "Mudar para RTL (hebraico)",
|
|
3380
|
+
onMouseDown: stop,
|
|
3381
|
+
onClick: onToggleDir,
|
|
3382
|
+
children: blockDir === "rtl" ? "RTL" : "LTR"
|
|
3383
|
+
}
|
|
3384
|
+
) }),
|
|
3385
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Group, { children: [
|
|
3386
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3387
|
+
"button",
|
|
3388
|
+
{
|
|
3389
|
+
type: "button",
|
|
3390
|
+
className: "ed-toolbar-btn",
|
|
3391
|
+
"aria-pressed": isListActive("bullet"),
|
|
3392
|
+
title: "Lista com marcadores (\u2318\u21E78)",
|
|
3393
|
+
onMouseDown: stop,
|
|
3394
|
+
onClick: (e) => {
|
|
3395
|
+
e.preventDefault();
|
|
3396
|
+
toggleList("bullet");
|
|
3397
|
+
},
|
|
3398
|
+
children: "\u2022"
|
|
3399
|
+
}
|
|
3400
|
+
),
|
|
3401
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3402
|
+
"button",
|
|
3403
|
+
{
|
|
3404
|
+
type: "button",
|
|
3405
|
+
className: "ed-toolbar-btn",
|
|
3406
|
+
"aria-pressed": isListActive("ordered"),
|
|
3407
|
+
title: "Lista numerada (\u2318\u21E77)",
|
|
3408
|
+
onMouseDown: stop,
|
|
3409
|
+
onClick: (e) => {
|
|
3410
|
+
e.preventDefault();
|
|
3411
|
+
toggleList("ordered");
|
|
3412
|
+
},
|
|
3413
|
+
children: "1."
|
|
3414
|
+
}
|
|
3415
|
+
),
|
|
3416
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3417
|
+
"button",
|
|
3418
|
+
{
|
|
3419
|
+
type: "button",
|
|
3420
|
+
className: "ed-toolbar-btn",
|
|
3421
|
+
title: "Diminuir recuo (\u21E7Tab)",
|
|
3422
|
+
onMouseDown: stop,
|
|
3423
|
+
onClick: (e) => {
|
|
3424
|
+
e.preventDefault();
|
|
3425
|
+
dedentList();
|
|
3426
|
+
},
|
|
3427
|
+
children: "\u21E4"
|
|
3428
|
+
}
|
|
3429
|
+
),
|
|
3430
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3431
|
+
"button",
|
|
3432
|
+
{
|
|
3433
|
+
type: "button",
|
|
3434
|
+
className: "ed-toolbar-btn",
|
|
3435
|
+
title: "Aumentar recuo (Tab)",
|
|
3436
|
+
onMouseDown: stop,
|
|
3437
|
+
onClick: (e) => {
|
|
3438
|
+
e.preventDefault();
|
|
3439
|
+
indentList();
|
|
3440
|
+
},
|
|
3441
|
+
children: "\u21E5"
|
|
3442
|
+
}
|
|
3443
|
+
)
|
|
3444
|
+
] }),
|
|
3445
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Group, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3446
|
+
"button",
|
|
3447
|
+
{
|
|
3448
|
+
type: "button",
|
|
3449
|
+
className: "ed-toolbar-btn",
|
|
3450
|
+
"aria-pressed": linkActive,
|
|
3451
|
+
title: linkActive ? "Remover link (\u2318K)" : "Adicionar link (\u2318K)",
|
|
3452
|
+
onMouseDown: stop,
|
|
3453
|
+
onClick: onLinkClick,
|
|
3454
|
+
children: "\u{1F517}"
|
|
3455
|
+
}
|
|
3456
|
+
) }),
|
|
3457
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Group, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TableMenu, {}) }),
|
|
3458
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Group, { children: [
|
|
3459
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3460
|
+
"button",
|
|
3461
|
+
{
|
|
3462
|
+
type: "button",
|
|
3463
|
+
className: "ed-toolbar-btn",
|
|
3464
|
+
title: "Inserir imagem",
|
|
3465
|
+
onMouseDown: stop,
|
|
3466
|
+
onClick: onInsertImageClick,
|
|
3467
|
+
children: "\u{1F5BC}"
|
|
3468
|
+
}
|
|
3469
|
+
),
|
|
3470
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3471
|
+
"input",
|
|
3472
|
+
{
|
|
3473
|
+
ref: imageInputRef,
|
|
3474
|
+
type: "file",
|
|
3475
|
+
accept: "image/*",
|
|
3476
|
+
hidden: true,
|
|
3477
|
+
onChange: onImageInputChange
|
|
3478
|
+
}
|
|
3479
|
+
)
|
|
3480
|
+
] }),
|
|
3481
|
+
selectedEmbed && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Group, { children: [
|
|
3482
|
+
IMAGE_LAYOUT_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3483
|
+
"button",
|
|
3484
|
+
{
|
|
3485
|
+
type: "button",
|
|
3486
|
+
className: "ed-toolbar-btn",
|
|
3487
|
+
title: opt.title,
|
|
3488
|
+
"aria-pressed": currentImageLayout === opt.value,
|
|
3489
|
+
onMouseDown: stop,
|
|
3490
|
+
onClick: onImageLayoutClick(opt.value),
|
|
3491
|
+
children: opt.label
|
|
3492
|
+
},
|
|
3493
|
+
opt.value
|
|
3494
|
+
)),
|
|
3495
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3496
|
+
"button",
|
|
3497
|
+
{
|
|
3498
|
+
type: "button",
|
|
3499
|
+
className: "ed-toolbar-btn",
|
|
3500
|
+
title: "Legenda da imagem",
|
|
3501
|
+
"aria-pressed": !!selectedEmbed.embed.caption,
|
|
3502
|
+
onMouseDown: stop,
|
|
3503
|
+
onClick: (e) => {
|
|
3504
|
+
e.preventDefault();
|
|
3505
|
+
const initialCaption = selectedEmbed.embed.caption ?? "";
|
|
3506
|
+
const initialAlign = selectedEmbed.embed.captionAlign ?? "center";
|
|
3507
|
+
void editor.requestImageCaption(initialCaption, initialAlign).then((result) => {
|
|
3508
|
+
if (result === null) return;
|
|
3509
|
+
const empty = result.caption.length === 0;
|
|
3510
|
+
editor.setImageAttrs(
|
|
3511
|
+
selectedEmbed.blockIndex,
|
|
3512
|
+
selectedEmbed.offset,
|
|
3513
|
+
{
|
|
3514
|
+
caption: empty ? void 0 : result.caption,
|
|
3515
|
+
captionAlign: empty ? void 0 : result.align
|
|
3516
|
+
},
|
|
3517
|
+
selectedEmbed.cellIndex
|
|
3518
|
+
);
|
|
3519
|
+
});
|
|
3520
|
+
},
|
|
3521
|
+
children: "Legenda"
|
|
3522
|
+
}
|
|
3523
|
+
)
|
|
3524
|
+
] }),
|
|
3525
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Group, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3526
|
+
"button",
|
|
3527
|
+
{
|
|
3528
|
+
type: "button",
|
|
3529
|
+
className: "ed-toolbar-btn",
|
|
3530
|
+
title: "Configurar p\xE1gina (tamanho, margens)",
|
|
3531
|
+
onMouseDown: stop,
|
|
3532
|
+
onClick: (e) => {
|
|
3533
|
+
e.preventDefault();
|
|
3534
|
+
void editor.requestPageConfig();
|
|
3535
|
+
},
|
|
3536
|
+
children: "\u2699"
|
|
3537
|
+
}
|
|
3538
|
+
) })
|
|
3539
|
+
]
|
|
3540
|
+
}
|
|
3541
|
+
);
|
|
3542
|
+
}
|
|
3543
|
+
function TableMenu() {
|
|
3544
|
+
const editor = useEditorContext();
|
|
3545
|
+
const inTable = editor.isInTable();
|
|
3546
|
+
const [open, setOpen] = (0, import_react13.useState)(false);
|
|
3547
|
+
const rootRef = (0, import_react13.useRef)(null);
|
|
3548
|
+
(0, import_react13.useEffect)(() => {
|
|
3549
|
+
if (!open) return;
|
|
3550
|
+
const onDown = (e) => {
|
|
3551
|
+
if (!rootRef.current) return;
|
|
3552
|
+
if (rootRef.current.contains(e.target)) return;
|
|
3553
|
+
setOpen(false);
|
|
3554
|
+
};
|
|
3555
|
+
document.addEventListener("mousedown", onDown);
|
|
3556
|
+
return () => document.removeEventListener("mousedown", onDown);
|
|
3557
|
+
}, [open]);
|
|
3558
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
|
|
3559
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3560
|
+
"button",
|
|
3561
|
+
{
|
|
3562
|
+
type: "button",
|
|
3563
|
+
className: "ed-toolbar-btn",
|
|
3564
|
+
title: "Tabela",
|
|
3565
|
+
"aria-pressed": open,
|
|
3566
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3567
|
+
onClick: (e) => {
|
|
3568
|
+
e.preventDefault();
|
|
3569
|
+
setOpen((v) => !v);
|
|
3570
|
+
},
|
|
3571
|
+
children: "\u25A6"
|
|
3572
|
+
}
|
|
3573
|
+
),
|
|
3574
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
3575
|
+
"div",
|
|
3576
|
+
{
|
|
3577
|
+
className: "ed-table-popover",
|
|
3578
|
+
role: "menu",
|
|
3579
|
+
onMouseDown: (e) => {
|
|
3580
|
+
const t = e.target;
|
|
3581
|
+
if (!t || t.tagName !== "INPUT" && t.tagName !== "BUTTON") {
|
|
3582
|
+
e.preventDefault();
|
|
3583
|
+
}
|
|
3584
|
+
},
|
|
3585
|
+
children: [
|
|
3586
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3587
|
+
TableSizePicker,
|
|
3588
|
+
{
|
|
3589
|
+
onPick: (rows, cols) => {
|
|
3590
|
+
setOpen(false);
|
|
3591
|
+
editor.insertTable(rows, cols);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
),
|
|
3595
|
+
inTable && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ed-table-actions", children: [
|
|
3596
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("hr", {}),
|
|
3597
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3598
|
+
setOpen(false);
|
|
3599
|
+
editor.insertRowAbove();
|
|
3600
|
+
}, children: "Inserir linha acima" }),
|
|
3601
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3602
|
+
setOpen(false);
|
|
3603
|
+
editor.insertRowBelow();
|
|
3604
|
+
}, children: "Inserir linha abaixo" }),
|
|
3605
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3606
|
+
setOpen(false);
|
|
3607
|
+
editor.insertColumnLeft();
|
|
3608
|
+
}, children: "Inserir coluna \xE0 esquerda" }),
|
|
3609
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3610
|
+
setOpen(false);
|
|
3611
|
+
editor.insertColumnRight();
|
|
3612
|
+
}, children: "Inserir coluna \xE0 direita" }),
|
|
3613
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3614
|
+
setOpen(false);
|
|
3615
|
+
editor.deleteCurrentRow();
|
|
3616
|
+
}, children: "Excluir linha" }),
|
|
3617
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3618
|
+
setOpen(false);
|
|
3619
|
+
editor.deleteCurrentColumn();
|
|
3620
|
+
}, children: "Excluir coluna" }),
|
|
3621
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("hr", {}),
|
|
3622
|
+
editor.hasTableRectSelection() && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3623
|
+
"button",
|
|
3624
|
+
{
|
|
3625
|
+
type: "button",
|
|
3626
|
+
onClick: () => {
|
|
3627
|
+
setOpen(false);
|
|
3628
|
+
editor.mergeSelection();
|
|
3629
|
+
},
|
|
3630
|
+
children: "Mesclar c\xE9lulas selecionadas"
|
|
3631
|
+
}
|
|
3632
|
+
),
|
|
3633
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3634
|
+
setOpen(false);
|
|
3635
|
+
editor.mergeRight();
|
|
3636
|
+
}, children: "Mesclar com c\xE9lula \xE0 direita" }),
|
|
3637
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3638
|
+
setOpen(false);
|
|
3639
|
+
editor.mergeDown();
|
|
3640
|
+
}, children: "Mesclar com c\xE9lula abaixo" }),
|
|
3641
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", onClick: () => {
|
|
3642
|
+
setOpen(false);
|
|
3643
|
+
editor.splitCell();
|
|
3644
|
+
}, children: "Dividir c\xE9lulas mescladas" }),
|
|
3645
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3646
|
+
"button",
|
|
3647
|
+
{
|
|
3648
|
+
type: "button",
|
|
3649
|
+
className: "ed-table-delete",
|
|
3650
|
+
onClick: () => {
|
|
3651
|
+
setOpen(false);
|
|
3652
|
+
editor.deleteTable();
|
|
3653
|
+
},
|
|
3654
|
+
children: "Excluir tabela"
|
|
3655
|
+
}
|
|
3656
|
+
)
|
|
3657
|
+
] })
|
|
3658
|
+
]
|
|
3659
|
+
}
|
|
3660
|
+
)
|
|
3661
|
+
] });
|
|
3662
|
+
}
|
|
3663
|
+
var PICKER_MAX_ROWS = 8;
|
|
3664
|
+
var PICKER_MAX_COLS = 10;
|
|
3665
|
+
function TableSizePicker({ onPick }) {
|
|
3666
|
+
const [hover, setHover] = (0, import_react13.useState)(null);
|
|
3667
|
+
const label = hover ? `${hover.r + 1} \xD7 ${hover.c + 1}` : "Inserir tabela";
|
|
3668
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ed-table-picker", children: [
|
|
3669
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ed-table-picker-label", children: label }),
|
|
3670
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3671
|
+
"div",
|
|
3672
|
+
{
|
|
3673
|
+
className: "ed-table-picker-grid",
|
|
3674
|
+
style: {
|
|
3675
|
+
gridTemplateColumns: `repeat(${PICKER_MAX_COLS}, 18px)`,
|
|
3676
|
+
gridTemplateRows: `repeat(${PICKER_MAX_ROWS}, 18px)`
|
|
3677
|
+
},
|
|
3678
|
+
onMouseLeave: () => setHover(null),
|
|
3679
|
+
children: Array.from(
|
|
3680
|
+
{ length: PICKER_MAX_ROWS },
|
|
3681
|
+
(_, r) => Array.from({ length: PICKER_MAX_COLS }, (_2, c) => {
|
|
3682
|
+
const filled = hover != null && r <= hover.r && c <= hover.c;
|
|
3683
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3684
|
+
"button",
|
|
3685
|
+
{
|
|
3686
|
+
type: "button",
|
|
3687
|
+
className: `ed-table-picker-cell${filled ? " filled" : ""}`,
|
|
3688
|
+
onMouseEnter: () => setHover({ r, c }),
|
|
3689
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3690
|
+
onClick: (e) => {
|
|
3691
|
+
e.preventDefault();
|
|
3692
|
+
onPick(r + 1, c + 1);
|
|
3693
|
+
},
|
|
3694
|
+
"aria-label": `${r + 1} \xD7 ${c + 1}`
|
|
3695
|
+
},
|
|
3696
|
+
`${r}-${c}`
|
|
3697
|
+
);
|
|
3698
|
+
})
|
|
3699
|
+
)
|
|
3700
|
+
}
|
|
3701
|
+
)
|
|
3702
|
+
] });
|
|
3703
|
+
}
|
|
3704
|
+
function blockSelectValue(type, level) {
|
|
3705
|
+
if (type === "mixed") return "";
|
|
3706
|
+
if (type === "heading") {
|
|
3707
|
+
if (typeof level === "number" && level >= 1 && level <= 6) return `h${level}`;
|
|
3708
|
+
return "h1";
|
|
3709
|
+
}
|
|
3710
|
+
return type;
|
|
3711
|
+
}
|
|
3712
|
+
function Group({ children }) {
|
|
3713
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ed-toolbar-group", children });
|
|
3714
|
+
}
|
|
3715
|
+
function MarkButton({ title, pressed, onClick, children }) {
|
|
3716
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
3717
|
+
"button",
|
|
3718
|
+
{
|
|
3719
|
+
type: "button",
|
|
3720
|
+
className: "ed-toolbar-btn",
|
|
3721
|
+
"aria-pressed": pressed,
|
|
3722
|
+
title,
|
|
3723
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3724
|
+
onClick,
|
|
3725
|
+
children
|
|
3726
|
+
}
|
|
3727
|
+
);
|
|
3728
|
+
}
|
|
3729
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3730
|
+
0 && (module.exports = {
|
|
3731
|
+
A4_PAGE,
|
|
3732
|
+
Editor,
|
|
3733
|
+
EditorProvider,
|
|
3734
|
+
ImageCaptionDialog,
|
|
3735
|
+
LinkDialog,
|
|
3736
|
+
NodeView,
|
|
3737
|
+
PageConfigDialog,
|
|
3738
|
+
Toolbar,
|
|
3739
|
+
applyDomSelection,
|
|
3740
|
+
contentHeight,
|
|
3741
|
+
contentWidth,
|
|
3742
|
+
defaultPageLayout,
|
|
3743
|
+
deltaLength,
|
|
3744
|
+
findBlockIndex,
|
|
3745
|
+
getBlockElement,
|
|
3746
|
+
getCellElement,
|
|
3747
|
+
getFragmentForOffset,
|
|
3748
|
+
isTableRectSelection,
|
|
3749
|
+
readDomSelection,
|
|
3750
|
+
renderInline,
|
|
3751
|
+
selectionsEqual,
|
|
3752
|
+
sliceDelta,
|
|
3753
|
+
textOffsetWithin,
|
|
3754
|
+
useEditor,
|
|
3755
|
+
useEditorContext,
|
|
3756
|
+
usePageLayout,
|
|
3757
|
+
usePageSettings
|
|
3758
|
+
});
|
|
3759
|
+
//# sourceMappingURL=index.cjs.map
|