@sofereditor/react 0.3.2 → 0.5.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
@@ -1,5 +1,5 @@
1
1
  import { RefObject, JSX, ReactNode } from 'react';
2
- import { EditorDocument, EditorHistory, SerializedDocument, Selection, MarkName, MarkAttrs, BlockType, BlockAttrs, AlignValue, ListKind, PageSettings, TableLocation, TableRect, ImageEmbed, EmbedLoc, SerializedBlock, DeltaOp, Position } from '@sofereditor/core';
2
+ import { EditorDocument, EditorHistory, SerializedDocument, Selection, MarkName, MarkAttrs, BlockType, BlockAttrs, AlignValue, TableBorderPreset, ListKind, PageSettings, TableLocation, AnswerLineSpacing, TableRect, ImageEmbed, EmbedLoc, SerializedBlock, DeltaOp, Position } from '@sofereditor/core';
3
3
 
4
4
  /**
5
5
  * Subconjunto mínimo da Awareness do y-protocols que o overlay usa. Tipar assim
@@ -180,6 +180,14 @@ interface UseEditorResult {
180
180
  setCellBackground: (color: string | null) => void;
181
181
  /** Cor de fundo da célula focada; undefined fora de tabela ou sem cor. */
182
182
  getCellBackground: () => string | undefined;
183
+ /** Onde as linhas da grade da tabela focada aparecem. No-op fora de tabela. */
184
+ setTableBorderPreset: (preset: TableBorderPreset) => void;
185
+ /** Preset da tabela focada; "all" fora de tabela ou quando ausente. */
186
+ getTableBorderPreset: () => TableBorderPreset;
187
+ /** Cor das linhas da grade da tabela focada. `null` restaura o padrão. No-op fora de tabela. */
188
+ setTableBorderColor: (color: string | null) => void;
189
+ /** Cor da tabela focada; undefined fora de tabela ou quando usa o padrão. */
190
+ getTableBorderColor: () => string | undefined;
183
191
  isListActive: (kind: ListKind) => boolean;
184
192
  toggleList: (kind: ListKind) => void;
185
193
  indentList: () => void;
@@ -214,6 +222,8 @@ interface UseEditorResult {
214
222
  isInTable: () => boolean;
215
223
  /** Returns `{blockIndex, row, col}` for the focus position, or null when outside a table. */
216
224
  getTableLocation: () => TableLocation | null;
225
+ /** Insere `count` linhas de resposta pautadas depois do bloco focado. */
226
+ insertAnswerLines: (count: number, spacing: AnswerLineSpacing) => void;
217
227
  insertTable: (rows: number, cols: number) => void;
218
228
  /** Removes the table at `blockIndex`. Defaults to the current focus table. */
219
229
  deleteTable: (blockIndex?: number) => void;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RefObject, JSX, ReactNode } from 'react';
2
- import { EditorDocument, EditorHistory, SerializedDocument, Selection, MarkName, MarkAttrs, BlockType, BlockAttrs, AlignValue, ListKind, PageSettings, TableLocation, TableRect, ImageEmbed, EmbedLoc, SerializedBlock, DeltaOp, Position } from '@sofereditor/core';
2
+ import { EditorDocument, EditorHistory, SerializedDocument, Selection, MarkName, MarkAttrs, BlockType, BlockAttrs, AlignValue, TableBorderPreset, ListKind, PageSettings, TableLocation, AnswerLineSpacing, TableRect, ImageEmbed, EmbedLoc, SerializedBlock, DeltaOp, Position } from '@sofereditor/core';
3
3
 
4
4
  /**
5
5
  * Subconjunto mínimo da Awareness do y-protocols que o overlay usa. Tipar assim
@@ -180,6 +180,14 @@ interface UseEditorResult {
180
180
  setCellBackground: (color: string | null) => void;
181
181
  /** Cor de fundo da célula focada; undefined fora de tabela ou sem cor. */
182
182
  getCellBackground: () => string | undefined;
