mgcode-docx-editor 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 +21 -0
- package/README.md +277 -0
- package/dist/index.d.mts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +1483 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1463 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1463 @@
|
|
|
1
|
+
// src/DocxEditor.tsx
|
|
2
|
+
import {
|
|
3
|
+
forwardRef,
|
|
4
|
+
useCallback as useCallback2,
|
|
5
|
+
useEffect,
|
|
6
|
+
useImperativeHandle,
|
|
7
|
+
useRef as useRef3,
|
|
8
|
+
useState as useState2
|
|
9
|
+
} from "react";
|
|
10
|
+
import { Box as Box3, Chip, CircularProgress } from "@mui/material";
|
|
11
|
+
|
|
12
|
+
// src/docx/generate.ts
|
|
13
|
+
import HTMLtoDOCX from "@turbodocx/html-to-docx";
|
|
14
|
+
import JSZip from "jszip";
|
|
15
|
+
import { Buffer as BufferPolyfill } from "buffer";
|
|
16
|
+
|
|
17
|
+
// src/docx/pageFields.ts
|
|
18
|
+
var PAGE_NUMBER_TOKEN = "[[PAGINA]]";
|
|
19
|
+
var TOTAL_PAGES_TOKEN = "[[TOTAL_PAGINAS]]";
|
|
20
|
+
var FIELD_BY_TOKEN = [
|
|
21
|
+
{ token: PAGE_NUMBER_TOKEN, instr: "PAGE" },
|
|
22
|
+
{ token: TOTAL_PAGES_TOKEN, instr: "NUMPAGES" }
|
|
23
|
+
];
|
|
24
|
+
function hasPageFieldTokens(html) {
|
|
25
|
+
if (!html) return false;
|
|
26
|
+
return FIELD_BY_TOKEN.some(({ token }) => html.includes(token));
|
|
27
|
+
}
|
|
28
|
+
var W_NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
29
|
+
var RUN_PATTERN = /<(w:)?r(?:\s[^>]*)?>[\s\S]*?<\/(?:w:)?r>/g;
|
|
30
|
+
function splitTextByTokens(text) {
|
|
31
|
+
const parts = [];
|
|
32
|
+
let rest = text;
|
|
33
|
+
while (rest.length > 0) {
|
|
34
|
+
let earliest = -1;
|
|
35
|
+
let found = null;
|
|
36
|
+
for (const candidate of FIELD_BY_TOKEN) {
|
|
37
|
+
const i = rest.indexOf(candidate.token);
|
|
38
|
+
if (i !== -1 && (earliest === -1 || i < earliest)) {
|
|
39
|
+
earliest = i;
|
|
40
|
+
found = candidate;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!found) {
|
|
44
|
+
parts.push({ kind: "text", value: rest });
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
if (earliest > 0) parts.push({ kind: "text", value: rest.slice(0, earliest) });
|
|
48
|
+
parts.push({ kind: "field", instr: found.instr });
|
|
49
|
+
rest = rest.slice(earliest + found.token.length);
|
|
50
|
+
}
|
|
51
|
+
return parts;
|
|
52
|
+
}
|
|
53
|
+
function rebuildRun(runXml, prefix) {
|
|
54
|
+
const tMatch = runXml.match(/<(?:w:)?t([^>]*)>([\s\S]*?)<\/(?:w:)?t>/);
|
|
55
|
+
if (!tMatch) return runXml;
|
|
56
|
+
const [, tAttrs, text] = tMatch;
|
|
57
|
+
if (!FIELD_BY_TOKEN.some(({ token }) => text.includes(token))) return runXml;
|
|
58
|
+
const rPrMatch = runXml.match(/<(?:w:)?rPr(?:\/>|>[\s\S]*?<\/(?:w:)?rPr>)/);
|
|
59
|
+
const props = rPrMatch ? rPrMatch[0] : "";
|
|
60
|
+
const p = prefix;
|
|
61
|
+
const tOpen = `<${p}t${tAttrs}>`;
|
|
62
|
+
const tClose = `</${p}t>`;
|
|
63
|
+
const nsDecl = p ? "" : ` xmlns:w="${W_NAMESPACE}"`;
|
|
64
|
+
return splitTextByTokens(text).map(
|
|
65
|
+
(part) => part.kind === "text" ? `<${p}r>${props}${tOpen}${part.value}${tClose}</${p}r>` : (
|
|
66
|
+
// "1" é o valor em cache exibido até o Word/LibreOffice atualizar o campo.
|
|
67
|
+
`<${p}fldSimple${nsDecl} w:instr=" ${part.instr} "><${p}r>${props}${tOpen}1${tClose}</${p}r></${p}fldSimple>`
|
|
68
|
+
)
|
|
69
|
+
).join("");
|
|
70
|
+
}
|
|
71
|
+
function injectPageFields(xml) {
|
|
72
|
+
return xml.replace(
|
|
73
|
+
RUN_PATTERN,
|
|
74
|
+
(runXml, prefix) => rebuildRun(runXml, prefix ?? "")
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/docx/generate.ts
|
|
79
|
+
var DEFAULT_MARGINS = {
|
|
80
|
+
top: 1440,
|
|
81
|
+
right: 1440,
|
|
82
|
+
bottom: 1440,
|
|
83
|
+
left: 1440
|
|
84
|
+
};
|
|
85
|
+
var PAGE_SIZE_A4_TWIPS = { width: 11906, height: 16838 };
|
|
86
|
+
function ensureBufferGlobal() {
|
|
87
|
+
const g = globalThis;
|
|
88
|
+
if (typeof g.Buffer === "undefined") g.Buffer = BufferPolyfill;
|
|
89
|
+
}
|
|
90
|
+
async function toArrayBuffer(out) {
|
|
91
|
+
if (out instanceof ArrayBuffer) return out;
|
|
92
|
+
if (typeof Blob !== "undefined" && out instanceof Blob) {
|
|
93
|
+
return out.arrayBuffer();
|
|
94
|
+
}
|
|
95
|
+
const u8 = out;
|
|
96
|
+
return u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
|
|
97
|
+
}
|
|
98
|
+
function wrapBody(bodyHtml) {
|
|
99
|
+
return `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>${bodyHtml}</body></html>`;
|
|
100
|
+
}
|
|
101
|
+
async function modelToDocx(model, options = {}) {
|
|
102
|
+
ensureBufferGlobal();
|
|
103
|
+
const margins = options.margins ?? DEFAULT_MARGINS;
|
|
104
|
+
const hasHeader = !!model.header && model.header.trim().length > 0;
|
|
105
|
+
const hasFooter = !!model.footer && model.footer.trim().length > 0;
|
|
106
|
+
const out = await HTMLtoDOCX(
|
|
107
|
+
wrapBody(model.body),
|
|
108
|
+
hasHeader ? model.header : null,
|
|
109
|
+
{
|
|
110
|
+
header: hasHeader,
|
|
111
|
+
footer: hasFooter,
|
|
112
|
+
margins,
|
|
113
|
+
pageSize: PAGE_SIZE_A4_TWIPS,
|
|
114
|
+
font: options.font ?? "Arial"
|
|
115
|
+
},
|
|
116
|
+
hasFooter ? model.footer : null
|
|
117
|
+
);
|
|
118
|
+
const buffer = await toArrayBuffer(out);
|
|
119
|
+
if (!hasPageFieldTokens(model.header) && !hasPageFieldTokens(model.footer)) {
|
|
120
|
+
return buffer;
|
|
121
|
+
}
|
|
122
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
123
|
+
const partNames = Object.keys(zip.files).filter(
|
|
124
|
+
(name) => /^word\/(header|footer)\d*\.xml$/.test(name)
|
|
125
|
+
);
|
|
126
|
+
for (const name of partNames) {
|
|
127
|
+
const xml = await zip.file(name).async("string");
|
|
128
|
+
zip.file(name, injectPageFields(xml));
|
|
129
|
+
}
|
|
130
|
+
return zip.generateAsync({ type: "arraybuffer" });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/docx/importDocx.ts
|
|
134
|
+
import mammoth from "mammoth";
|
|
135
|
+
async function bufferToModel(buffer) {
|
|
136
|
+
const input = typeof Buffer !== "undefined" ? { buffer: Buffer.from(buffer) } : { arrayBuffer: buffer };
|
|
137
|
+
const { value: bodyHtml } = await mammoth.convertToHtml(input);
|
|
138
|
+
return {
|
|
139
|
+
body: bodyHtml
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/docx/pagination.ts
|
|
144
|
+
function computePageLayout(blocks, metrics) {
|
|
145
|
+
const { usableHeightPx, interPageGapPx } = metrics;
|
|
146
|
+
const stride = usableHeightPx + interPageGapPx;
|
|
147
|
+
const spacers = [];
|
|
148
|
+
let added = 0;
|
|
149
|
+
let maxEnd = 0;
|
|
150
|
+
blocks.forEach((block, index) => {
|
|
151
|
+
let start = block.top + added;
|
|
152
|
+
const page = Math.floor(start / stride);
|
|
153
|
+
const regionStart = page * stride;
|
|
154
|
+
const regionEnd = regionStart + usableHeightPx;
|
|
155
|
+
const fitsInAPage = block.height <= usableHeightPx;
|
|
156
|
+
const crossesBottom = start + block.height > regionEnd;
|
|
157
|
+
if (start >= regionEnd || crossesBottom && fitsInAPage && start > regionStart) {
|
|
158
|
+
const nextStart = (page + 1) * stride;
|
|
159
|
+
const height = nextStart - start;
|
|
160
|
+
spacers.push({ beforeBlockIndex: index, heightPx: height });
|
|
161
|
+
added += height;
|
|
162
|
+
start = nextStart;
|
|
163
|
+
}
|
|
164
|
+
maxEnd = Math.max(maxEnd, start + block.height);
|
|
165
|
+
});
|
|
166
|
+
const pageCount = Math.max(1, Math.floor(Math.max(0, maxEnd - 1) / stride) + 1);
|
|
167
|
+
return { spacers, pageCount };
|
|
168
|
+
}
|
|
169
|
+
var PAGE_SPACER_ATTR = "data-page-spacer";
|
|
170
|
+
function createSpacerElement(heightPx) {
|
|
171
|
+
const el = document.createElement("div");
|
|
172
|
+
el.setAttribute(PAGE_SPACER_ATTR, "true");
|
|
173
|
+
el.setAttribute("contenteditable", "false");
|
|
174
|
+
el.setAttribute("aria-hidden", "true");
|
|
175
|
+
el.style.height = `${heightPx}px`;
|
|
176
|
+
el.style.userSelect = "none";
|
|
177
|
+
el.style.pointerEvents = "none";
|
|
178
|
+
return el;
|
|
179
|
+
}
|
|
180
|
+
function stripPageSpacers(html) {
|
|
181
|
+
const tmp = document.createElement("div");
|
|
182
|
+
tmp.innerHTML = html;
|
|
183
|
+
tmp.querySelectorAll(`[${PAGE_SPACER_ATTR}]`).forEach((n) => n.remove());
|
|
184
|
+
return tmp.innerHTML;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/editorCommands.ts
|
|
188
|
+
function execFormat(command, value) {
|
|
189
|
+
document.execCommand("styleWithCSS", false, "true");
|
|
190
|
+
document.execCommand(command, false, value);
|
|
191
|
+
}
|
|
192
|
+
function setFontSize(region, pt) {
|
|
193
|
+
document.execCommand("styleWithCSS", false, "false");
|
|
194
|
+
document.execCommand("fontSize", false, "7");
|
|
195
|
+
const marked = region.querySelectorAll('font[size="7"]');
|
|
196
|
+
marked.forEach((el) => {
|
|
197
|
+
el.removeAttribute("size");
|
|
198
|
+
el.style.fontSize = `${pt}pt`;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
function setFontFamily(family) {
|
|
202
|
+
document.execCommand("styleWithCSS", false, "true");
|
|
203
|
+
document.execCommand("fontName", false, family);
|
|
204
|
+
}
|
|
205
|
+
function insertTextIntoRegion(region, text) {
|
|
206
|
+
region.focus();
|
|
207
|
+
const selection = window.getSelection();
|
|
208
|
+
const node = document.createTextNode(text);
|
|
209
|
+
if (selection && selection.rangeCount > 0 && selection.anchorNode && region.contains(selection.anchorNode)) {
|
|
210
|
+
const range = selection.getRangeAt(0);
|
|
211
|
+
range.deleteContents();
|
|
212
|
+
range.insertNode(node);
|
|
213
|
+
range.setStartAfter(node);
|
|
214
|
+
range.setEndAfter(node);
|
|
215
|
+
selection.removeAllRanges();
|
|
216
|
+
selection.addRange(range);
|
|
217
|
+
} else {
|
|
218
|
+
region.appendChild(node);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/docx/units.ts
|
|
223
|
+
var TWIPS_PER_INCH = 1440;
|
|
224
|
+
var PX_PER_INCH = 96;
|
|
225
|
+
function pxToTwips(px) {
|
|
226
|
+
return Math.round(px / PX_PER_INCH * TWIPS_PER_INCH);
|
|
227
|
+
}
|
|
228
|
+
function twipsToPx(twips) {
|
|
229
|
+
return twips / TWIPS_PER_INCH * PX_PER_INCH;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/Toolbar.tsx
|
|
233
|
+
import { useRef, useState } from "react";
|
|
234
|
+
import {
|
|
235
|
+
Box,
|
|
236
|
+
Divider,
|
|
237
|
+
IconButton,
|
|
238
|
+
Menu,
|
|
239
|
+
MenuItem,
|
|
240
|
+
Select,
|
|
241
|
+
Tooltip,
|
|
242
|
+
Button,
|
|
243
|
+
Typography
|
|
244
|
+
} from "@mui/material";
|
|
245
|
+
import UndoIcon from "@mui/icons-material/Undo";
|
|
246
|
+
import RedoIcon from "@mui/icons-material/Redo";
|
|
247
|
+
import FormatBoldIcon from "@mui/icons-material/FormatBold";
|
|
248
|
+
import FormatItalicIcon from "@mui/icons-material/FormatItalic";
|
|
249
|
+
import FormatUnderlinedIcon from "@mui/icons-material/FormatUnderlined";
|
|
250
|
+
import StrikethroughSIcon from "@mui/icons-material/StrikethroughS";
|
|
251
|
+
import FormatColorTextIcon from "@mui/icons-material/FormatColorText";
|
|
252
|
+
import BorderColorIcon from "@mui/icons-material/BorderColor";
|
|
253
|
+
import LinkIcon from "@mui/icons-material/Link";
|
|
254
|
+
import SuperscriptIcon from "@mui/icons-material/Superscript";
|
|
255
|
+
import SubscriptIcon from "@mui/icons-material/Subscript";
|
|
256
|
+
import FormatAlignLeftIcon from "@mui/icons-material/FormatAlignLeft";
|
|
257
|
+
import FormatAlignCenterIcon from "@mui/icons-material/FormatAlignCenter";
|
|
258
|
+
import FormatAlignRightIcon from "@mui/icons-material/FormatAlignRight";
|
|
259
|
+
import FormatAlignJustifyIcon from "@mui/icons-material/FormatAlignJustify";
|
|
260
|
+
import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted";
|
|
261
|
+
import FormatListNumberedIcon from "@mui/icons-material/FormatListNumbered";
|
|
262
|
+
import FormatIndentDecreaseIcon from "@mui/icons-material/FormatIndentDecrease";
|
|
263
|
+
import FormatIndentIncreaseIcon from "@mui/icons-material/FormatIndentIncrease";
|
|
264
|
+
import FormatClearIcon from "@mui/icons-material/FormatClear";
|
|
265
|
+
import ImageIcon from "@mui/icons-material/Image";
|
|
266
|
+
import DataObjectIcon from "@mui/icons-material/DataObject";
|
|
267
|
+
import AddIcon from "@mui/icons-material/Add";
|
|
268
|
+
import RemoveIcon from "@mui/icons-material/Remove";
|
|
269
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
270
|
+
var FONT_FAMILIES = [
|
|
271
|
+
"Arial",
|
|
272
|
+
"Times New Roman",
|
|
273
|
+
"Calibri",
|
|
274
|
+
"Courier New",
|
|
275
|
+
"Georgia",
|
|
276
|
+
"Verdana"
|
|
277
|
+
];
|
|
278
|
+
var BLOCK_FORMATS = [
|
|
279
|
+
{ label: "Texto normal", tag: "P" },
|
|
280
|
+
{ label: "T\xEDtulo 1", tag: "H1" },
|
|
281
|
+
{ label: "T\xEDtulo 2", tag: "H2" },
|
|
282
|
+
{ label: "T\xEDtulo 3", tag: "H3" }
|
|
283
|
+
];
|
|
284
|
+
function Toolbar(props) {
|
|
285
|
+
const [fontFamily, setFontFamilyState] = useState("Arial");
|
|
286
|
+
const [fontSize, setFontSizeState] = useState(11);
|
|
287
|
+
const [blockTag, setBlockTag] = useState("P");
|
|
288
|
+
const [varAnchor, setVarAnchor] = useState(null);
|
|
289
|
+
const colorInputRef = useRef(null);
|
|
290
|
+
const highlightInputRef = useRef(null);
|
|
291
|
+
const changeFontSize = (next) => {
|
|
292
|
+
const clamped = Math.min(96, Math.max(1, next));
|
|
293
|
+
setFontSizeState(clamped);
|
|
294
|
+
props.onFontSize(clamped);
|
|
295
|
+
};
|
|
296
|
+
return /* @__PURE__ */ jsxs(
|
|
297
|
+
Box,
|
|
298
|
+
{
|
|
299
|
+
sx: {
|
|
300
|
+
display: "flex",
|
|
301
|
+
alignItems: "center",
|
|
302
|
+
flexWrap: "wrap",
|
|
303
|
+
gap: 0.5,
|
|
304
|
+
px: 1,
|
|
305
|
+
py: 0.5,
|
|
306
|
+
borderBottom: 1,
|
|
307
|
+
borderColor: "divider",
|
|
308
|
+
bgcolor: "background.paper"
|
|
309
|
+
},
|
|
310
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
311
|
+
children: [
|
|
312
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Desfazer", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("undo"), children: /* @__PURE__ */ jsx(UndoIcon, { fontSize: "small" }) }) }),
|
|
313
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Refazer", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("redo"), children: /* @__PURE__ */ jsx(RedoIcon, { fontSize: "small" }) }) }),
|
|
314
|
+
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true, sx: { mx: 0.5 } }),
|
|
315
|
+
/* @__PURE__ */ jsx(
|
|
316
|
+
Select,
|
|
317
|
+
{
|
|
318
|
+
size: "small",
|
|
319
|
+
value: blockTag,
|
|
320
|
+
onChange: (e) => {
|
|
321
|
+
setBlockTag(e.target.value);
|
|
322
|
+
props.onFormatBlock(e.target.value);
|
|
323
|
+
},
|
|
324
|
+
sx: { minWidth: 130, height: 32 },
|
|
325
|
+
children: BLOCK_FORMATS.map((b) => /* @__PURE__ */ jsx(MenuItem, { value: b.tag, children: b.label }, b.tag))
|
|
326
|
+
}
|
|
327
|
+
),
|
|
328
|
+
/* @__PURE__ */ jsx(
|
|
329
|
+
Select,
|
|
330
|
+
{
|
|
331
|
+
size: "small",
|
|
332
|
+
value: fontFamily,
|
|
333
|
+
onChange: (e) => {
|
|
334
|
+
setFontFamilyState(e.target.value);
|
|
335
|
+
props.onFontFamily(e.target.value);
|
|
336
|
+
},
|
|
337
|
+
sx: { minWidth: 130, height: 32 },
|
|
338
|
+
children: FONT_FAMILIES.map((f) => /* @__PURE__ */ jsx(MenuItem, { value: f, style: { fontFamily: f }, children: f }, f))
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center" }, children: [
|
|
342
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Diminuir fonte", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => changeFontSize(fontSize - 1), children: /* @__PURE__ */ jsx(RemoveIcon, { fontSize: "small" }) }) }),
|
|
343
|
+
/* @__PURE__ */ jsx(
|
|
344
|
+
Typography,
|
|
345
|
+
{
|
|
346
|
+
component: "span",
|
|
347
|
+
sx: {
|
|
348
|
+
minWidth: 24,
|
|
349
|
+
textAlign: "center",
|
|
350
|
+
fontVariantNumeric: "tabular-nums"
|
|
351
|
+
},
|
|
352
|
+
children: fontSize
|
|
353
|
+
}
|
|
354
|
+
),
|
|
355
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Aumentar fonte", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => changeFontSize(fontSize + 1), children: /* @__PURE__ */ jsx(AddIcon, { fontSize: "small" }) }) })
|
|
356
|
+
] }),
|
|
357
|
+
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true, sx: { mx: 0.5 } }),
|
|
358
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Negrito", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("bold"), children: /* @__PURE__ */ jsx(FormatBoldIcon, { fontSize: "small" }) }) }),
|
|
359
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "It\xE1lico", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("italic"), children: /* @__PURE__ */ jsx(FormatItalicIcon, { fontSize: "small" }) }) }),
|
|
360
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Sublinhado", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("underline"), children: /* @__PURE__ */ jsx(FormatUnderlinedIcon, { fontSize: "small" }) }) }),
|
|
361
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Tachado", children: /* @__PURE__ */ jsx(
|
|
362
|
+
IconButton,
|
|
363
|
+
{
|
|
364
|
+
size: "small",
|
|
365
|
+
onClick: () => props.onCommand("strikeThrough"),
|
|
366
|
+
children: /* @__PURE__ */ jsx(StrikethroughSIcon, { fontSize: "small" })
|
|
367
|
+
}
|
|
368
|
+
) }),
|
|
369
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Cor do texto", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => colorInputRef.current?.click(), children: /* @__PURE__ */ jsx(FormatColorTextIcon, { fontSize: "small" }) }) }),
|
|
370
|
+
/* @__PURE__ */ jsx(
|
|
371
|
+
"input",
|
|
372
|
+
{
|
|
373
|
+
ref: colorInputRef,
|
|
374
|
+
type: "color",
|
|
375
|
+
"aria-label": "Cor do texto",
|
|
376
|
+
style: { display: "none" },
|
|
377
|
+
onChange: (e) => props.onColor(e.target.value)
|
|
378
|
+
}
|
|
379
|
+
),
|
|
380
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Realce", children: /* @__PURE__ */ jsx(
|
|
381
|
+
IconButton,
|
|
382
|
+
{
|
|
383
|
+
size: "small",
|
|
384
|
+
onClick: () => highlightInputRef.current?.click(),
|
|
385
|
+
children: /* @__PURE__ */ jsx(BorderColorIcon, { fontSize: "small" })
|
|
386
|
+
}
|
|
387
|
+
) }),
|
|
388
|
+
/* @__PURE__ */ jsx(
|
|
389
|
+
"input",
|
|
390
|
+
{
|
|
391
|
+
ref: highlightInputRef,
|
|
392
|
+
type: "color",
|
|
393
|
+
"aria-label": "Cor de realce",
|
|
394
|
+
style: { display: "none" },
|
|
395
|
+
onChange: (e) => props.onHighlight(e.target.value)
|
|
396
|
+
}
|
|
397
|
+
),
|
|
398
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Inserir link", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: props.onLink, children: /* @__PURE__ */ jsx(LinkIcon, { fontSize: "small" }) }) }),
|
|
399
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Sobrescrito", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("superscript"), children: /* @__PURE__ */ jsx(SuperscriptIcon, { fontSize: "small" }) }) }),
|
|
400
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Subscrito", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("subscript"), children: /* @__PURE__ */ jsx(SubscriptIcon, { fontSize: "small" }) }) }),
|
|
401
|
+
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true, sx: { mx: 0.5 } }),
|
|
402
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Alinhar \xE0 esquerda", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("justifyLeft"), children: /* @__PURE__ */ jsx(FormatAlignLeftIcon, { fontSize: "small" }) }) }),
|
|
403
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Centralizar", children: /* @__PURE__ */ jsx(
|
|
404
|
+
IconButton,
|
|
405
|
+
{
|
|
406
|
+
size: "small",
|
|
407
|
+
onClick: () => props.onCommand("justifyCenter"),
|
|
408
|
+
children: /* @__PURE__ */ jsx(FormatAlignCenterIcon, { fontSize: "small" })
|
|
409
|
+
}
|
|
410
|
+
) }),
|
|
411
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Alinhar \xE0 direita", children: /* @__PURE__ */ jsx(
|
|
412
|
+
IconButton,
|
|
413
|
+
{
|
|
414
|
+
size: "small",
|
|
415
|
+
onClick: () => props.onCommand("justifyRight"),
|
|
416
|
+
children: /* @__PURE__ */ jsx(FormatAlignRightIcon, { fontSize: "small" })
|
|
417
|
+
}
|
|
418
|
+
) }),
|
|
419
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Justificar", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("justifyFull"), children: /* @__PURE__ */ jsx(FormatAlignJustifyIcon, { fontSize: "small" }) }) }),
|
|
420
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Lista com marcadores", children: /* @__PURE__ */ jsx(
|
|
421
|
+
IconButton,
|
|
422
|
+
{
|
|
423
|
+
size: "small",
|
|
424
|
+
onClick: () => props.onCommand("insertUnorderedList"),
|
|
425
|
+
children: /* @__PURE__ */ jsx(FormatListBulletedIcon, { fontSize: "small" })
|
|
426
|
+
}
|
|
427
|
+
) }),
|
|
428
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Lista numerada", children: /* @__PURE__ */ jsx(
|
|
429
|
+
IconButton,
|
|
430
|
+
{
|
|
431
|
+
size: "small",
|
|
432
|
+
onClick: () => props.onCommand("insertOrderedList"),
|
|
433
|
+
children: /* @__PURE__ */ jsx(FormatListNumberedIcon, { fontSize: "small" })
|
|
434
|
+
}
|
|
435
|
+
) }),
|
|
436
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Diminuir recuo", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("outdent"), children: /* @__PURE__ */ jsx(FormatIndentDecreaseIcon, { fontSize: "small" }) }) }),
|
|
437
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Aumentar recuo", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: () => props.onCommand("indent"), children: /* @__PURE__ */ jsx(FormatIndentIncreaseIcon, { fontSize: "small" }) }) }),
|
|
438
|
+
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true, sx: { mx: 0.5 } }),
|
|
439
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Inserir imagem", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: props.onInsertImage, children: /* @__PURE__ */ jsx(ImageIcon, { fontSize: "small" }) }) }),
|
|
440
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Limpar formata\xE7\xE3o", children: /* @__PURE__ */ jsx(
|
|
441
|
+
IconButton,
|
|
442
|
+
{
|
|
443
|
+
size: "small",
|
|
444
|
+
onClick: () => props.onCommand("removeFormat"),
|
|
445
|
+
children: /* @__PURE__ */ jsx(FormatClearIcon, { fontSize: "small" })
|
|
446
|
+
}
|
|
447
|
+
) }),
|
|
448
|
+
/* @__PURE__ */ jsx(
|
|
449
|
+
Button,
|
|
450
|
+
{
|
|
451
|
+
size: "small",
|
|
452
|
+
variant: "outlined",
|
|
453
|
+
startIcon: /* @__PURE__ */ jsx(DataObjectIcon, {}),
|
|
454
|
+
onClick: (e) => setVarAnchor(e.currentTarget),
|
|
455
|
+
sx: { ml: 0.5, textTransform: "none" },
|
|
456
|
+
children: "Vari\xE1vel"
|
|
457
|
+
}
|
|
458
|
+
),
|
|
459
|
+
/* @__PURE__ */ jsxs(
|
|
460
|
+
Menu,
|
|
461
|
+
{
|
|
462
|
+
anchorEl: varAnchor,
|
|
463
|
+
open: Boolean(varAnchor),
|
|
464
|
+
onClose: () => setVarAnchor(null),
|
|
465
|
+
children: [
|
|
466
|
+
props.variables.map((name) => /* @__PURE__ */ jsx(
|
|
467
|
+
MenuItem,
|
|
468
|
+
{
|
|
469
|
+
onClick: () => {
|
|
470
|
+
props.onInsertVariable(name);
|
|
471
|
+
setVarAnchor(null);
|
|
472
|
+
},
|
|
473
|
+
children: name
|
|
474
|
+
},
|
|
475
|
+
name
|
|
476
|
+
)),
|
|
477
|
+
props.variables.length > 0 && /* @__PURE__ */ jsx(Divider, {}),
|
|
478
|
+
/* @__PURE__ */ jsx(
|
|
479
|
+
MenuItem,
|
|
480
|
+
{
|
|
481
|
+
onClick: () => {
|
|
482
|
+
props.onInsertPageField("pageNumber");
|
|
483
|
+
setVarAnchor(null);
|
|
484
|
+
},
|
|
485
|
+
children: "N\xBA da p\xE1gina"
|
|
486
|
+
}
|
|
487
|
+
),
|
|
488
|
+
/* @__PURE__ */ jsx(
|
|
489
|
+
MenuItem,
|
|
490
|
+
{
|
|
491
|
+
onClick: () => {
|
|
492
|
+
props.onInsertPageField("totalPages");
|
|
493
|
+
setVarAnchor(null);
|
|
494
|
+
},
|
|
495
|
+
children: "Total de p\xE1ginas"
|
|
496
|
+
}
|
|
497
|
+
)
|
|
498
|
+
]
|
|
499
|
+
}
|
|
500
|
+
)
|
|
501
|
+
]
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
var Toolbar_default = Toolbar;
|
|
506
|
+
|
|
507
|
+
// src/ZoomControl.tsx
|
|
508
|
+
import { Select as Select2, MenuItem as MenuItem2 } from "@mui/material";
|
|
509
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
510
|
+
var ZOOM_LEVELS = [0.5, 0.75, 0.9, 1, 1.25, 1.5, 2];
|
|
511
|
+
function ZoomControl(props) {
|
|
512
|
+
return /* @__PURE__ */ jsx2(
|
|
513
|
+
Select2,
|
|
514
|
+
{
|
|
515
|
+
size: "small",
|
|
516
|
+
value: props.zoom,
|
|
517
|
+
onChange: (e) => props.onChange(Number(e.target.value)),
|
|
518
|
+
"aria-label": "Zoom",
|
|
519
|
+
sx: { height: 32, minWidth: 88 },
|
|
520
|
+
children: ZOOM_LEVELS.map((z) => /* @__PURE__ */ jsxs2(MenuItem2, { value: z, children: [
|
|
521
|
+
Math.round(z * 100),
|
|
522
|
+
"%"
|
|
523
|
+
] }, z))
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
var ZoomControl_default = ZoomControl;
|
|
528
|
+
|
|
529
|
+
// src/Ruler.tsx
|
|
530
|
+
import { useCallback, useRef as useRef2 } from "react";
|
|
531
|
+
import { Box as Box2 } from "@mui/material";
|
|
532
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
533
|
+
var RULER_HEIGHT = 24;
|
|
534
|
+
function Ruler(props) {
|
|
535
|
+
const { pageWidthPx, zoom, margins, indent } = props;
|
|
536
|
+
const barRef = useRef2(null);
|
|
537
|
+
const scaledWidth = pageWidthPx * zoom;
|
|
538
|
+
const screenPxToTwips = useCallback(
|
|
539
|
+
(px) => pxToTwips(px / zoom),
|
|
540
|
+
[zoom]
|
|
541
|
+
);
|
|
542
|
+
const leftMarginPx = twipsToPx(margins.left) * zoom;
|
|
543
|
+
const rightMarginFromLeftPx = (pageWidthPx - twipsToPx(margins.right)) * zoom;
|
|
544
|
+
const firstLinePx = (twipsToPx(margins.left) + twipsToPx(indent.firstLine)) * zoom;
|
|
545
|
+
const leftIndentPx = (twipsToPx(margins.left) + twipsToPx(indent.left)) * zoom;
|
|
546
|
+
const rightIndentPx = (pageWidthPx - twipsToPx(margins.right) - twipsToPx(indent.right)) * zoom;
|
|
547
|
+
const startDrag = (kind) => (e) => {
|
|
548
|
+
e.preventDefault();
|
|
549
|
+
const bar = barRef.current;
|
|
550
|
+
if (!bar) return;
|
|
551
|
+
const onMove = (ev) => {
|
|
552
|
+
const rect = bar.getBoundingClientRect();
|
|
553
|
+
const x = Math.min(Math.max(ev.clientX - rect.left, 0), scaledWidth);
|
|
554
|
+
const twipsFromLeft = screenPxToTwips(x);
|
|
555
|
+
const pageWidthTwips = pxToTwips(pageWidthPx);
|
|
556
|
+
switch (kind) {
|
|
557
|
+
case "leftMargin": {
|
|
558
|
+
const left = Math.min(twipsFromLeft, pageWidthTwips - margins.right - 720);
|
|
559
|
+
props.onMarginsChange({ ...margins, left: Math.max(0, left) });
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
case "rightMargin": {
|
|
563
|
+
const right = Math.min(
|
|
564
|
+
pageWidthTwips - twipsFromLeft,
|
|
565
|
+
pageWidthTwips - margins.left - 720
|
|
566
|
+
);
|
|
567
|
+
props.onMarginsChange({ ...margins, right: Math.max(0, right) });
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
case "firstLineIndent": {
|
|
571
|
+
props.onIndentChange({
|
|
572
|
+
...indent,
|
|
573
|
+
firstLine: Math.max(0, twipsFromLeft - margins.left)
|
|
574
|
+
});
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
case "leftIndent": {
|
|
578
|
+
props.onIndentChange({
|
|
579
|
+
...indent,
|
|
580
|
+
left: Math.max(0, twipsFromLeft - margins.left)
|
|
581
|
+
});
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
case "rightIndent": {
|
|
585
|
+
props.onIndentChange({
|
|
586
|
+
...indent,
|
|
587
|
+
right: Math.max(0, pageWidthTwips - margins.right - twipsFromLeft)
|
|
588
|
+
});
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
const onUp = () => {
|
|
594
|
+
window.removeEventListener("mousemove", onMove);
|
|
595
|
+
window.removeEventListener("mouseup", onUp);
|
|
596
|
+
};
|
|
597
|
+
window.addEventListener("mousemove", onMove);
|
|
598
|
+
window.addEventListener("mouseup", onUp);
|
|
599
|
+
};
|
|
600
|
+
const ticks = [];
|
|
601
|
+
const inches = Math.floor(pageWidthPx / PX_PER_INCH);
|
|
602
|
+
for (let i = 0; i <= inches; i++) {
|
|
603
|
+
ticks.push(
|
|
604
|
+
/* @__PURE__ */ jsx3(
|
|
605
|
+
Box2,
|
|
606
|
+
{
|
|
607
|
+
sx: {
|
|
608
|
+
position: "absolute",
|
|
609
|
+
left: i * PX_PER_INCH * zoom,
|
|
610
|
+
top: 0,
|
|
611
|
+
bottom: 0,
|
|
612
|
+
borderLeft: "1px solid",
|
|
613
|
+
borderColor: "text.disabled",
|
|
614
|
+
fontSize: 9,
|
|
615
|
+
color: "text.secondary",
|
|
616
|
+
pl: "2px"
|
|
617
|
+
},
|
|
618
|
+
children: i
|
|
619
|
+
},
|
|
620
|
+
i
|
|
621
|
+
)
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
const marginHandleSx = {
|
|
625
|
+
position: "absolute",
|
|
626
|
+
top: 0,
|
|
627
|
+
width: 8,
|
|
628
|
+
height: RULER_HEIGHT,
|
|
629
|
+
cursor: "ew-resize",
|
|
630
|
+
bgcolor: "primary.main",
|
|
631
|
+
opacity: 0.6,
|
|
632
|
+
transform: "translateX(-50%)",
|
|
633
|
+
"&:hover": { opacity: 1 }
|
|
634
|
+
};
|
|
635
|
+
const indentHandleSx = {
|
|
636
|
+
position: "absolute",
|
|
637
|
+
width: 0,
|
|
638
|
+
height: 0,
|
|
639
|
+
cursor: "ew-resize",
|
|
640
|
+
borderLeft: "6px solid transparent",
|
|
641
|
+
borderRight: "6px solid transparent",
|
|
642
|
+
transform: "translateX(-50%)"
|
|
643
|
+
};
|
|
644
|
+
return /* @__PURE__ */ jsxs3(
|
|
645
|
+
Box2,
|
|
646
|
+
{
|
|
647
|
+
ref: barRef,
|
|
648
|
+
sx: {
|
|
649
|
+
position: "relative",
|
|
650
|
+
height: RULER_HEIGHT,
|
|
651
|
+
width: scaledWidth,
|
|
652
|
+
bgcolor: "grey.100",
|
|
653
|
+
borderBottom: 1,
|
|
654
|
+
borderColor: "divider",
|
|
655
|
+
userSelect: "none",
|
|
656
|
+
mx: "auto"
|
|
657
|
+
},
|
|
658
|
+
role: "presentation",
|
|
659
|
+
"aria-label": "R\xE9gua",
|
|
660
|
+
children: [
|
|
661
|
+
ticks,
|
|
662
|
+
/* @__PURE__ */ jsx3(
|
|
663
|
+
Box2,
|
|
664
|
+
{
|
|
665
|
+
sx: {
|
|
666
|
+
position: "absolute",
|
|
667
|
+
left: 0,
|
|
668
|
+
top: 0,
|
|
669
|
+
bottom: 0,
|
|
670
|
+
width: leftMarginPx,
|
|
671
|
+
bgcolor: "grey.300",
|
|
672
|
+
opacity: 0.5
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
),
|
|
676
|
+
/* @__PURE__ */ jsx3(
|
|
677
|
+
Box2,
|
|
678
|
+
{
|
|
679
|
+
sx: {
|
|
680
|
+
position: "absolute",
|
|
681
|
+
left: rightMarginFromLeftPx,
|
|
682
|
+
right: 0,
|
|
683
|
+
top: 0,
|
|
684
|
+
bottom: 0,
|
|
685
|
+
bgcolor: "grey.300",
|
|
686
|
+
opacity: 0.5
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
),
|
|
690
|
+
/* @__PURE__ */ jsx3(
|
|
691
|
+
Box2,
|
|
692
|
+
{
|
|
693
|
+
sx: { ...marginHandleSx, left: leftMarginPx },
|
|
694
|
+
onMouseDown: startDrag("leftMargin"),
|
|
695
|
+
"aria-label": "Margem esquerda"
|
|
696
|
+
}
|
|
697
|
+
),
|
|
698
|
+
/* @__PURE__ */ jsx3(
|
|
699
|
+
Box2,
|
|
700
|
+
{
|
|
701
|
+
sx: { ...marginHandleSx, left: rightMarginFromLeftPx },
|
|
702
|
+
onMouseDown: startDrag("rightMargin"),
|
|
703
|
+
"aria-label": "Margem direita"
|
|
704
|
+
}
|
|
705
|
+
),
|
|
706
|
+
/* @__PURE__ */ jsx3(
|
|
707
|
+
Box2,
|
|
708
|
+
{
|
|
709
|
+
sx: {
|
|
710
|
+
...indentHandleSx,
|
|
711
|
+
left: firstLinePx,
|
|
712
|
+
top: 0,
|
|
713
|
+
borderTop: "7px solid",
|
|
714
|
+
borderTopColor: "primary.dark"
|
|
715
|
+
},
|
|
716
|
+
onMouseDown: startDrag("firstLineIndent"),
|
|
717
|
+
"aria-label": "Recuo da primeira linha"
|
|
718
|
+
}
|
|
719
|
+
),
|
|
720
|
+
/* @__PURE__ */ jsx3(
|
|
721
|
+
Box2,
|
|
722
|
+
{
|
|
723
|
+
sx: {
|
|
724
|
+
...indentHandleSx,
|
|
725
|
+
left: leftIndentPx,
|
|
726
|
+
bottom: 0,
|
|
727
|
+
borderBottom: "7px solid",
|
|
728
|
+
borderBottomColor: "primary.dark"
|
|
729
|
+
},
|
|
730
|
+
onMouseDown: startDrag("leftIndent"),
|
|
731
|
+
"aria-label": "Recuo \xE0 esquerda"
|
|
732
|
+
}
|
|
733
|
+
),
|
|
734
|
+
/* @__PURE__ */ jsx3(
|
|
735
|
+
Box2,
|
|
736
|
+
{
|
|
737
|
+
sx: {
|
|
738
|
+
...indentHandleSx,
|
|
739
|
+
left: rightIndentPx,
|
|
740
|
+
bottom: 0,
|
|
741
|
+
borderBottom: "7px solid",
|
|
742
|
+
borderBottomColor: "primary.dark"
|
|
743
|
+
},
|
|
744
|
+
onMouseDown: startDrag("rightIndent"),
|
|
745
|
+
"aria-label": "Recuo \xE0 direita"
|
|
746
|
+
}
|
|
747
|
+
)
|
|
748
|
+
]
|
|
749
|
+
}
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
var Ruler_default = Ruler;
|
|
753
|
+
|
|
754
|
+
// src/DocxEditor.tsx
|
|
755
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
756
|
+
var PAGE_WIDTH_PX = 794;
|
|
757
|
+
var PAGE_HEIGHT_PX = 1123;
|
|
758
|
+
var PAGE_GAP_PX = 24;
|
|
759
|
+
var HEADER_FOOTER_DISTANCE_PX = 48;
|
|
760
|
+
var EMPTY_INDENT = { firstLine: 0, left: 0, right: 0 };
|
|
761
|
+
function EditableRegion(props) {
|
|
762
|
+
const setRef = (el) => {
|
|
763
|
+
if (el && el.dataset.initialized !== "1") {
|
|
764
|
+
el.innerHTML = props.html;
|
|
765
|
+
el.dataset.initialized = "1";
|
|
766
|
+
}
|
|
767
|
+
props.onRegister(el);
|
|
768
|
+
};
|
|
769
|
+
return /* @__PURE__ */ jsx4(
|
|
770
|
+
Box3,
|
|
771
|
+
{
|
|
772
|
+
component: "div",
|
|
773
|
+
ref: setRef,
|
|
774
|
+
contentEditable: props.editable,
|
|
775
|
+
suppressContentEditableWarning: true,
|
|
776
|
+
onFocus: (e) => props.onFocus(e.currentTarget),
|
|
777
|
+
onBlur: props.onBlur,
|
|
778
|
+
onInput: props.onInput,
|
|
779
|
+
"aria-label": props.ariaLabel,
|
|
780
|
+
"data-placeholder": props.placeholder,
|
|
781
|
+
sx: [
|
|
782
|
+
{
|
|
783
|
+
outline: "none",
|
|
784
|
+
position: "relative",
|
|
785
|
+
minHeight: 24,
|
|
786
|
+
"& p": { m: 0, mb: 1 },
|
|
787
|
+
"&:empty::before": {
|
|
788
|
+
content: "attr(data-placeholder)",
|
|
789
|
+
color: "text.disabled"
|
|
790
|
+
}
|
|
791
|
+
},
|
|
792
|
+
...Array.isArray(props.sx) ? props.sx : props.sx ? [props.sx] : []
|
|
793
|
+
]
|
|
794
|
+
}
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
function currentBlock(region) {
|
|
798
|
+
if (!region) return null;
|
|
799
|
+
const selection = window.getSelection();
|
|
800
|
+
let node = selection && selection.anchorNode ? selection.anchorNode : null;
|
|
801
|
+
if (!node || !region.contains(node)) {
|
|
802
|
+
return region.firstElementChild ?? region;
|
|
803
|
+
}
|
|
804
|
+
let el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
805
|
+
while (el && el !== region) {
|
|
806
|
+
if (/^(P|H1|H2|H3|H4|LI|DIV)$/.test(el.tagName) && el.parentElement === region) {
|
|
807
|
+
return el;
|
|
808
|
+
}
|
|
809
|
+
if (el.parentElement === region) return el;
|
|
810
|
+
el = el.parentElement;
|
|
811
|
+
}
|
|
812
|
+
return region.firstElementChild ?? region;
|
|
813
|
+
}
|
|
814
|
+
var mirrorSx = {
|
|
815
|
+
pointerEvents: "none",
|
|
816
|
+
"& p": { m: 0, mb: 1 }
|
|
817
|
+
};
|
|
818
|
+
function withPagePreview(html, pageNumber, totalPages) {
|
|
819
|
+
return html.split(PAGE_NUMBER_TOKEN).join(String(pageNumber)).split(TOTAL_PAGES_TOKEN).join(String(totalPages));
|
|
820
|
+
}
|
|
821
|
+
var RESIZE_CORNERS = ["nw", "ne", "sw", "se"];
|
|
822
|
+
var DocxEditor = forwardRef(
|
|
823
|
+
function DocxEditor2(props, ref) {
|
|
824
|
+
const {
|
|
825
|
+
documentBuffer,
|
|
826
|
+
document: documentModel,
|
|
827
|
+
mode,
|
|
828
|
+
showToolbar = false,
|
|
829
|
+
showRuler = false,
|
|
830
|
+
showZoomControl = false
|
|
831
|
+
} = props;
|
|
832
|
+
const editable = mode === "editing";
|
|
833
|
+
const [model, setModel] = useState2(
|
|
834
|
+
() => documentModel ?? { body: "" }
|
|
835
|
+
);
|
|
836
|
+
const [docVersion, setDocVersion] = useState2(0);
|
|
837
|
+
const [loading, setLoading] = useState2(false);
|
|
838
|
+
const [zoom, setZoom] = useState2(1);
|
|
839
|
+
const [margins, setMargins] = useState2(DEFAULT_MARGINS);
|
|
840
|
+
const [indent, setIndent] = useState2(EMPTY_INDENT);
|
|
841
|
+
const [pageCount, setPageCount] = useState2(1);
|
|
842
|
+
const [mirror, setMirror] = useState2(() => ({
|
|
843
|
+
header: documentModel?.header ?? "",
|
|
844
|
+
footer: documentModel?.footer ?? ""
|
|
845
|
+
}));
|
|
846
|
+
const [chromeHeights, setChromeHeights] = useState2({ header: 0, footer: 0 });
|
|
847
|
+
const [focusedRegion, setFocusedRegion] = useState2(null);
|
|
848
|
+
const [selectedImg, setSelectedImg] = useState2(null);
|
|
849
|
+
const [frame, setFrame] = useState2(null);
|
|
850
|
+
const regionEls = useRef3({});
|
|
851
|
+
const activeRegion = useRef3(null);
|
|
852
|
+
const imageInputRef = useRef3(null);
|
|
853
|
+
const rafId = useRef3(null);
|
|
854
|
+
const pagesColumnRef = useRef3(null);
|
|
855
|
+
const padTop = twipsToPx(margins.top);
|
|
856
|
+
const padRight = twipsToPx(margins.right);
|
|
857
|
+
const padBottom = twipsToPx(margins.bottom);
|
|
858
|
+
const padLeft = twipsToPx(margins.left);
|
|
859
|
+
const bodyTopPx = Math.max(
|
|
860
|
+
padTop,
|
|
861
|
+
HEADER_FOOTER_DISTANCE_PX + chromeHeights.header
|
|
862
|
+
);
|
|
863
|
+
const bodyBottomPx = Math.max(
|
|
864
|
+
padBottom,
|
|
865
|
+
HEADER_FOOTER_DISTANCE_PX + chromeHeights.footer
|
|
866
|
+
);
|
|
867
|
+
const recomputePagination = useCallback2(() => {
|
|
868
|
+
const body = regionEls.current.body;
|
|
869
|
+
if (!body) return;
|
|
870
|
+
const headerH = regionEls.current.header?.offsetHeight ?? 0;
|
|
871
|
+
const footerH = regionEls.current.footer?.offsetHeight ?? 0;
|
|
872
|
+
setChromeHeights(
|
|
873
|
+
(prev) => prev.header === headerH && prev.footer === footerH ? prev : { header: headerH, footer: footerH }
|
|
874
|
+
);
|
|
875
|
+
const top = Math.max(
|
|
876
|
+
twipsToPx(margins.top),
|
|
877
|
+
HEADER_FOOTER_DISTANCE_PX + headerH
|
|
878
|
+
);
|
|
879
|
+
const bottom = Math.max(
|
|
880
|
+
twipsToPx(margins.bottom),
|
|
881
|
+
HEADER_FOOTER_DISTANCE_PX + footerH
|
|
882
|
+
);
|
|
883
|
+
const usableHeightPx = Math.max(PAGE_HEIGHT_PX - top - bottom, 1);
|
|
884
|
+
const interPageGapPx = bottom + PAGE_GAP_PX + top;
|
|
885
|
+
let spacerAccum = 0;
|
|
886
|
+
const blocks = [];
|
|
887
|
+
const blockEls = [];
|
|
888
|
+
Array.from(body.children).forEach((child) => {
|
|
889
|
+
const el = child;
|
|
890
|
+
if (el.hasAttribute(PAGE_SPACER_ATTR)) {
|
|
891
|
+
spacerAccum += el.offsetHeight;
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
blocks.push({ top: el.offsetTop - spacerAccum, height: el.offsetHeight });
|
|
895
|
+
blockEls.push(el);
|
|
896
|
+
});
|
|
897
|
+
const layout = computePageLayout(blocks, { usableHeightPx, interPageGapPx });
|
|
898
|
+
body.querySelectorAll(`[${PAGE_SPACER_ATTR}]`).forEach((node) => node.remove());
|
|
899
|
+
for (const spacer of layout.spacers) {
|
|
900
|
+
const target = blockEls[spacer.beforeBlockIndex];
|
|
901
|
+
if (target) body.insertBefore(createSpacerElement(spacer.heightPx), target);
|
|
902
|
+
}
|
|
903
|
+
setPageCount(layout.pageCount);
|
|
904
|
+
}, [margins]);
|
|
905
|
+
const schedulePagination = useCallback2(() => {
|
|
906
|
+
if (rafId.current !== null) cancelAnimationFrame(rafId.current);
|
|
907
|
+
rafId.current = requestAnimationFrame(() => {
|
|
908
|
+
rafId.current = null;
|
|
909
|
+
recomputePagination();
|
|
910
|
+
});
|
|
911
|
+
}, [recomputePagination]);
|
|
912
|
+
const handleContentChanged = useCallback2(() => {
|
|
913
|
+
const header = regionEls.current.header?.innerHTML ?? "";
|
|
914
|
+
const footer = regionEls.current.footer?.innerHTML ?? "";
|
|
915
|
+
setMirror(
|
|
916
|
+
(prev) => prev.header === header && prev.footer === footer ? prev : { header, footer }
|
|
917
|
+
);
|
|
918
|
+
schedulePagination();
|
|
919
|
+
}, [schedulePagination]);
|
|
920
|
+
useEffect(() => {
|
|
921
|
+
if (!documentBuffer) return;
|
|
922
|
+
let cancelled = false;
|
|
923
|
+
setLoading(true);
|
|
924
|
+
bufferToModel(documentBuffer).then((imported) => {
|
|
925
|
+
if (cancelled) return;
|
|
926
|
+
setModel((prev) => ({ ...imported, variables: prev.variables }));
|
|
927
|
+
setMirror({
|
|
928
|
+
header: imported.header ?? "",
|
|
929
|
+
footer: imported.footer ?? ""
|
|
930
|
+
});
|
|
931
|
+
setDocVersion((v) => v + 1);
|
|
932
|
+
}).finally(() => {
|
|
933
|
+
if (!cancelled) setLoading(false);
|
|
934
|
+
});
|
|
935
|
+
return () => {
|
|
936
|
+
cancelled = true;
|
|
937
|
+
};
|
|
938
|
+
}, [documentBuffer]);
|
|
939
|
+
useEffect(() => {
|
|
940
|
+
recomputePagination();
|
|
941
|
+
}, [recomputePagination, docVersion, mirror]);
|
|
942
|
+
useEffect(() => {
|
|
943
|
+
const body = regionEls.current.body;
|
|
944
|
+
if (!body) return;
|
|
945
|
+
const onLoad = () => schedulePagination();
|
|
946
|
+
body.addEventListener("load", onLoad, true);
|
|
947
|
+
return () => body.removeEventListener("load", onLoad, true);
|
|
948
|
+
}, [schedulePagination, docVersion]);
|
|
949
|
+
useEffect(
|
|
950
|
+
() => () => {
|
|
951
|
+
if (rafId.current !== null) cancelAnimationFrame(rafId.current);
|
|
952
|
+
},
|
|
953
|
+
[]
|
|
954
|
+
);
|
|
955
|
+
const buildModel = useCallback2(() => {
|
|
956
|
+
const els = regionEls.current;
|
|
957
|
+
return {
|
|
958
|
+
header: els.header ? els.header.innerHTML : model.header,
|
|
959
|
+
body: els.body ? stripPageSpacers(els.body.innerHTML) : model.body,
|
|
960
|
+
footer: els.footer ? els.footer.innerHTML : model.footer,
|
|
961
|
+
variables: model.variables
|
|
962
|
+
};
|
|
963
|
+
}, [model]);
|
|
964
|
+
useImperativeHandle(
|
|
965
|
+
ref,
|
|
966
|
+
() => ({
|
|
967
|
+
save: () => modelToDocx(buildModel(), { margins }),
|
|
968
|
+
getModel: () => buildModel()
|
|
969
|
+
}),
|
|
970
|
+
[buildModel, margins]
|
|
971
|
+
);
|
|
972
|
+
const focusActive = () => {
|
|
973
|
+
const el = activeRegion.current ?? regionEls.current.body ?? null;
|
|
974
|
+
el?.focus();
|
|
975
|
+
return el;
|
|
976
|
+
};
|
|
977
|
+
const handleCommand = (command, value) => {
|
|
978
|
+
focusActive();
|
|
979
|
+
execFormat(command, value);
|
|
980
|
+
handleContentChanged();
|
|
981
|
+
};
|
|
982
|
+
const handleLink = () => {
|
|
983
|
+
const url = window.prompt("URL do link:");
|
|
984
|
+
if (!url) return;
|
|
985
|
+
focusActive();
|
|
986
|
+
execFormat("createLink", url);
|
|
987
|
+
handleContentChanged();
|
|
988
|
+
};
|
|
989
|
+
const handleInsertImage = () => imageInputRef.current?.click();
|
|
990
|
+
const onImagePicked = (e) => {
|
|
991
|
+
const file = e.target.files?.[0];
|
|
992
|
+
if (!file) return;
|
|
993
|
+
const reader = new FileReader();
|
|
994
|
+
reader.onload = () => {
|
|
995
|
+
focusActive();
|
|
996
|
+
execFormat("insertImage", String(reader.result));
|
|
997
|
+
handleContentChanged();
|
|
998
|
+
};
|
|
999
|
+
reader.readAsDataURL(file);
|
|
1000
|
+
e.target.value = "";
|
|
1001
|
+
};
|
|
1002
|
+
const handleInsertVariable = (name) => {
|
|
1003
|
+
const el = activeRegion.current ?? regionEls.current.body ?? null;
|
|
1004
|
+
if (el) {
|
|
1005
|
+
insertTextIntoRegion(el, `{{${name}}}`);
|
|
1006
|
+
handleContentChanged();
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
const handleInsertPageField = (field) => {
|
|
1010
|
+
const el = activeRegion.current ?? regionEls.current.footer ?? null;
|
|
1011
|
+
if (el) {
|
|
1012
|
+
insertTextIntoRegion(
|
|
1013
|
+
el,
|
|
1014
|
+
field === "pageNumber" ? PAGE_NUMBER_TOKEN : TOTAL_PAGES_TOKEN
|
|
1015
|
+
);
|
|
1016
|
+
handleContentChanged();
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
const applyIndent = (next) => {
|
|
1020
|
+
setIndent(next);
|
|
1021
|
+
const block = currentBlock(activeRegion.current ?? regionEls.current.body ?? null);
|
|
1022
|
+
if (block) {
|
|
1023
|
+
block.style.textIndent = `${twipsToPx(next.firstLine)}px`;
|
|
1024
|
+
block.style.marginLeft = `${twipsToPx(next.left)}px`;
|
|
1025
|
+
block.style.marginRight = `${twipsToPx(next.right)}px`;
|
|
1026
|
+
}
|
|
1027
|
+
schedulePagination();
|
|
1028
|
+
};
|
|
1029
|
+
const registerRegion = (key) => (el) => {
|
|
1030
|
+
regionEls.current[key] = el;
|
|
1031
|
+
};
|
|
1032
|
+
const onRegionFocus = (el) => {
|
|
1033
|
+
activeRegion.current = el;
|
|
1034
|
+
const els = regionEls.current;
|
|
1035
|
+
setFocusedRegion(
|
|
1036
|
+
el === els.header ? "header" : el === els.footer ? "footer" : "body"
|
|
1037
|
+
);
|
|
1038
|
+
};
|
|
1039
|
+
const onRegionBlur = () => setFocusedRegion(null);
|
|
1040
|
+
const editingChrome = focusedRegion === "header" || focusedRegion === "footer";
|
|
1041
|
+
const updateFrame = useCallback2(() => {
|
|
1042
|
+
const img = selectedImg;
|
|
1043
|
+
const column = pagesColumnRef.current;
|
|
1044
|
+
if (!img || !img.isConnected || !column) {
|
|
1045
|
+
setFrame(null);
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
const imgRect = img.getBoundingClientRect();
|
|
1049
|
+
const colRect = column.getBoundingClientRect();
|
|
1050
|
+
setFrame({
|
|
1051
|
+
top: (imgRect.top - colRect.top) / zoom,
|
|
1052
|
+
left: (imgRect.left - colRect.left) / zoom,
|
|
1053
|
+
width: imgRect.width / zoom,
|
|
1054
|
+
height: imgRect.height / zoom
|
|
1055
|
+
});
|
|
1056
|
+
}, [selectedImg, zoom]);
|
|
1057
|
+
const updateFrameRef = useRef3(updateFrame);
|
|
1058
|
+
useEffect(() => {
|
|
1059
|
+
updateFrameRef.current = updateFrame;
|
|
1060
|
+
});
|
|
1061
|
+
useEffect(() => {
|
|
1062
|
+
updateFrame();
|
|
1063
|
+
}, [updateFrame, pageCount, margins, mirror]);
|
|
1064
|
+
useEffect(() => {
|
|
1065
|
+
setSelectedImg(null);
|
|
1066
|
+
}, [docVersion]);
|
|
1067
|
+
const handleColumnClick = (e) => {
|
|
1068
|
+
if (!editable) return;
|
|
1069
|
+
const target = e.target;
|
|
1070
|
+
if (target instanceof HTMLImageElement && target.closest("[contenteditable]")) {
|
|
1071
|
+
setSelectedImg(target);
|
|
1072
|
+
} else {
|
|
1073
|
+
setSelectedImg(null);
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
const startResize = (corner) => (e) => {
|
|
1077
|
+
const img = selectedImg;
|
|
1078
|
+
if (!img) return;
|
|
1079
|
+
e.preventDefault();
|
|
1080
|
+
e.stopPropagation();
|
|
1081
|
+
const startX = e.clientX;
|
|
1082
|
+
const startWidth = img.offsetWidth;
|
|
1083
|
+
const region = img.closest("[contenteditable]");
|
|
1084
|
+
const maxWidth = region ? region.clientWidth : PAGE_WIDTH_PX;
|
|
1085
|
+
const sign = corner === "ne" || corner === "se" ? 1 : -1;
|
|
1086
|
+
const onMove = (ev) => {
|
|
1087
|
+
const dx = (ev.clientX - startX) / zoom * sign;
|
|
1088
|
+
const next = Math.round(Math.min(maxWidth, Math.max(20, startWidth + dx)));
|
|
1089
|
+
img.style.width = `${next}px`;
|
|
1090
|
+
img.style.height = "auto";
|
|
1091
|
+
updateFrameRef.current();
|
|
1092
|
+
};
|
|
1093
|
+
const onUp = () => {
|
|
1094
|
+
document.removeEventListener("mousemove", onMove);
|
|
1095
|
+
document.removeEventListener("mouseup", onUp);
|
|
1096
|
+
img.style.height = `${img.offsetHeight}px`;
|
|
1097
|
+
handleContentChanged();
|
|
1098
|
+
updateFrameRef.current();
|
|
1099
|
+
};
|
|
1100
|
+
document.addEventListener("mousemove", onMove);
|
|
1101
|
+
document.addEventListener("mouseup", onUp);
|
|
1102
|
+
};
|
|
1103
|
+
const columnHeight = pageCount * PAGE_HEIGHT_PX + (pageCount - 1) * PAGE_GAP_PX;
|
|
1104
|
+
const usableFirstPagePx = Math.max(
|
|
1105
|
+
PAGE_HEIGHT_PX - bodyTopPx - bodyBottomPx,
|
|
1106
|
+
24
|
|
1107
|
+
);
|
|
1108
|
+
return /* @__PURE__ */ jsxs4(
|
|
1109
|
+
Box3,
|
|
1110
|
+
{
|
|
1111
|
+
sx: {
|
|
1112
|
+
display: "flex",
|
|
1113
|
+
flexDirection: "column",
|
|
1114
|
+
height: "100%",
|
|
1115
|
+
bgcolor: "grey.200"
|
|
1116
|
+
},
|
|
1117
|
+
children: [
|
|
1118
|
+
showToolbar && /* @__PURE__ */ jsxs4(
|
|
1119
|
+
Box3,
|
|
1120
|
+
{
|
|
1121
|
+
sx: {
|
|
1122
|
+
display: "flex",
|
|
1123
|
+
alignItems: "center",
|
|
1124
|
+
gap: 1,
|
|
1125
|
+
flexWrap: "wrap",
|
|
1126
|
+
position: "sticky",
|
|
1127
|
+
top: 0,
|
|
1128
|
+
zIndex: 4
|
|
1129
|
+
},
|
|
1130
|
+
children: [
|
|
1131
|
+
/* @__PURE__ */ jsx4(Box3, { sx: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsx4(
|
|
1132
|
+
Toolbar_default,
|
|
1133
|
+
{
|
|
1134
|
+
variables: model.variables ?? [],
|
|
1135
|
+
onCommand: handleCommand,
|
|
1136
|
+
onFontFamily: (f) => {
|
|
1137
|
+
focusActive();
|
|
1138
|
+
setFontFamily(f);
|
|
1139
|
+
handleContentChanged();
|
|
1140
|
+
},
|
|
1141
|
+
onFontSize: (pt) => {
|
|
1142
|
+
const el = focusActive();
|
|
1143
|
+
if (el) setFontSize(el, pt);
|
|
1144
|
+
handleContentChanged();
|
|
1145
|
+
},
|
|
1146
|
+
onFormatBlock: (tag) => handleCommand("formatBlock", tag),
|
|
1147
|
+
onColor: (c) => handleCommand("foreColor", c),
|
|
1148
|
+
onHighlight: (c) => handleCommand("hiliteColor", c),
|
|
1149
|
+
onLink: handleLink,
|
|
1150
|
+
onInsertImage: handleInsertImage,
|
|
1151
|
+
onInsertVariable: handleInsertVariable,
|
|
1152
|
+
onInsertPageField: handleInsertPageField
|
|
1153
|
+
}
|
|
1154
|
+
) }),
|
|
1155
|
+
showZoomControl && /* @__PURE__ */ jsx4(Box3, { sx: { px: 1 }, children: /* @__PURE__ */ jsx4(ZoomControl_default, { zoom, onChange: setZoom }) })
|
|
1156
|
+
]
|
|
1157
|
+
}
|
|
1158
|
+
),
|
|
1159
|
+
/* @__PURE__ */ jsx4(
|
|
1160
|
+
"input",
|
|
1161
|
+
{
|
|
1162
|
+
ref: imageInputRef,
|
|
1163
|
+
type: "file",
|
|
1164
|
+
accept: "image/*",
|
|
1165
|
+
style: { display: "none" },
|
|
1166
|
+
onChange: onImagePicked
|
|
1167
|
+
}
|
|
1168
|
+
),
|
|
1169
|
+
/* @__PURE__ */ jsxs4(Box3, { sx: { flex: 1, overflow: "auto", py: 3 }, children: [
|
|
1170
|
+
showRuler && /* @__PURE__ */ jsx4(Box3, { sx: { display: "flex", justifyContent: "center", mb: 1 }, children: /* @__PURE__ */ jsx4(
|
|
1171
|
+
Ruler_default,
|
|
1172
|
+
{
|
|
1173
|
+
pageWidthPx: PAGE_WIDTH_PX,
|
|
1174
|
+
zoom,
|
|
1175
|
+
margins,
|
|
1176
|
+
indent,
|
|
1177
|
+
onMarginsChange: setMargins,
|
|
1178
|
+
onIndentChange: applyIndent
|
|
1179
|
+
}
|
|
1180
|
+
) }),
|
|
1181
|
+
/* @__PURE__ */ jsxs4(
|
|
1182
|
+
Box3,
|
|
1183
|
+
{
|
|
1184
|
+
ref: pagesColumnRef,
|
|
1185
|
+
onClick: handleColumnClick,
|
|
1186
|
+
sx: {
|
|
1187
|
+
width: PAGE_WIDTH_PX,
|
|
1188
|
+
height: columnHeight,
|
|
1189
|
+
transform: `scale(${zoom})`,
|
|
1190
|
+
transformOrigin: "top center",
|
|
1191
|
+
mx: "auto",
|
|
1192
|
+
position: "relative"
|
|
1193
|
+
},
|
|
1194
|
+
children: [
|
|
1195
|
+
Array.from({ length: pageCount }, (_, page) => /* @__PURE__ */ jsx4(
|
|
1196
|
+
Box3,
|
|
1197
|
+
{
|
|
1198
|
+
"data-page-index": page,
|
|
1199
|
+
sx: {
|
|
1200
|
+
position: "absolute",
|
|
1201
|
+
top: page * (PAGE_HEIGHT_PX + PAGE_GAP_PX),
|
|
1202
|
+
left: 0,
|
|
1203
|
+
width: "100%",
|
|
1204
|
+
height: PAGE_HEIGHT_PX,
|
|
1205
|
+
bgcolor: "background.paper",
|
|
1206
|
+
boxShadow: 3,
|
|
1207
|
+
zIndex: 0
|
|
1208
|
+
},
|
|
1209
|
+
children: page > 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1210
|
+
/* @__PURE__ */ jsx4(
|
|
1211
|
+
Box3,
|
|
1212
|
+
{
|
|
1213
|
+
"aria-hidden": true,
|
|
1214
|
+
sx: {
|
|
1215
|
+
...mirrorSx,
|
|
1216
|
+
position: "absolute",
|
|
1217
|
+
top: HEADER_FOOTER_DISTANCE_PX,
|
|
1218
|
+
left: padLeft,
|
|
1219
|
+
right: padRight,
|
|
1220
|
+
zIndex: 2,
|
|
1221
|
+
opacity: focusedRegion === "body" ? 0.55 : 1,
|
|
1222
|
+
transition: "opacity 120ms"
|
|
1223
|
+
},
|
|
1224
|
+
dangerouslySetInnerHTML: {
|
|
1225
|
+
__html: withPagePreview(mirror.header, page + 1, pageCount)
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
),
|
|
1229
|
+
/* @__PURE__ */ jsx4(
|
|
1230
|
+
Box3,
|
|
1231
|
+
{
|
|
1232
|
+
"aria-hidden": true,
|
|
1233
|
+
sx: {
|
|
1234
|
+
...mirrorSx,
|
|
1235
|
+
position: "absolute",
|
|
1236
|
+
bottom: HEADER_FOOTER_DISTANCE_PX,
|
|
1237
|
+
left: padLeft,
|
|
1238
|
+
right: padRight,
|
|
1239
|
+
zIndex: 2,
|
|
1240
|
+
opacity: focusedRegion === "body" ? 0.55 : 1,
|
|
1241
|
+
transition: "opacity 120ms"
|
|
1242
|
+
},
|
|
1243
|
+
dangerouslySetInnerHTML: {
|
|
1244
|
+
__html: withPagePreview(mirror.footer, page + 1, pageCount)
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
)
|
|
1248
|
+
] })
|
|
1249
|
+
},
|
|
1250
|
+
page
|
|
1251
|
+
)),
|
|
1252
|
+
/* @__PURE__ */ jsxs4(
|
|
1253
|
+
Box3,
|
|
1254
|
+
{
|
|
1255
|
+
sx: {
|
|
1256
|
+
position: "absolute",
|
|
1257
|
+
top: 0,
|
|
1258
|
+
left: 0,
|
|
1259
|
+
width: "100%",
|
|
1260
|
+
height: PAGE_HEIGHT_PX,
|
|
1261
|
+
zIndex: 2,
|
|
1262
|
+
pointerEvents: "none"
|
|
1263
|
+
},
|
|
1264
|
+
children: [
|
|
1265
|
+
/* @__PURE__ */ jsxs4(
|
|
1266
|
+
Box3,
|
|
1267
|
+
{
|
|
1268
|
+
sx: {
|
|
1269
|
+
position: "absolute",
|
|
1270
|
+
top: HEADER_FOOTER_DISTANCE_PX,
|
|
1271
|
+
left: padLeft,
|
|
1272
|
+
right: padRight,
|
|
1273
|
+
pointerEvents: "auto",
|
|
1274
|
+
opacity: focusedRegion === "body" ? 0.55 : 1,
|
|
1275
|
+
transition: "opacity 120ms",
|
|
1276
|
+
...focusedRegion === "header" && {
|
|
1277
|
+
outline: "1px dashed",
|
|
1278
|
+
outlineColor: "primary.main",
|
|
1279
|
+
outlineOffset: "4px"
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1282
|
+
children: [
|
|
1283
|
+
focusedRegion === "header" && /* @__PURE__ */ jsx4(
|
|
1284
|
+
Chip,
|
|
1285
|
+
{
|
|
1286
|
+
label: "Cabe\xE7alho",
|
|
1287
|
+
size: "small",
|
|
1288
|
+
color: "primary",
|
|
1289
|
+
sx: {
|
|
1290
|
+
position: "absolute",
|
|
1291
|
+
top: -32,
|
|
1292
|
+
left: -4,
|
|
1293
|
+
borderRadius: 0.5,
|
|
1294
|
+
pointerEvents: "none"
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
),
|
|
1298
|
+
/* @__PURE__ */ jsx4(
|
|
1299
|
+
EditableRegion,
|
|
1300
|
+
{
|
|
1301
|
+
html: model.header ?? "",
|
|
1302
|
+
editable,
|
|
1303
|
+
ariaLabel: "Cabe\xE7alho",
|
|
1304
|
+
placeholder: "Cabe\xE7alho (opcional)",
|
|
1305
|
+
onRegister: registerRegion("header"),
|
|
1306
|
+
onFocus: onRegionFocus,
|
|
1307
|
+
onBlur: onRegionBlur,
|
|
1308
|
+
onInput: handleContentChanged
|
|
1309
|
+
},
|
|
1310
|
+
`header-${docVersion}`
|
|
1311
|
+
)
|
|
1312
|
+
]
|
|
1313
|
+
}
|
|
1314
|
+
),
|
|
1315
|
+
/* @__PURE__ */ jsxs4(
|
|
1316
|
+
Box3,
|
|
1317
|
+
{
|
|
1318
|
+
sx: {
|
|
1319
|
+
position: "absolute",
|
|
1320
|
+
bottom: HEADER_FOOTER_DISTANCE_PX,
|
|
1321
|
+
left: padLeft,
|
|
1322
|
+
right: padRight,
|
|
1323
|
+
pointerEvents: "auto",
|
|
1324
|
+
opacity: focusedRegion === "body" ? 0.55 : 1,
|
|
1325
|
+
transition: "opacity 120ms",
|
|
1326
|
+
...focusedRegion === "footer" && {
|
|
1327
|
+
outline: "1px dashed",
|
|
1328
|
+
outlineColor: "primary.main",
|
|
1329
|
+
outlineOffset: "4px"
|
|
1330
|
+
}
|
|
1331
|
+
},
|
|
1332
|
+
children: [
|
|
1333
|
+
focusedRegion === "footer" && /* @__PURE__ */ jsx4(
|
|
1334
|
+
Chip,
|
|
1335
|
+
{
|
|
1336
|
+
label: "Rodap\xE9",
|
|
1337
|
+
size: "small",
|
|
1338
|
+
color: "primary",
|
|
1339
|
+
sx: {
|
|
1340
|
+
position: "absolute",
|
|
1341
|
+
bottom: -32,
|
|
1342
|
+
left: -4,
|
|
1343
|
+
borderRadius: 0.5,
|
|
1344
|
+
pointerEvents: "none"
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
),
|
|
1348
|
+
/* @__PURE__ */ jsx4(
|
|
1349
|
+
EditableRegion,
|
|
1350
|
+
{
|
|
1351
|
+
html: model.footer ?? "",
|
|
1352
|
+
editable,
|
|
1353
|
+
ariaLabel: "Rodap\xE9",
|
|
1354
|
+
placeholder: "Rodap\xE9 (opcional)",
|
|
1355
|
+
onRegister: registerRegion("footer"),
|
|
1356
|
+
onFocus: onRegionFocus,
|
|
1357
|
+
onBlur: onRegionBlur,
|
|
1358
|
+
onInput: handleContentChanged
|
|
1359
|
+
},
|
|
1360
|
+
`footer-${docVersion}`
|
|
1361
|
+
)
|
|
1362
|
+
]
|
|
1363
|
+
}
|
|
1364
|
+
)
|
|
1365
|
+
]
|
|
1366
|
+
}
|
|
1367
|
+
),
|
|
1368
|
+
/* @__PURE__ */ jsx4(
|
|
1369
|
+
Box3,
|
|
1370
|
+
{
|
|
1371
|
+
sx: {
|
|
1372
|
+
position: "absolute",
|
|
1373
|
+
top: bodyTopPx,
|
|
1374
|
+
left: padLeft,
|
|
1375
|
+
width: PAGE_WIDTH_PX - padLeft - padRight,
|
|
1376
|
+
zIndex: 1,
|
|
1377
|
+
opacity: editingChrome ? 0.45 : 1,
|
|
1378
|
+
transition: "opacity 120ms"
|
|
1379
|
+
},
|
|
1380
|
+
children: /* @__PURE__ */ jsx4(
|
|
1381
|
+
EditableRegion,
|
|
1382
|
+
{
|
|
1383
|
+
html: model.body,
|
|
1384
|
+
editable,
|
|
1385
|
+
ariaLabel: "Corpo do documento",
|
|
1386
|
+
placeholder: "Digite o conte\xFAdo do template\u2026",
|
|
1387
|
+
sx: { minHeight: usableFirstPagePx },
|
|
1388
|
+
onRegister: registerRegion("body"),
|
|
1389
|
+
onFocus: onRegionFocus,
|
|
1390
|
+
onBlur: onRegionBlur,
|
|
1391
|
+
onInput: handleContentChanged
|
|
1392
|
+
},
|
|
1393
|
+
`body-${docVersion}`
|
|
1394
|
+
)
|
|
1395
|
+
}
|
|
1396
|
+
),
|
|
1397
|
+
editable && selectedImg && frame && /* @__PURE__ */ jsx4(
|
|
1398
|
+
Box3,
|
|
1399
|
+
{
|
|
1400
|
+
"data-testid": "image-resize-frame",
|
|
1401
|
+
sx: {
|
|
1402
|
+
position: "absolute",
|
|
1403
|
+
top: frame.top,
|
|
1404
|
+
left: frame.left,
|
|
1405
|
+
width: frame.width,
|
|
1406
|
+
height: frame.height,
|
|
1407
|
+
border: "1px solid",
|
|
1408
|
+
borderColor: "primary.main",
|
|
1409
|
+
zIndex: 3,
|
|
1410
|
+
pointerEvents: "none"
|
|
1411
|
+
},
|
|
1412
|
+
children: RESIZE_CORNERS.map((corner) => /* @__PURE__ */ jsx4(
|
|
1413
|
+
Box3,
|
|
1414
|
+
{
|
|
1415
|
+
"data-testid": "image-resize-handle",
|
|
1416
|
+
onMouseDown: startResize(corner),
|
|
1417
|
+
sx: {
|
|
1418
|
+
position: "absolute",
|
|
1419
|
+
width: 10,
|
|
1420
|
+
height: 10,
|
|
1421
|
+
bgcolor: "primary.main",
|
|
1422
|
+
border: "1px solid #fff",
|
|
1423
|
+
pointerEvents: "auto",
|
|
1424
|
+
cursor: corner === "nw" || corner === "se" ? "nwse-resize" : "nesw-resize",
|
|
1425
|
+
top: corner.startsWith("n") ? -5 : "auto",
|
|
1426
|
+
bottom: corner.startsWith("s") ? -5 : "auto",
|
|
1427
|
+
left: corner.endsWith("w") ? -5 : "auto",
|
|
1428
|
+
right: corner.endsWith("e") ? -5 : "auto"
|
|
1429
|
+
}
|
|
1430
|
+
},
|
|
1431
|
+
corner
|
|
1432
|
+
))
|
|
1433
|
+
}
|
|
1434
|
+
),
|
|
1435
|
+
loading && /* @__PURE__ */ jsx4(
|
|
1436
|
+
Box3,
|
|
1437
|
+
{
|
|
1438
|
+
sx: {
|
|
1439
|
+
position: "absolute",
|
|
1440
|
+
inset: 0,
|
|
1441
|
+
display: "flex",
|
|
1442
|
+
alignItems: "center",
|
|
1443
|
+
justifyContent: "center",
|
|
1444
|
+
bgcolor: "rgba(255,255,255,0.6)",
|
|
1445
|
+
zIndex: 3
|
|
1446
|
+
},
|
|
1447
|
+
children: /* @__PURE__ */ jsx4(CircularProgress, {})
|
|
1448
|
+
}
|
|
1449
|
+
)
|
|
1450
|
+
]
|
|
1451
|
+
}
|
|
1452
|
+
)
|
|
1453
|
+
] })
|
|
1454
|
+
]
|
|
1455
|
+
}
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
);
|
|
1459
|
+
var DocxEditor_default = DocxEditor;
|
|
1460
|
+
export {
|
|
1461
|
+
DocxEditor_default as DocxEditor
|
|
1462
|
+
};
|
|
1463
|
+
//# sourceMappingURL=index.mjs.map
|