@meistrari/minuta-editor 1.0.1 → 1.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/dist/chunks/docxExport.mjs +110 -2
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.mjs +79 -5
- package/package.json +1 -1
|
@@ -70,6 +70,8 @@ function parseStyle(style) {
|
|
|
70
70
|
if (bg && bg.toLowerCase() !== "ffffff")
|
|
71
71
|
info.highlight = bg;
|
|
72
72
|
info.size = cssSizeToHalfPoints(getProp(style, "font-size"));
|
|
73
|
+
if (getProp(style, "text-transform") === "uppercase")
|
|
74
|
+
info.caps = true;
|
|
73
75
|
const family = getProp(style, "font-family");
|
|
74
76
|
if (family)
|
|
75
77
|
info.font = family.split(",")[0].trim().replace(/["']/g, "");
|
|
@@ -123,6 +125,7 @@ function measureEl(el) {
|
|
|
123
125
|
color: cssColorToHex(cs.color),
|
|
124
126
|
bold: (Number.parseInt(cs.fontWeight) || 400) >= 600,
|
|
125
127
|
italics: cs.fontStyle.includes("italic"),
|
|
128
|
+
caps: cs.textTransform === "uppercase",
|
|
126
129
|
align: cssAlignToDocx(cs.textAlign)
|
|
127
130
|
};
|
|
128
131
|
}
|
|
@@ -145,6 +148,7 @@ function runOptions(info) {
|
|
|
145
148
|
italics: info.italics,
|
|
146
149
|
underline: info.underline ? {} : void 0,
|
|
147
150
|
strike: info.strike,
|
|
151
|
+
allCaps: info.caps || void 0,
|
|
148
152
|
color: info.color,
|
|
149
153
|
size: info.size,
|
|
150
154
|
font: info.font,
|
|
@@ -165,6 +169,22 @@ function primaryFont(cs) {
|
|
|
165
169
|
const family = cs.fontFamily?.split(",")[0]?.trim().replace(/["']/g, "");
|
|
166
170
|
return family || void 0;
|
|
167
171
|
}
|
|
172
|
+
function withMarks(base, marks) {
|
|
173
|
+
const info = { ...base };
|
|
174
|
+
for (const mark of marks) {
|
|
175
|
+
if (mark.type.name === "bold")
|
|
176
|
+
info.bold = true;
|
|
177
|
+
if (mark.type.name === "italic")
|
|
178
|
+
info.italics = true;
|
|
179
|
+
if (mark.type.name === "underline")
|
|
180
|
+
info.underline = true;
|
|
181
|
+
if (mark.type.name === "strike")
|
|
182
|
+
info.strike = true;
|
|
183
|
+
if (mark.type.name === "inlineStyle")
|
|
184
|
+
Object.assign(info, cleanUndefined(parseStyle(mark.attrs.style)));
|
|
185
|
+
}
|
|
186
|
+
return info;
|
|
187
|
+
}
|
|
168
188
|
function inlineToRuns(node, base, ctx) {
|
|
169
189
|
const runs = [];
|
|
170
190
|
node.forEach((child) => {
|
|
@@ -186,7 +206,7 @@ function inlineToRuns(node, base, ctx) {
|
|
|
186
206
|
}
|
|
187
207
|
if (child.type.name === "variableChip") {
|
|
188
208
|
if (child.attrs.text)
|
|
189
|
-
runs.push(new TextRun({ text: child.attrs.text, ...runOptions(base) }));
|
|
209
|
+
runs.push(new TextRun({ text: child.attrs.text, ...runOptions(withMarks(base, child.marks)) }));
|
|
190
210
|
return;
|
|
191
211
|
}
|
|
192
212
|
if (child.type.name === "hardBreak") {
|
|
@@ -245,6 +265,7 @@ function domInlineToRuns(root, fallback = {}) {
|
|
|
245
265
|
italics: cs.fontStyle.includes("italic") || void 0,
|
|
246
266
|
underline: cs.textDecorationLine?.includes("underline") ? {} : void 0,
|
|
247
267
|
strike: cs.textDecorationLine?.includes("line-through") || void 0,
|
|
268
|
+
allCaps: cs.textTransform === "uppercase" || void 0,
|
|
248
269
|
characterSpacing: TRACKING_COMPENSATION_TW
|
|
249
270
|
}));
|
|
250
271
|
}
|
|
@@ -296,7 +317,7 @@ function spacingFrom(m, style) {
|
|
|
296
317
|
function textBlockToParagraph(node, pos, ctx, opts = {}) {
|
|
297
318
|
const m = measureAt(ctx, pos);
|
|
298
319
|
const style = node.attrs.style;
|
|
299
|
-
const base = m ? { bold: m.bold, italics: m.italics, color: m.color, size: m.sizeHalf, font: primaryFont(m.cs) } : parseStyle(style);
|
|
320
|
+
const base = m ? { bold: m.bold, italics: m.italics, caps: m.caps, color: m.color, size: m.sizeHalf, font: primaryFont(m.cs) } : parseStyle(style);
|
|
300
321
|
if (ctx.extraTrackingTw)
|
|
301
322
|
base.trackingTw = ctx.extraTrackingTw;
|
|
302
323
|
const ownIndent = m ? m.marginLeftTw + Math.round((Number.parseFloat(m.cs.paddingLeft) || 0) * PX_TO_TWIP) : cssLengthToTwips(getProp(style, "margin-left")) ?? 0;
|
|
@@ -652,6 +673,66 @@ function isFlexRow(node) {
|
|
|
652
673
|
return false;
|
|
653
674
|
return node.childCount > 1;
|
|
654
675
|
}
|
|
676
|
+
function islandImage(node) {
|
|
677
|
+
if (node.type.name !== "htmlIsland")
|
|
678
|
+
return null;
|
|
679
|
+
const tpl = document.createElement("template");
|
|
680
|
+
tpl.innerHTML = node.attrs.html || "";
|
|
681
|
+
const el = tpl.content.firstElementChild;
|
|
682
|
+
if (!el)
|
|
683
|
+
return null;
|
|
684
|
+
const img = el.tagName === "IMG" ? el : el.querySelector("img");
|
|
685
|
+
return img instanceof HTMLImageElement ? img : null;
|
|
686
|
+
}
|
|
687
|
+
function collectImageIslands(node, pos) {
|
|
688
|
+
if (islandImage(node))
|
|
689
|
+
return [{ node, pos }];
|
|
690
|
+
if (node.type.name !== "styledDiv")
|
|
691
|
+
return null;
|
|
692
|
+
const out = [];
|
|
693
|
+
let childPos = pos + 1;
|
|
694
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
695
|
+
const child = node.child(i);
|
|
696
|
+
if (!(child.isTextblock && child.content.size === 0)) {
|
|
697
|
+
const inner = collectImageIslands(child, childPos);
|
|
698
|
+
if (!inner)
|
|
699
|
+
return null;
|
|
700
|
+
out.push(...inner);
|
|
701
|
+
}
|
|
702
|
+
childPos += child.nodeSize;
|
|
703
|
+
}
|
|
704
|
+
return out.length ? out : null;
|
|
705
|
+
}
|
|
706
|
+
async function inlineImagesParagraph(islands, innerPx, ctx) {
|
|
707
|
+
const GAP_PX = 8;
|
|
708
|
+
const items = [];
|
|
709
|
+
for (const { node, pos } of islands) {
|
|
710
|
+
const img = islandImage(node);
|
|
711
|
+
const src = await srcToDataUrl(img.getAttribute("src") || "");
|
|
712
|
+
if (!src)
|
|
713
|
+
continue;
|
|
714
|
+
const live = domAt(ctx, pos)?.querySelector("img");
|
|
715
|
+
const styleW = Number.parseFloat(getProp(img.getAttribute("style"), "width") ?? "");
|
|
716
|
+
const widthPx = live?.offsetWidth || 0 || (Number.isFinite(styleW) && styleW > 0 ? styleW : 0) || Math.min(innerPx, 200);
|
|
717
|
+
items.push({ src, widthPx });
|
|
718
|
+
}
|
|
719
|
+
if (!items.length)
|
|
720
|
+
return null;
|
|
721
|
+
const total = items.reduce((a, b) => a + b.widthPx, 0) + GAP_PX * (items.length - 1);
|
|
722
|
+
const scale = innerPx > 0 && total > innerPx ? (innerPx - GAP_PX * (items.length - 1)) / (total - GAP_PX * (items.length - 1)) : 1;
|
|
723
|
+
const runs = [];
|
|
724
|
+
for (let i = 0; i < items.length; i++) {
|
|
725
|
+
const run = await dataUrlToImageRun(items[i].src, Math.max(1, Math.round(items[i].widthPx * scale)));
|
|
726
|
+
if (!run)
|
|
727
|
+
continue;
|
|
728
|
+
if (runs.length)
|
|
729
|
+
runs.push(new TextRun({ text: " " }));
|
|
730
|
+
runs.push(run);
|
|
731
|
+
}
|
|
732
|
+
if (!runs.length)
|
|
733
|
+
return null;
|
|
734
|
+
return new Paragraph({ children: runs, alignment: AlignmentType.CENTER, spacing: { after: 0, before: 0 } });
|
|
735
|
+
}
|
|
655
736
|
async function tableToDocx(node, pos, contentWidthPx, ctx) {
|
|
656
737
|
const tableDomRaw = domAt(ctx, pos);
|
|
657
738
|
const tableEl = tableDomRaw?.tagName === "TABLE" ? tableDomRaw : tableDomRaw?.querySelector("table") ?? null;
|
|
@@ -681,9 +762,36 @@ async function tableToDocx(node, pos, contentWidthPx, ctx) {
|
|
|
681
762
|
}
|
|
682
763
|
}
|
|
683
764
|
const cellCtx = { ...ctx, insideCell: true, indentLeft: 0, lastAfterTw: 0, pending: { breakBefore: false }, extraTrackingTw: extraTrackingTw || void 0 };
|
|
765
|
+
const cellInnerPx = cellM?.el ? cellM.el.clientWidth - (Number.parseFloat(cellM.cs.paddingLeft) || 0) - (Number.parseFloat(cellM.cs.paddingRight) || 0) : contentWidthPx / 2;
|
|
766
|
+
const isEmptyBlock = (n) => n.isTextblock && n.content.size === 0;
|
|
767
|
+
const besideImage = (k) => k > 0 && !!collectImageIslands(cellNode.child(k - 1), 0) || k + 1 < cellNode.childCount && !!collectImageIslands(cellNode.child(k + 1), 0);
|
|
684
768
|
let childPos = cellPos + 1;
|
|
685
769
|
for (let k = 0; k < cellNode.childCount; k++) {
|
|
686
770
|
const child = cellNode.child(k);
|
|
771
|
+
if (isEmptyBlock(child) && besideImage(k)) {
|
|
772
|
+
childPos += child.nodeSize;
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
const islands = collectImageIslands(child, childPos);
|
|
776
|
+
if (islands?.length) {
|
|
777
|
+
let end = k + 1;
|
|
778
|
+
let endPos = childPos + child.nodeSize;
|
|
779
|
+
while (end < cellNode.childCount) {
|
|
780
|
+
const next = cellNode.child(end);
|
|
781
|
+
const more = collectImageIslands(next, endPos);
|
|
782
|
+
if (!more?.length)
|
|
783
|
+
break;
|
|
784
|
+
islands.push(...more);
|
|
785
|
+
endPos += next.nodeSize;
|
|
786
|
+
end++;
|
|
787
|
+
}
|
|
788
|
+
const para = await inlineImagesParagraph(islands, cellInnerPx, cellCtx);
|
|
789
|
+
if (para)
|
|
790
|
+
cellContent.push(para);
|
|
791
|
+
childPos = endPos;
|
|
792
|
+
k = end - 1;
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
687
795
|
if (child.isTextblock)
|
|
688
796
|
cellContent.push(textBlockToParagraph(child, childPos, cellCtx));
|
|
689
797
|
else
|
package/dist/index.d.mts
CHANGED
|
@@ -232,6 +232,12 @@ interface MinutaEditorOptions {
|
|
|
232
232
|
toolbar?: boolean;
|
|
233
233
|
/** realce de proveniência de bloco no hover (só faz sentido no modo estruturado) */
|
|
234
234
|
showBlockOrigin?: boolean;
|
|
235
|
+
/**
|
|
236
|
+
* Dispara `onBlockClick` também com o editor editável. Desligado por
|
|
237
|
+
* padrão: na tela do defesa.ai o clique em edição é para posicionar o
|
|
238
|
+
* cursor, e abrir o bloco do checklist a cada clique atrapalharia.
|
|
239
|
+
*/
|
|
240
|
+
blockClickWhileEditing?: boolean;
|
|
235
241
|
onReady?: (tiptap: Editor) => void;
|
|
236
242
|
/** o documento mudou por edição (troca de imagem fria não conta) */
|
|
237
243
|
onChange?: () => void;
|
|
@@ -274,6 +280,8 @@ interface MinutaEditor {
|
|
|
274
280
|
setToolbar: (on: boolean) => void;
|
|
275
281
|
setMargins: (margins: MinutaMargins) => void;
|
|
276
282
|
setShowBlockOrigin: (on: boolean) => void;
|
|
283
|
+
/** liga/desliga `onBlockClick` em edição depois de criado (ver `blockClickWhileEditing`) */
|
|
284
|
+
setBlockClickWhileEditing: (on: boolean) => void;
|
|
277
285
|
/**
|
|
278
286
|
* Ponte formulário → documento: rola até a primeira ocorrência da variável
|
|
279
287
|
* e a destaca por um instante. `false` se ela não está no documento.
|
|
@@ -286,7 +294,13 @@ interface MinutaEditor {
|
|
|
286
294
|
* editou o texto em volta. Devolve quantas ocorrências mudaram.
|
|
287
295
|
*/
|
|
288
296
|
setVariableValue: (variableId: string, value: string) => number;
|
|
289
|
-
/**
|
|
297
|
+
/**
|
|
298
|
+
* Troca a imagem de uma variável de imagem: o `<img data-variable-id>` ganha
|
|
299
|
+
* o `src` novo, e um aviso de imagem faltando com esse id vira a imagem.
|
|
300
|
+
* Devolve quantas ocorrências mudaram.
|
|
301
|
+
*/
|
|
302
|
+
setVariableImage: (variableId: string, src: string) => number;
|
|
303
|
+
/** ids marcados no documento, na ordem, sem repetição: chips de texto, imagens e imagens faltando */
|
|
290
304
|
variablesInDocument: () => string[];
|
|
291
305
|
/** repagina (o host chama após mudar algo que afeta altura fora do editor) */
|
|
292
306
|
schedulePaginate: () => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -232,6 +232,12 @@ interface MinutaEditorOptions {
|
|
|
232
232
|
toolbar?: boolean;
|
|
233
233
|
/** realce de proveniência de bloco no hover (só faz sentido no modo estruturado) */
|
|
234
234
|
showBlockOrigin?: boolean;
|
|
235
|
+
/**
|
|
236
|
+
* Dispara `onBlockClick` também com o editor editável. Desligado por
|
|
237
|
+
* padrão: na tela do defesa.ai o clique em edição é para posicionar o
|
|
238
|
+
* cursor, e abrir o bloco do checklist a cada clique atrapalharia.
|
|
239
|
+
*/
|
|
240
|
+
blockClickWhileEditing?: boolean;
|
|
235
241
|
onReady?: (tiptap: Editor) => void;
|
|
236
242
|
/** o documento mudou por edição (troca de imagem fria não conta) */
|
|
237
243
|
onChange?: () => void;
|
|
@@ -274,6 +280,8 @@ interface MinutaEditor {
|
|
|
274
280
|
setToolbar: (on: boolean) => void;
|
|
275
281
|
setMargins: (margins: MinutaMargins) => void;
|
|
276
282
|
setShowBlockOrigin: (on: boolean) => void;
|
|
283
|
+
/** liga/desliga `onBlockClick` em edição depois de criado (ver `blockClickWhileEditing`) */
|
|
284
|
+
setBlockClickWhileEditing: (on: boolean) => void;
|
|
277
285
|
/**
|
|
278
286
|
* Ponte formulário → documento: rola até a primeira ocorrência da variável
|
|
279
287
|
* e a destaca por um instante. `false` se ela não está no documento.
|
|
@@ -286,7 +294,13 @@ interface MinutaEditor {
|
|
|
286
294
|
* editou o texto em volta. Devolve quantas ocorrências mudaram.
|
|
287
295
|
*/
|
|
288
296
|
setVariableValue: (variableId: string, value: string) => number;
|
|
289
|
-
/**
|
|
297
|
+
/**
|
|
298
|
+
* Troca a imagem de uma variável de imagem: o `<img data-variable-id>` ganha
|
|
299
|
+
* o `src` novo, e um aviso de imagem faltando com esse id vira a imagem.
|
|
300
|
+
* Devolve quantas ocorrências mudaram.
|
|
301
|
+
*/
|
|
302
|
+
setVariableImage: (variableId: string, src: string) => number;
|
|
303
|
+
/** ids marcados no documento, na ordem, sem repetição: chips de texto, imagens e imagens faltando */
|
|
290
304
|
variablesInDocument: () => string[];
|
|
291
305
|
/** repagina (o host chama após mudar algo que afeta altura fora do editor) */
|
|
292
306
|
schedulePaginate: () => void;
|
package/dist/index.mjs
CHANGED
|
@@ -1426,8 +1426,9 @@ function createMinutaEditor(element, opts) {
|
|
|
1426
1426
|
opts.onEditChrome?.(part);
|
|
1427
1427
|
window.dispatchEvent(new CustomEvent("poc:edit-chrome", { detail: { part, editor: tiptap } }));
|
|
1428
1428
|
}
|
|
1429
|
+
let blockClickWhileEditing = opts.blockClickWhileEditing === true;
|
|
1429
1430
|
function onSheetClick(event) {
|
|
1430
|
-
if (editable)
|
|
1431
|
+
if (editable && !blockClickWhileEditing)
|
|
1431
1432
|
return;
|
|
1432
1433
|
const target = event.target;
|
|
1433
1434
|
if (target.closest(".poc-varchip") || target.closest(".poc-page-headerarea") || target.closest(".poc-page-footerarea") || target.closest(".poc-first-header") || target.closest(".poc-last-footer")) {
|
|
@@ -1652,9 +1653,29 @@ function createMinutaEditor(element, opts) {
|
|
|
1652
1653
|
text: String(detail.text ?? "")
|
|
1653
1654
|
});
|
|
1654
1655
|
}
|
|
1656
|
+
function variableTargetElement(variableId) {
|
|
1657
|
+
const id = CSS.escape(variableId);
|
|
1658
|
+
const chip = sheet.querySelector(`.poc-varchip [data-variable-id="${id}"], .poc-varchip[data-variable-id="${id}"]`);
|
|
1659
|
+
if (chip)
|
|
1660
|
+
return chip.closest(".poc-varchip") ?? chip;
|
|
1661
|
+
const img = sheet.querySelector(`img[data-variable-id="${id}"]`);
|
|
1662
|
+
if (img)
|
|
1663
|
+
return img;
|
|
1664
|
+
const ed = tiptap;
|
|
1665
|
+
if (!ed || ed.isDestroyed)
|
|
1666
|
+
return null;
|
|
1667
|
+
let found = null;
|
|
1668
|
+
ed.state.doc.descendants((node, pos) => {
|
|
1669
|
+
if (found || node.type.name !== "missingImage" || node.attrs.variableId !== variableId)
|
|
1670
|
+
return;
|
|
1671
|
+
const dom = ed.view.nodeDOM(pos);
|
|
1672
|
+
if (dom instanceof HTMLElement)
|
|
1673
|
+
found = dom;
|
|
1674
|
+
});
|
|
1675
|
+
return found;
|
|
1676
|
+
}
|
|
1655
1677
|
function scrollToVariable(variableId) {
|
|
1656
|
-
const
|
|
1657
|
-
const chip = el?.closest(".poc-varchip") ?? el;
|
|
1678
|
+
const chip = variableTargetElement(variableId);
|
|
1658
1679
|
if (!chip)
|
|
1659
1680
|
return false;
|
|
1660
1681
|
chip.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
@@ -1682,17 +1703,66 @@ function createMinutaEditor(element, opts) {
|
|
|
1682
1703
|
ed.view.dispatch(tr);
|
|
1683
1704
|
return count;
|
|
1684
1705
|
}
|
|
1706
|
+
function setVariableImage(variableId, src) {
|
|
1707
|
+
const ed = tiptap;
|
|
1708
|
+
if (!ed || ed.isDestroyed)
|
|
1709
|
+
return 0;
|
|
1710
|
+
const islandType = ed.state.schema.nodes.htmlIsland;
|
|
1711
|
+
const tr = ed.state.tr;
|
|
1712
|
+
let count = 0;
|
|
1713
|
+
const hits = [];
|
|
1714
|
+
ed.state.doc.descendants((node, pos) => {
|
|
1715
|
+
if (node.type.name === "missingImage" && node.attrs.variableId === variableId)
|
|
1716
|
+
hits.push({ pos, node });
|
|
1717
|
+
else if (node.type.name === "htmlIsland" && String(node.attrs.html ?? "").includes(`data-variable-id="${variableId}"`))
|
|
1718
|
+
hits.push({ pos, node });
|
|
1719
|
+
});
|
|
1720
|
+
for (const { pos, node } of hits.reverse()) {
|
|
1721
|
+
if (node.type.name === "missingImage") {
|
|
1722
|
+
if (!islandType)
|
|
1723
|
+
continue;
|
|
1724
|
+
const html = `<img src="${escapeAttr(src)}" alt="${escapeAttr(variableId)}" data-variable-id="${escapeAttr(variableId)}" data-parent-block-id="${escapeAttr(String(node.attrs.parentBlockId ?? ""))}" style="max-width: 100%; height: auto;" />`;
|
|
1725
|
+
tr.replaceWith(pos, pos + node.nodeSize, islandType.create({ html }));
|
|
1726
|
+
count++;
|
|
1727
|
+
continue;
|
|
1728
|
+
}
|
|
1729
|
+
const tpl = document.createElement("template");
|
|
1730
|
+
tpl.innerHTML = String(node.attrs.html ?? "");
|
|
1731
|
+
const imgs = tpl.content.querySelectorAll(`img[data-variable-id="${CSS.escape(variableId)}"]`);
|
|
1732
|
+
if (!imgs.length)
|
|
1733
|
+
continue;
|
|
1734
|
+
for (const img of imgs)
|
|
1735
|
+
img.setAttribute("src", src);
|
|
1736
|
+
tr.setNodeMarkup(pos, void 0, { ...node.attrs, html: tpl.innerHTML });
|
|
1737
|
+
count += imgs.length;
|
|
1738
|
+
}
|
|
1739
|
+
if (count)
|
|
1740
|
+
ed.view.dispatch(tr);
|
|
1741
|
+
return count;
|
|
1742
|
+
}
|
|
1685
1743
|
function variablesInDocument() {
|
|
1686
1744
|
const ed = tiptap;
|
|
1687
1745
|
const ids = [];
|
|
1688
1746
|
if (!ed || ed.isDestroyed)
|
|
1689
1747
|
return ids;
|
|
1748
|
+
const add = (id) => {
|
|
1749
|
+
if (typeof id === "string" && id && !ids.includes(id))
|
|
1750
|
+
ids.push(id);
|
|
1751
|
+
};
|
|
1690
1752
|
ed.state.doc.descendants((node) => {
|
|
1691
|
-
if (node.type.name === "variableChip"
|
|
1692
|
-
|
|
1753
|
+
if (node.type.name === "variableChip" || node.type.name === "missingImage") {
|
|
1754
|
+
add(node.attrs.variableId);
|
|
1755
|
+
} else if (node.type.name === "htmlIsland") {
|
|
1756
|
+
for (const m of String(node.attrs.html ?? "").matchAll(/data-variable-id="([^"]+)"/g)) {
|
|
1757
|
+
add(m[1]);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1693
1760
|
});
|
|
1694
1761
|
return ids;
|
|
1695
1762
|
}
|
|
1763
|
+
function escapeAttr(value) {
|
|
1764
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1765
|
+
}
|
|
1696
1766
|
function buildBubbleMenu() {
|
|
1697
1767
|
const TEXT_COLORS = [
|
|
1698
1768
|
{ label: "Preto", value: "#1C242D" },
|
|
@@ -1862,8 +1932,12 @@ function createMinutaEditor(element, opts) {
|
|
|
1862
1932
|
setShowBlockOrigin(on) {
|
|
1863
1933
|
toggleClass(root, "poc-no-block-origin", !on);
|
|
1864
1934
|
},
|
|
1935
|
+
setBlockClickWhileEditing(on) {
|
|
1936
|
+
blockClickWhileEditing = on;
|
|
1937
|
+
},
|
|
1865
1938
|
scrollToVariable,
|
|
1866
1939
|
setVariableValue,
|
|
1940
|
+
setVariableImage,
|
|
1867
1941
|
variablesInDocument,
|
|
1868
1942
|
schedulePaginate,
|
|
1869
1943
|
refreshChrome,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meistrari/minuta-editor",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Editor de minuta do defesa.ai sem framework: folha A4 paginada, edição rica, placeholders de variável e export DOCX fiel ao preview — monta em qualquer app (React, Vue, vanilla)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|