183
+ /** Onde as linhas da grade da tabela focada aparecem. No-op fora de tabela. */
184
+ setTableBorderPreset: (preset: TableBorderPreset) => void;
185
+ /** Preset da tabela focada; "all" fora de tabela ou quando ausente. */
186
+ getTableBorderPreset: () => TableBorderPreset;
187
+ /** Cor das linhas da grade da tabela focada. `null` restaura o padrão. No-op fora de tabela. */
188
+ setTableBorderColor: (color: string | null) => void;
189
+ /** Cor da tabela focada; undefined fora de tabela ou quando usa o padrão. */
190
+ getTableBorderColor: () => string | undefined;
183
191
  isListActive: (kind: ListKind) => boolean;
184
192
  toggleList: (kind: ListKind) => void;
185
193
  indentList: () => void;
@@ -214,6 +222,8 @@ interface UseEditorResult {
214
222
  isInTable: () => boolean;
215
223
  /** Returns `{blockIndex, row, col}` for the focus position, or null when outside a table. */
216
224
  getTableLocation: () => TableLocation | null;
225
+ /** Insere `count` linhas de resposta pautadas depois do bloco focado. */
226
+ insertAnswerLines: (count: number, spacing: AnswerLineSpacing) => void;
217
227
  insertTable: (rows: number, cols: number) => void;
218
228
  /** Removes the table at `blockIndex`. Defaults to the current focus table. */
219
229
  deleteTable: (blockIndex?: number) => void;
package/dist/index.js CHANGED
@@ -1708,11 +1708,19 @@ function RemoteCursorsOverlay({
1708
1708
 
1709
1709
  // src/NodeView.tsx
1710
1710
  import { useMemo as useMemo2, useRef as useRef7 } from "react";
1711
- import { tableRectSelection } from "@sofereditor/core";
1711
+ import {
1712
+ answerLineStyle,
1713
+ cellBorderStyle,
1714
+ tableRectSelection
1715
+ } from "@sofereditor/core";
1712
1716
 
1713
1717
  // src/renderInline.tsx
1714
1718
  import { Fragment as Fragment3, memo } from "react";
1715
- import { isImageEmbed } from "@sofereditor/core";
1719
+ import {
1720
+ BLANK_STYLE,
1721
+ isImageEmbed,
1722
+ splitUnderscoreRuns
1723
+ } from "@sofereditor/core";
1716
1724
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1717
1725
  function renderInline(delta, keyPrefix) {
1718
1726
  if (delta.length === 0 || delta.length === 1 && typeof delta[0].insert === "string" && delta[0].insert === "") {
@@ -1891,8 +1899,15 @@ function ImageEmbedViewImpl({
1891
1899
  }
1892
1900
  );
1893
1901
  }
1902
+ function decorateBlanks(text) {
1903
+ const segs = splitUnderscoreRuns(text);
1904
+ if (segs.length === 1 && !segs[0].blank) return text;
1905
+ return segs.map(
1906
+ (s, i) => s.blank ? /* @__PURE__ */ jsx9("span", { "data-blank": "true", style: BLANK_STYLE, children: s.text }, i) : /* @__PURE__ */ jsx9(Fragment3, { children: s.text }, i)
1907
+ );
1908
+ }
1894
1909
  function wrap(text, attrs) {
1895
- let node = text;
1910
+ let node = decorateBlanks(text);
1896
1911
  if (!attrs) return node;
1897
1912
  if (attrs.bold) node = /* @__PURE__ */ jsx9("strong", { children: node });
1898
1913
  if (attrs.italic) node = /* @__PURE__ */ jsx9("em", { children: node });
@@ -1900,6 +1915,7 @@ function wrap(text, attrs) {
1900
1915
  if (attrs.strike) node = /* @__PURE__ */ jsx9("s", { children: node });
1901
1916
  const styleParts = {};
1902
1917
  if (attrs.color) styleParts.color = attrs.color;
1918
+ if (attrs.highlight) styleParts.backgroundColor = attrs.highlight;
1903
1919
  if (attrs.fontFamily) styleParts.fontFamily = attrs.fontFamily;
1904
1920
  if (attrs.fontSize) styleParts.fontSize = attrs.fontSize;
1905
1921
  if (Object.keys(styleParts).length > 0) {
@@ -2152,6 +2168,7 @@ function NodeView({ block, index, children, fragment, tableFragment }) {
2152
2168
  function commonBlockProps(attrs, index, type, fragment) {
2153
2169
  const style = {};
2154
2170
  if (attrs.align) style.textAlign = attrs.align;
2171
+ Object.assign(style, answerLineStyle(attrs) ?? {});
2155
2172
  const base = {
2156
2173
  "data-block-index": index,
2157
2174
  "data-block-type": type,
@@ -2171,11 +2188,10 @@ function fragmentClass(f) {
2171
2188
  function alignClass(a) {
2172
2189
  return a ? ` ed-align-${a}` : "";
2173
2190
  }
2174
- function cellStyle(attrs) {
2175
- if (!attrs) return void 0;
2176
- const style = {};
2177
- if (attrs.align) style.textAlign = attrs.align;
2178
- if (attrs.bgColor) style.backgroundColor = attrs.bgColor;
2191
+ function cellStyle(attrs, border) {
2192
+ const style = { ...border };
2193
+ if (attrs?.align) style.textAlign = attrs.align;
2194
+ if (attrs?.bgColor) style.backgroundColor = attrs.bgColor;
2179
2195
  return Object.keys(style).length > 0 ? style : void 0;
2180
2196
  }
2181
2197
  function clampLevel(l) {
@@ -2231,7 +2247,17 @@ function TableView({ block, index, tableFragment }) {
2231
2247
  "data-cell-colspan": colspan,
2232
2248
  rowSpan: rowspan > 1 ? rowspan : void 0,
2233
2249
  colSpan: colspan > 1 ? colspan : void 0,
2234
- style: cellStyle(cell?.attrs),
2250
+ style: cellStyle(
2251
+ cell?.attrs,
2252
+ // rowStart/rowEnd são os limites do FRAGMENTO: numa tabela
2253
+ // quebrada entre páginas, cada página fecha a própria caixa.
2254
+ cellBorderStyle(
2255
+ block.attrs.borderPreset,
2256
+ { row: r, col: c, rowspan, colspan, cols, rowStart, rowEnd },
2257
+ "screen",
2258
+ block.attrs.borderColor
2259
+ )
2260
+ ),
2235
2261
  className: inRect ? "ed-cell ed-cell--selected" : "ed-cell",
2236
2262
  children: renderInline(delta, `t${index}-c${flat}`)
2237
2263
  },
@@ -2814,12 +2840,14 @@ import {
2814
2840
  mergeRight as cmdMergeRight,
2815
2841
  mergeSelection as cmdMergeSelection,
2816
2842
  moveEmbedAnchor as cmdMoveEmbedAnchor,
2843
+ setBlockAttrAtIndex as cmdSetBlockAttrAtIndex,
2817
2844
  setColumnWidth as cmdSetColumnWidth,
2818
2845
  setImageAttrs as cmdSetImageAttrs,
2819
2846
  splitCell as cmdSplitCell,
2820
2847
  tableRectSelection as tableRectSelection2,
2821
2848
  getMarksInRange,
2822
2849
  indentList as cmdIndentList,
2850
+ insertAnswerLines as cmdInsertAnswerLines,
2823
2851
  insertTable as cmdInsertTable,
2824
2852
  insertTableColumn as cmdInsertColumn,
2825
2853
  insertTableRow as cmdInsertRow,
@@ -3050,6 +3078,26 @@ function useEditor(opts = {}) {
3050
3078
  if (sel.focus.cellIndex == null || !doc.isTable(sel.focus.blockIndex)) return void 0;
3051
3079
  return doc.getCellAttrs(sel.focus.blockIndex, sel.focus.cellIndex).bgColor;
3052
3080
  }, [doc]);
3081
+ const setTableBorderPreset = useCallback6((preset) => {
3082
+ const sel = selectionRef.current;
3083
+ if (!doc.isTable(sel.focus.blockIndex)) return;
3084
+ cmdSetBlockAttrAtIndex(ctxRef.current, sel.focus.blockIndex, "borderPreset", preset);
3085
+ }, [doc]);
3086
+ const getTableBorderPreset = useCallback6(() => {
3087
+ const sel = selectionRef.current;
3088
+ if (!doc.isTable(sel.focus.blockIndex)) return "all";
3089
+ return doc.getBlockAttrs(sel.focus.blockIndex).borderPreset ?? "all";
3090
+ }, [doc]);
3091
+ const setTableBorderColor = useCallback6((color) => {
3092
+ const sel = selectionRef.current;
3093
+ if (!doc.isTable(sel.focus.blockIndex)) return;
3094
+ cmdSetBlockAttrAtIndex(ctxRef.current, sel.focus.blockIndex, "borderColor", color);
3095
+ }, [doc]);
3096
+ const getTableBorderColor = useCallback6(() => {
3097
+ const sel = selectionRef.current;
3098
+ if (!doc.isTable(sel.focus.blockIndex)) return void 0;
3099
+ return doc.getBlockAttrs(sel.focus.blockIndex).borderColor;
3100
+ }, [doc]);
3053
3101
  const isListActive = useCallback6(
3054
3102
  (kind) => {
3055
3103
  const sel = selectionRef.current;
@@ -3083,6 +3131,9 @@ function useEditor(opts = {}) {
3083
3131
  const insertTable = useCallback6((rows, cols) => {
3084
3132
  cmdInsertTable(ctxRef.current, rows, cols);
3085
3133
  }, []);
3134
+ const insertAnswerLines = useCallback6((count, spacing) => {
3135
+ cmdInsertAnswerLines(ctxRef.current, count, spacing);
3136
+ }, []);
3086
3137
  const deleteTable = useCallback6((blockIndex) => {
3087
3138
  const idx = blockIndex ?? selectionRef.current.focus.blockIndex;
3088
3139
  cmdDeleteTable(ctxRef.current, idx);
@@ -3291,6 +3342,10 @@ function useEditor(opts = {}) {
3291
3342
  getAlign,
3292
3343
  setCellBackground,
3293
3344
  getCellBackground,
3345
+ setTableBorderPreset,
3346
+ getTableBorderPreset,
3347
+ setTableBorderColor,
3348
+ getTableBorderColor,
3294
3349
  isListActive,
3295
3350
  toggleList,
3296
3351
  indentList,
@@ -3301,6 +3356,7 @@ function useEditor(opts = {}) {
3301
3356
  resolveLinkRequest,
3302
3357
  isInTable,
3303
3358
  getTableLocation,
3359
+ insertAnswerLines,
3304
3360
  insertTable,
3305
3361
  deleteTable,
3306
3362
  insertRowAbove,
@@ -4260,6 +4316,7 @@ import {
4260
4316
  useRef as useRef12,
4261
4317
  useState as useState11
4262
4318
  } from "react";
4319
+ import { ANSWER_LINE_MAX, TABLE_BORDER_COLOR } from "@sofereditor/core";
4263
4320
  import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
4264
4321
  var FONT_FAMILIES = ["Arial"];
4265
4322
  var FONT_SIZES = ["8pt", "10pt", "11pt", "12pt", "14pt", "16pt", "18pt", "24pt", "32pt", "48pt"];
@@ -4281,6 +4338,15 @@ var ALIGN_OPTIONS = [
4281
4338
  { value: "justify", label: "\u2630", title: "Justificar" }
4282
4339
  ];
4283
4340
  var IMAGE_ALIGN_VALUES = /* @__PURE__ */ new Set(["left", "center", "right"]);
4341
+ var NATIVE_CONTROL_TAGS = /* @__PURE__ */ new Set(["SELECT", "INPUT", "OPTION"]);
4342
+ function isNativeControl(t) {
4343
+ const el = t;
4344
+ return !!el && NATIVE_CONTROL_TAGS.has(el.tagName);
4345
+ }
4346
+ function keepsOwnMouseDown(t) {
4347
+ const el = t;
4348
+ return !!el && (NATIVE_CONTROL_TAGS.has(el.tagName) || el.tagName === "BUTTON");
4349
+ }
4284
4350
  var IMAGE_LAYOUT_OPTIONS = [
4285
4351
  { value: "inline", label: "\u2194", title: "Inline (no fluxo do texto)" },
4286
4352
  { value: "wrap-left", label: "\u25E7", title: "Texto contorna \xE0 direita" },
@@ -4313,10 +4379,7 @@ function Toolbar({ className }) {
4313
4379
  const blockAlign = getAlign();
4314
4380
  const blockDir = getBlockAttr("dir");
4315
4381
  const stop = (e) => {
4316
- const t = e.target;
4317
- if (t && (t.tagName === "SELECT" || t.tagName === "INPUT" || t.tagName === "OPTION")) {
4318
- return;
4319
- }
4382
+ if (isNativeControl(e.target)) return;
4320
4383
  e.preventDefault();
4321
4384
  };
4322
4385
  const onToggle = (name) => (e) => {
@@ -4326,6 +4389,9 @@ function Toolbar({ className }) {
4326
4389
  const onColorChange = (e) => {
4327
4390
  setMark("color", e.target.value);
4328
4391
  };
4392
+ const onHighlightChange = (e) => {
4393
+ setMark("highlight", e.target.value);
4394
+ };
4329
4395
  const onFontFamilyChange = (e) => {
4330
4396
  const v = e.target.value;
4331
4397
  if (v === "") removeMark("fontFamily");
@@ -4390,6 +4456,7 @@ function Toolbar({ className }) {
4390
4456
  [active.link, editor, removeMark, setMark]
4391
4457
  );
4392
4458
  const colorValue = typeof active.color === "string" ? active.color : "#000000";
4459
+ const highlightValue = typeof active.highlight === "string" ? active.highlight : "#fff176";
4393
4460
  const fontFamilyValue = typeof active.fontFamily === "string" ? active.fontFamily : "";
4394
4461
  const fontSizeValue = typeof active.fontSize === "string" ? active.fontSize : "";
4395
4462
  const blockValue = blockSelectValue(blockType, blockLevel);
@@ -4453,6 +4520,40 @@ function Toolbar({ className }) {
4453
4520
  },
4454
4521
  children: "\u232B"
4455
4522
  }
4523
+ ),
4524
+ /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", title: "Marca-texto", children: [
4525
+ /* @__PURE__ */ jsx13(
4526
+ "span",
4527
+ {
4528
+ className: "ed-toolbar-swatch ed-toolbar-swatch--highlight",
4529
+ "aria-hidden": true,
4530
+ style: { background: highlightValue }
4531
+ }
4532
+ ),
4533
+ /* @__PURE__ */ jsx13(
4534
+ "input",
4535
+ {
4536
+ type: "color",
4537
+ value: highlightValue,
4538
+ onChange: onHighlightChange,
4539
+ onMouseDown: stop,
4540
+ "aria-label": "Marca-texto"
4541
+ }
4542
+ )
4543
+ ] }),
4544
+ /* @__PURE__ */ jsx13(
4545
+ "button",
4546
+ {
4547
+ type: "button",
4548
+ className: "ed-toolbar-btn",
4549
+ title: "Remover marca-texto",
4550
+ onMouseDown: stop,
4551
+ onClick: (e) => {
4552
+ e.preventDefault();
4553
+ removeMark("highlight");
4554
+ },
4555
+ children: "\u232B"
4556
+ }
4456
4557
  )
4457
4558
  ] }),
4458
4559
  /* @__PURE__ */ jsxs12(Group, { children: [
@@ -4582,7 +4683,10 @@ function Toolbar({ className }) {
4582
4683
  children: "\u{1F517}"
4583
4684
  }
4584
4685
  ) }),
4585
- /* @__PURE__ */ jsx13(Group, { children: /* @__PURE__ */ jsx13(TableMenu, {}) }),
4686
+ /* @__PURE__ */ jsxs12(Group, { children: [
4687
+ /* @__PURE__ */ jsx13(TableMenu, {}),
4688
+ /* @__PURE__ */ jsx13(AnswerLinesMenu, {})
4689
+ ] }),
4586
4690
  /* @__PURE__ */ jsxs12(Group, { children: [
4587
4691
  /* @__PURE__ */ jsx13(
4588
4692
  "button",
@@ -4668,6 +4772,99 @@ function Toolbar({ className }) {
4668
4772
  }
4669
4773
  );
4670
4774
  }
4775
+ function AnswerLinesMenu() {
4776
+ const editor = useEditorContext();
4777
+ const [open, setOpen] = useState11(false);
4778
+ const [count, setCount] = useState11(5);
4779
+ const [spacing, setSpacing] = useState11(1.5);
4780
+ const rootRef = useRef12(null);
4781
+ useEffect10(() => {
4782
+ if (!open) return;
4783
+ const onDown = (e) => {
4784
+ if (!rootRef.current) return;
4785
+ if (rootRef.current.contains(e.target)) return;
4786
+ setOpen(false);
4787
+ };
4788
+ document.addEventListener("mousedown", onDown);
4789
+ return () => document.removeEventListener("mousedown", onDown);
4790
+ }, [open]);
4791
+ const clamped = Math.max(1, Math.min(ANSWER_LINE_MAX, Math.trunc(count) || 1));
4792
+ return /* @__PURE__ */ jsxs12("div", { ref: rootRef, className: "ed-toolbar-tablemenu", children: [
4793
+ /* @__PURE__ */ jsx13(
4794
+ "button",
4795
+ {
4796
+ type: "button",
4797
+ className: "ed-toolbar-btn",
4798
+ title: "Linhas de resposta",
4799
+ "aria-pressed": open,
4800
+ onMouseDown: (e) => e.preventDefault(),
4801
+ onClick: (e) => {
4802
+ e.preventDefault();
4803
+ setOpen((v) => !v);
4804
+ },
4805
+ children: "\u2630"
4806
+ }
4807
+ ),
4808
+ open && /* @__PURE__ */ jsxs12(
4809
+ "div",
4810
+ {
4811
+ className: "ed-table-popover ed-answerlines-popover",
4812
+ role: "menu",
4813
+ onMouseDown: (e) => {
4814
+ if (!keepsOwnMouseDown(e.target)) e.preventDefault();
4815
+ },
4816
+ children: [
4817
+ /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", children: [
4818
+ "Quantidade",
4819
+ /* @__PURE__ */ jsx13(
4820
+ "input",
4821
+ {
4822
+ type: "number",
4823
+ min: 1,
4824
+ max: ANSWER_LINE_MAX,
4825
+ value: count,
4826
+ onChange: (e) => setCount(Number(e.target.value)),
4827
+ "aria-label": "Quantidade de linhas"
4828
+ }
4829
+ )
4830
+ ] }),
4831
+ /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", children: [
4832
+ "Entrelinha",
4833
+ /* @__PURE__ */ jsxs12(
4834
+ "select",
4835
+ {
4836
+ value: String(spacing),
4837
+ onChange: (e) => setSpacing(Number(e.target.value)),
4838
+ "aria-label": "Entrelinha",
4839
+ children: [
4840
+ /* @__PURE__ */ jsx13("option", { value: "1", children: "Simples" }),
4841
+ /* @__PURE__ */ jsx13("option", { value: "1.5", children: "1,5" }),
4842
+ /* @__PURE__ */ jsx13("option", { value: "2", children: "Duplo" })
4843
+ ]
4844
+ }
4845
+ )
4846
+ ] }),
4847
+ /* @__PURE__ */ jsxs12(
4848
+ "button",
4849
+ {
4850
+ type: "button",
4851
+ onClick: () => {
4852
+ setOpen(false);
4853
+ editor.insertAnswerLines(clamped, spacing);
4854
+ },
4855
+ children: [
4856
+ "Inserir ",
4857
+ clamped,
4858
+ " linha",
4859
+ clamped > 1 ? "s" : ""
4860
+ ]
4861
+ }
4862
+ )
4863
+ ]
4864
+ }
4865
+ )
4866
+ ] });
4867
+ }
4671
4868
  function TableMenu() {
4672
4869
  const editor = useEditorContext();
4673
4870
  const inTable = editor.isInTable();
@@ -4705,10 +4902,7 @@ function TableMenu() {
4705
4902
  className: "ed-table-popover",
4706
4903
  role: "menu",
4707
4904
  onMouseDown: (e) => {
4708
- const t = e.target;
4709
- if (!t || t.tagName !== "INPUT" && t.tagName !== "BUTTON") {
4710
- e.preventDefault();
4711
- }
4905
+ if (!keepsOwnMouseDown(e.target)) e.preventDefault();
4712
4906
  },
4713
4907
  children: [
4714
4908
  /* @__PURE__ */ jsx13(
@@ -4796,6 +4990,56 @@ function TableMenu() {
4796
4990
  editor.setCellBackground(null);
4797
4991
  }, children: "Remover cor de fundo" }),
4798
4992
  /* @__PURE__ */ jsx13("hr", {}),
4993
+ /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label", children: [
4994
+ "Bordas",
4995
+ /* @__PURE__ */ jsxs12(
4996
+ "select",
4997
+ {
4998
+ value: editor.getTableBorderPreset(),
4999
+ onChange: (e) => editor.setTableBorderPreset(e.target.value),
5000
+ "aria-label": "Bordas da tabela",
5001
+ children: [
5002
+ /* @__PURE__ */ jsx13("option", { value: "all", children: "Todas" }),
5003
+ /* @__PURE__ */ jsx13("option", { value: "outer", children: "S\xF3 externas" }),
5004
+ /* @__PURE__ */ jsx13("option", { value: "horizontal", children: "S\xF3 horizontais" }),
5005
+ /* @__PURE__ */ jsx13("option", { value: "vertical", children: "S\xF3 verticais" }),
5006
+ /* @__PURE__ */ jsx13("option", { value: "none", children: "Nenhuma" })
5007
+ ]
5008
+ }
5009
+ )
5010
+ ] }),
5011
+ /* @__PURE__ */ jsxs12("label", { className: "ed-toolbar-label ed-table-bgcolor", children: [
5012
+ /* @__PURE__ */ jsx13(
5013
+ "span",
5014
+ {
5015
+ className: "ed-toolbar-swatch",
5016
+ "aria-hidden": true,
5017
+ style: { background: editor.getTableBorderColor() ?? TABLE_BORDER_COLOR }
5018
+ }
5019
+ ),
5020
+ "Cor da borda",
5021
+ /* @__PURE__ */ jsx13(
5022
+ "input",
5023
+ {
5024
+ type: "color",
5025
+ value: editor.getTableBorderColor() ?? TABLE_BORDER_COLOR,
5026
+ onChange: (e) => editor.setTableBorderColor(e.target.value),
5027
+ "aria-label": "Cor da borda"
5028
+ }
5029
+ )
5030
+ ] }),
5031
+ /* @__PURE__ */ jsx13(
5032
+ "button",
5033
+ {
5034
+ type: "button",
5035
+ onClick: () => {
5036
+ setOpen(false);
5037
+ editor.setTableBorderColor(null);
5038
+ },
5039
+ children: "Restaurar cor padr\xE3o"
5040
+ }
5041
+ ),
5042
+ /* @__PURE__ */ jsx13("hr", {}),
4799
5043
  /* @__PURE__ */ jsx13(
4800
5044
  "button",
4801
5045
  {