@sofereditor/react 0.9.0 → 0.10.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/index.d.cts CHANGED
@@ -417,8 +417,15 @@ declare function NodeView({ block, index, children, fragment, tableFragment }: N
417
417
  *
418
418
  * Keys are stable on `${blockIndex}-${runningOffset}` so React reconciliation
419
419
  * survives split/merge of runs under collaboration.
420
+ *
421
+ * `baseOffset` shifts every emitted `data-embed-offset` (and key) by a fixed
422
+ * amount. Needed when `delta` is only a SLICE of a larger model range — e.g.
423
+ * one line of a célula-lista, where each `<li>` gets its own line-local delta
424
+ * but `data-embed-offset` must stay in CELL coordinates (contract with
425
+ * `Editor.tsx`'s embed-selection and `ImageResizeOverlay`'s DOM query).
426
+ * Defaults to 0, which reproduces the old line-local numbering exactly.
420
427
  */
421
- declare function renderInline(delta: DeltaOp[], keyPrefix: number | string): ReactNode;
428
+ declare function renderInline(delta: DeltaOp[], keyPrefix: number | string, baseOffset?: number): ReactNode;
422
429
 
423
430
  interface EditorProviderProps {
424
431
  editor: UseEditorResult;
@@ -512,10 +519,11 @@ declare function applyDomSelection(root: HTMLElement, modelSel: Selection): void
512
519
  * com o marcador (número/símbolo) como texto literal dentro de um span
513
520
  * `mso-list:Ignore`. `parseWordListLevel` + a exclusão desse span tratam isso.
514
521
  *
515
- * fontFamily é deliberadamente NUNCA emitida: a escola padroniza Arial e cada
516
- * documento tem sua própria tipografia (mesma decisão de `import-docx`, que
517
- * descarta `w:rFonts`). fontSize É preservado (ver `parseFontSizePt`): o
518
- * modelo já suporta a mark, a barra já tem controle de tamanho, e descartar o
522
+ * fontFamily é deliberadamente NUNCA emitida: a escola padroniza uma fonte só,
523
+ * definida no CSS (`.ed-root`) e cada documento tem sua própria tipografia
524
+ * (mesma decisão de `import-docx`, que descarta `w:rFonts`). fontSize É
525
+ * preservado (ver `parseFontSizePt`): o modelo já suporta a mark, a barra já
526
+ * tem controle de tamanho, e descartar o
519
527
  * tamanho perdia informação real (título de 37.5pt colado do Google Docs saía
520
528
  * do tamanho do corpo). Unidades que dependem do contexto de herança do
521
529
  * documento de origem (em/rem/%/larger/smaller/medium) não são resolvíveis
package/dist/index.d.ts CHANGED
@@ -417,8 +417,15 @@ declare function NodeView({ block, index, children, fragment, tableFragment }: N
417
417
  *
418
418
  * Keys are stable on `${blockIndex}-${runningOffset}` so React reconciliation
419
419
  * survives split/merge of runs under collaboration.
420
+ *
421
+ * `baseOffset` shifts every emitted `data-embed-offset` (and key) by a fixed
422
+ * amount. Needed when `delta` is only a SLICE of a larger model range — e.g.
423
+ * one line of a célula-lista, where each `<li>` gets its own line-local delta
424
+ * but `data-embed-offset` must stay in CELL coordinates (contract with
425
+ * `Editor.tsx`'s embed-selection and `ImageResizeOverlay`'s DOM query).
426
+ * Defaults to 0, which reproduces the old line-local numbering exactly.
420
427
  */
421
- declare function renderInline(delta: DeltaOp[], keyPrefix: number | string): ReactNode;
428
+ declare function renderInline(delta: DeltaOp[], keyPrefix: number | string, baseOffset?: number): ReactNode;
422
429
 
423
430
  interface EditorProviderProps {
424
431
  editor: UseEditorResult;
@@ -512,10 +519,11 @@ declare function applyDomSelection(root: HTMLElement, modelSel: Selection): void
512
519
  * com o marcador (número/símbolo) como texto literal dentro de um span
513
520
  * `mso-list:Ignore`. `parseWordListLevel` + a exclusão desse span tratam isso.
514
521
  *
515
- * fontFamily é deliberadamente NUNCA emitida: a escola padroniza Arial e cada
516
- * documento tem sua própria tipografia (mesma decisão de `import-docx`, que
517
- * descarta `w:rFonts`). fontSize É preservado (ver `parseFontSizePt`): o
518
- * modelo já suporta a mark, a barra já tem controle de tamanho, e descartar o
522
+ * fontFamily é deliberadamente NUNCA emitida: a escola padroniza uma fonte só,
523
+ * definida no CSS (`.ed-root`) e cada documento tem sua própria tipografia
524
+ * (mesma decisão de `import-docx`, que descarta `w:rFonts`). fontSize É
525
+ * preservado (ver `parseFontSizePt`): o modelo já suporta a mark, a barra já
526
+ * tem controle de tamanho, e descartar o
519
527
  * tamanho perdia informação real (título de 37.5pt colado do Google Docs saía
520
528
  * do tamanho do corpo). Unidades que dependem do contexto de herança do
521
529
  * documento de origem (em/rem/%/larger/smaller/medium) não são resolvíveis
package/dist/index.js CHANGED
@@ -94,6 +94,11 @@ function isWrapAnchorImg(n) {
94
94
  function isPhantomEmbed(n) {
95
95
  return n.nodeType === Node.ELEMENT_NODE && n.dataset.embedPhantom === "true";
96
96
  }
97
+ function isCellLineBoundary(n) {
98
+ if (n.nodeType !== Node.ELEMENT_NODE) return false;
99
+ const line = n.dataset.cellLine;
100
+ return line != null && Number(line) > 0;
101
+ }
97
102
  function indexInParent(n) {
98
103
  const parent = n.parentNode;
99
104
  if (!parent) return 0;
@@ -121,6 +126,7 @@ function textOffsetWithin(container, node, offsetInNode) {
121
126
  let done = false;
122
127
  function visit(n) {
123
128
  if (done) return;
129
+ if (isCellLineBoundary(n)) offset += 1;
124
130
  if (n === target) {
125
131
  if (n.nodeType === Node.TEXT_NODE) {
126
132
  offset += targetOff;
@@ -221,6 +227,7 @@ function locatePoint(root, pos) {
221
227
  let remaining = Math.max(0, pos.offset - containerStart);
222
228
  let result = null;
223
229
  let lastImg = null;
230
+ let lastBoundary = null;
224
231
  let lastText = null;
225
232
  function visit(n) {
226
233
  if (result) return;
@@ -232,6 +239,7 @@ function locatePoint(root, pos) {
232
239
  }
233
240
  remaining -= len;
234
241
  lastImg = null;
242
+ lastBoundary = null;
235
243
  lastText = n;
236
244
  return;
237
245
  }
@@ -239,6 +247,25 @@ function locatePoint(root, pos) {
239
247
  const el = n;
240
248
  if (isRejectedSubtree(el, container)) return;
241
249
  if (isImgEmbed(el) && isWrapAnchorImg(el)) return;
250
+ if (isCellLineBoundary(el)) {
251
+ if (remaining === 0) {
252
+ if (lastImg) {
253
+ const parent = lastImg.parentNode;
254
+ if (parent) {
255
+ result = { node: parent, offset: indexInParent(lastImg) + 1 };
256
+ return;
257
+ }
258
+ }
259
+ const prevLine = lastBoundary ?? el.previousElementSibling;
260
+ if (prevLine) {
261
+ result = { node: prevLine, offset: 0 };
262
+ return;
263
+ }
264
+ }
265
+ remaining -= 1;
266
+ lastBoundary = el;
267
+ lastImg = null;
268
+ }
242
269
  if (isImgEmbed(el) || isPhantomEmbed(el)) {
243
270
  if (remaining === 0) {
244
271
  const parent = el.parentNode;
@@ -249,6 +276,7 @@ function locatePoint(root, pos) {
249
276
  }
250
277
  remaining -= 1;
251
278
  lastImg = el;
279
+ lastBoundary = null;
252
280
  return;
253
281
  }
254
282
  for (const child of Array.from(el.childNodes)) {
@@ -258,7 +286,10 @@ function locatePoint(root, pos) {
258
286
  }
259
287
  visit(container);
260
288
  if (result) return result;
261
- if (lastImg && remaining === 0) {
289
+ if (lastBoundary) {
290
+ return { node: lastBoundary, offset: 0 };
291
+ }
292
+ if (lastImg) {
262
293
  const parent = lastImg.parentNode;
263
294
  if (parent) return { node: parent, offset: indexInParent(lastImg) + 1 };
264
295
  }
@@ -2815,7 +2846,9 @@ import { useMemo as useMemo2, useRef as useRef8 } from "react";
2815
2846
  import {
2816
2847
  answerLineStyle,
2817
2848
  cellBorderStyle,
2849
+ deltaLength as deltaLength2,
2818
2850
  normalizarLarguras as normalizarLarguras2,
2851
+ splitDeltaByLines,
2819
2852
  tableRectSelection
2820
2853
  } from "@sofereditor/core";
2821
2854
 
@@ -2827,11 +2860,11 @@ import {
2827
2860
  splitUnderscoreRuns
2828
2861
  } from "@sofereditor/core";
2829
2862
  import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
2830
- function renderInline(delta, keyPrefix) {
2863
+ function renderInline(delta, keyPrefix, baseOffset = 0) {
2831
2864
  if (delta.length === 0 || delta.length === 1 && typeof delta[0].insert === "string" && delta[0].insert === "") {
2832
2865
  return /* @__PURE__ */ jsx10("br", { "data-empty": "true" });
2833
2866
  }
2834
- let offset = 0;
2867
+ let offset = baseOffset;
2835
2868
  const wrapAnchors = [];
2836
2869
  const body = [];
2837
2870
  for (const op of delta) {
@@ -3499,6 +3532,27 @@ function clampListLevel(l) {
3499
3532
  if (typeof l !== "number" || !Number.isFinite(l)) return 0;
3500
3533
  return Math.max(0, Math.min(5, Math.trunc(l)));
3501
3534
  }
3535
+ function renderCellContent(attrs, delta, keyPrefix) {
3536
+ const kind = attrs?.listKind;
3537
+ if (!kind) return renderInline(delta, keyPrefix);
3538
+ const linhas = splitDeltaByLines(delta);
3539
+ const Tag = kind === "ordered" ? "ol" : "ul";
3540
+ let baseOffset = 0;
3541
+ return /* @__PURE__ */ jsx12(
3542
+ Tag,
3543
+ {
3544
+ className: `ed-list ed-list-${kind}`,
3545
+ "data-list-kind": kind,
3546
+ start: kind === "ordered" && typeof attrs?.listStart === "number" ? attrs.listStart : void 0,
3547
+ style: attrs?.listStyle ? { listStyleType: attrs.listStyle } : void 0,
3548
+ children: linhas.map((linha, i) => {
3549
+ const li = /* @__PURE__ */ jsx12("li", { className: "ed-listitem", "data-cell-line": i, children: renderInline(linha, `${keyPrefix}-l${i}`, baseOffset) }, i);
3550
+ baseOffset += deltaLength2(linha) + 1;
3551
+ return li;
3552
+ })
3553
+ }
3554
+ );
3555
+ }
3502
3556
  function TableView({ block, index, tableFragment }) {
3503
3557
  const rows = clampPositive(block.attrs.rows);
3504
3558
  const cols = clampPositive(block.attrs.cols);
@@ -3564,7 +3618,7 @@ function TableView({ block, index, tableFragment }) {
3564
3618
  )
3565
3619
  ),
3566
3620
  className: inRect ? "ed-cell ed-cell--selected" : "ed-cell",
3567
- children: renderInline(delta, `t${index}-c${flat}`)
3621
+ children: renderCellContent(cell?.attrs, delta, `t${index}-c${flat}`)
3568
3622
  },
3569
3623
  c
3570
3624
  );
@@ -4403,6 +4457,10 @@ function useEditor(opts = {}) {
4403
4457
  const isListActive = useCallback7(
4404
4458
  (kind) => {
4405
4459
  const sel = selectionRef.current;
4460
+ if (sel.focus.cellIndex != null && doc.isTable(sel.focus.blockIndex)) {
4461
+ const real = doc.realCellIndex(sel.focus.blockIndex, sel.focus.cellIndex);
4462
+ return real != null && doc.getCellAttrs(sel.focus.blockIndex, real).listKind === kind;
4463
+ }
4406
4464
  const { start, end } = orderedRange(sel);
4407
4465
  for (let i = start.blockIndex; i <= end.blockIndex; i++) {
4408
4466
  if (doc.getBlockType(i) !== "listItem") return false;
@@ -5844,7 +5902,6 @@ function TableBorderPanel({
5844
5902
 
5845
5903
  // src/Toolbar.tsx
5846
5904
  import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
5847
- var FONT_FAMILIES = ["Arial"];
5848
5905
  var FONT_SIZES = ["8pt", "10pt", "11pt", "12pt", "14pt", "16pt", "18pt", "24pt", "32pt", "48pt"];
5849
5906
  var BLOCK_OPTIONS = [
5850
5907
  { value: "paragraph", label: "Par\xE1grafo", type: "paragraph" },
@@ -5918,11 +5975,6 @@ function Toolbar({ className }) {
5918
5975
  const onHighlightChange = (e) => {
5919
5976
  setMark("highlight", e.target.value);
5920
5977
  };
5921
- const onFontFamilyChange = (e) => {
5922
- const v = e.target.value;
5923
- if (v === "") removeMark("fontFamily");
5924
- else setMark("fontFamily", v);
5925
- };
5926
5978
  const onFontSizeChange = (e) => {
5927
5979
  const v = e.target.value;
5928
5980
  if (v === "") removeMark("fontSize");
@@ -5983,7 +6035,6 @@ function Toolbar({ className }) {
5983
6035
  );
5984
6036
  const colorValue = typeof active.color === "string" ? active.color : "#000000";
5985
6037
  const highlightValue = typeof active.highlight === "string" ? active.highlight : "#fff176";
5986
- const fontFamilyValue = typeof active.fontFamily === "string" ? active.fontFamily : "";
5987
6038
  const fontSizeValue = typeof active.fontSize === "string" ? active.fontSize : "";
5988
6039
  const blockValue = blockSelectValue(blockType, blockLevel);
5989
6040
  const selectedEmbed = editor.getSelectedEmbed();
@@ -6082,36 +6133,20 @@ function Toolbar({ className }) {
6082
6133
  }
6083
6134
  )
6084
6135
  ] }),
6085
- /* @__PURE__ */ jsxs15(Group, { children: [
6086
- /* @__PURE__ */ jsxs15(
6087
- "select",
6088
- {
6089
- className: "ed-toolbar-select",
6090
- value: fontFamilyValue,
6091
- onChange: onFontFamilyChange,
6092
- onMouseDown: stop,
6093
- "aria-label": "Fam\xEDlia da fonte",
6094
- children: [
6095
- /* @__PURE__ */ jsx15("option", { value: "", children: "Fonte\u2026" }),
6096
- FONT_FAMILIES.map((f) => /* @__PURE__ */ jsx15("option", { value: f, children: f }, f))
6097
- ]
6098
- }
6099
- ),
6100
- /* @__PURE__ */ jsxs15(
6101
- "select",
6102
- {
6103
- className: "ed-toolbar-select",
6104
- value: fontSizeValue,
6105
- onChange: onFontSizeChange,
6106
- onMouseDown: stop,
6107
- "aria-label": "Tamanho da fonte",
6108
- children: [
6109
- /* @__PURE__ */ jsx15("option", { value: "", children: "Tamanho\u2026" }),
6110
- FONT_SIZES.map((s) => /* @__PURE__ */ jsx15("option", { value: s, children: s }, s))
6111
- ]
6112
- }
6113
- )
6114
- ] }),
6136
+ /* @__PURE__ */ jsx15(Group, { children: /* @__PURE__ */ jsxs15(
6137
+ "select",
6138
+ {
6139
+ className: "ed-toolbar-select",
6140
+ value: fontSizeValue,
6141
+ onChange: onFontSizeChange,
6142
+ onMouseDown: stop,
6143
+ "aria-label": "Tamanho da fonte",
6144
+ children: [
6145
+ /* @__PURE__ */ jsx15("option", { value: "", children: "Tamanho\u2026" }),
6146
+ FONT_SIZES.map((s) => /* @__PURE__ */ jsx15("option", { value: s, children: s }, s))
6147
+ ]
6148
+ }
6149
+ ) }),
6115
6150
  /* @__PURE__ */ jsx15(Group, { children: ALIGN_OPTIONS.map((a) => /* @__PURE__ */ jsx15(
6116
6151
  "button",
6117
6152
  {