smartrte-react 0.3.5 → 1.0.0-beta.10

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.
Files changed (147) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +205 -14
  3. package/dist/adapters/domChecklistCommandBridge.js +1 -1
  4. package/dist/adapters/domCommandBridge.d.ts +2 -2
  5. package/dist/adapters/domCommandBridge.js +2 -2
  6. package/dist/adapters/domInlineAtomCommandBridge.d.ts +10 -9
  7. package/dist/adapters/domInlineAtomCommandBridge.js +154 -50
  8. package/dist/adapters/domInlineImageCommandBridge.d.ts +7 -7
  9. package/dist/adapters/domInlineImageCommandBridge.js +6 -72
  10. package/dist/adapters/domMoveCommandBridge.js +1 -1
  11. package/dist/adapters/domSelectionBridge.d.ts +3 -3
  12. package/dist/adapters/domSmartDocument.d.ts +4 -4
  13. package/dist/adapters/domSmartDocument.js +2 -2
  14. package/dist/adapters/domTableCommandBridge.d.ts +42 -16
  15. package/dist/adapters/domTableCommandBridge.js +203 -173
  16. package/dist/adapters/internalFlags.d.ts +0 -8
  17. package/dist/adapters/internalFlags.js +0 -24
  18. package/dist/adapters/legacyListShadowComparator.d.ts +1 -1
  19. package/dist/adapters/legacyListShadowComparator.js +1 -1
  20. package/dist/adapters/pdfPrint.d.ts +8 -0
  21. package/dist/adapters/pdfPrint.js +22 -0
  22. package/dist/adapters/shadowMode.d.ts +3 -3
  23. package/dist/adapters/shadowMode.js +1 -1
  24. package/dist/canonicalEditorRuntime.d.ts +65 -1
  25. package/dist/canonicalEditorRuntime.js +63 -5
  26. package/dist/capabilityPresets.d.ts +27 -0
  27. package/dist/capabilityPresets.js +21 -0
  28. package/dist/collabTransport.d.ts +66 -0
  29. package/dist/collabTransport.js +1 -0
  30. package/dist/commentProvider.d.ts +24 -0
  31. package/dist/commentProvider.js +1 -0
  32. package/dist/components/CanonicalAuthorityEditor.d.ts +56 -1
  33. package/dist/components/CanonicalAuthorityEditor.js +1304 -116
  34. package/dist/components/ClassicEditorAuthority.d.ts +93 -22
  35. package/dist/components/ClassicEditorAuthority.js +11 -43
  36. package/dist/components/ColorPickerPopover.d.ts +39 -0
  37. package/dist/components/ColorPickerPopover.js +287 -0
  38. package/dist/components/CommentMarkers.d.ts +25 -0
  39. package/dist/components/CommentMarkers.js +105 -0
  40. package/dist/components/CommentThreadPanel.d.ts +26 -0
  41. package/dist/components/CommentThreadPanel.js +47 -0
  42. package/dist/components/ContextMenu.d.ts +23 -0
  43. package/dist/components/ContextMenu.js +141 -0
  44. package/dist/components/FormulaLibraryPopover.d.ts +16 -0
  45. package/dist/components/FormulaLibraryPopover.js +107 -0
  46. package/dist/components/LinkEditorPopover.d.ts +3 -1
  47. package/dist/components/LinkEditorPopover.js +22 -6
  48. package/dist/components/MediaDetailsPopover.d.ts +35 -0
  49. package/dist/components/MediaDetailsPopover.js +136 -0
  50. package/dist/components/MediaManager.d.ts +4 -20
  51. package/dist/components/MediaManager.js +40 -11
  52. package/dist/components/MediaOverlay.d.ts +27 -0
  53. package/dist/components/MediaOverlay.js +228 -0
  54. package/dist/components/SpecialCharacterPopover.d.ts +18 -0
  55. package/dist/components/SpecialCharacterPopover.js +99 -0
  56. package/dist/components/StructuralSuggestionMarkers.d.ts +20 -0
  57. package/dist/components/StructuralSuggestionMarkers.js +56 -0
  58. package/dist/components/SuggestionPanel.d.ts +26 -0
  59. package/dist/components/SuggestionPanel.js +33 -0
  60. package/dist/components/TableBorderPopover.d.ts +59 -0
  61. package/dist/components/TableBorderPopover.js +149 -0
  62. package/dist/components/TableResizeHandles.d.ts +25 -0
  63. package/dist/components/TableResizeHandles.js +323 -0
  64. package/dist/components/TableSizePickerPopover.d.ts +16 -0
  65. package/dist/components/TableSizePickerPopover.js +126 -0
  66. package/dist/components/ToolbarPrimitives.d.ts +161 -0
  67. package/dist/components/ToolbarPrimitives.js +237 -0
  68. package/dist/components/VersionHistoryPanel.d.ts +9 -0
  69. package/dist/components/VersionHistoryPanel.js +157 -0
  70. package/dist/components/fixedPositioning.d.ts +25 -0
  71. package/dist/components/fixedPositioning.js +42 -0
  72. package/dist/formulaLibrary.d.ts +31 -0
  73. package/dist/formulaLibrary.js +90 -0
  74. package/dist/index.d.ts +25 -24
  75. package/dist/index.js +15 -11
  76. package/dist/mediaManagerAdapter.d.ts +12 -0
  77. package/dist/mediaManagerAdapter.js +34 -0
  78. package/dist/mediaProvider.d.ts +21 -1
  79. package/dist/specialCharacters.d.ts +22 -0
  80. package/dist/specialCharacters.js +86 -0
  81. package/dist/standalone/classic-editor-embed.js +19 -12
  82. package/dist/suggestionProvider.d.ts +17 -0
  83. package/dist/suggestionProvider.js +1 -0
  84. package/dist/theme.d.ts +1 -1
  85. package/dist/theme.js +219 -30
  86. package/dist/toolbarTools.d.ts +95 -0
  87. package/dist/toolbarTools.js +17 -0
  88. package/dist/versionProvider.d.ts +29 -0
  89. package/dist/versionProvider.js +1 -0
  90. package/package.json +28 -14
  91. package/dist/adapters/canonicalInlineCommandBridge.d.ts +0 -15
  92. package/dist/adapters/canonicalInlineCommandBridge.js +0 -269
  93. package/dist/adapters/canonicalListCommandBridge.d.ts +0 -26
  94. package/dist/adapters/canonicalListCommandBridge.js +0 -430
  95. package/dist/adapters/coreBold.d.ts +0 -5
  96. package/dist/adapters/coreBold.js +0 -3
  97. package/dist/adapters/documentFormats.d.ts +0 -26
  98. package/dist/adapters/documentFormats.js +0 -49
  99. package/dist/adapters/docxFormat.d.ts +0 -5
  100. package/dist/adapters/docxFormat.js +0 -272
  101. package/dist/adapters/domBlockCommandBridge.d.ts +0 -19
  102. package/dist/adapters/domBlockCommandBridge.js +0 -79
  103. package/dist/adapters/inlineMarkCoreExecution.d.ts +0 -9
  104. package/dist/adapters/inlineMarkCoreExecution.js +0 -25
  105. package/dist/adapters/pdfFormat.d.ts +0 -23
  106. package/dist/adapters/pdfFormat.js +0 -204
  107. package/dist/adapters/portableDocxAtoms.d.ts +0 -4
  108. package/dist/adapters/portableDocxAtoms.js +0 -58
  109. package/dist/adapters/styledDocxFormat.d.ts +0 -8
  110. package/dist/adapters/styledDocxFormat.js +0 -190
  111. package/dist/builtInFormatDefinitions.d.ts +0 -5
  112. package/dist/builtInFormatDefinitions.js +0 -82
  113. package/dist/canonicalAuthorityFlag.d.ts +0 -32
  114. package/dist/canonicalAuthorityFlag.js +0 -49
  115. package/dist/canonicalClipboardRuntime.d.ts +0 -15
  116. package/dist/canonicalClipboardRuntime.js +0 -76
  117. package/dist/components/ClassicEditor.d.ts +0 -76
  118. package/dist/components/ClassicEditor.js +0 -6729
  119. package/dist/editorController.d.ts +0 -78
  120. package/dist/editorController.js +0 -298
  121. package/dist/formatFidelity.d.ts +0 -14
  122. package/dist/formatFidelity.js +0 -107
  123. package/dist/formatRuntime.d.ts +0 -50
  124. package/dist/formatRuntime.js +0 -20
  125. package/dist/pluginRuntime.d.ts +0 -68
  126. package/dist/pluginRuntime.js +0 -78
  127. package/dist/standalone/editor.js +0 -1068
  128. package/dist/test-harness/atomShadowComparator.d.ts +0 -14
  129. package/dist/test-harness/atomShadowComparator.js +0 -63
  130. package/dist/test-harness/blockShadowComparator.d.ts +0 -30
  131. package/dist/test-harness/blockShadowComparator.js +0 -140
  132. package/dist/test-harness/clipboardShadowComparator.d.ts +0 -12
  133. package/dist/test-harness/clipboardShadowComparator.js +0 -46
  134. package/dist/test-harness/inlineShadowComparator.d.ts +0 -32
  135. package/dist/test-harness/inlineShadowComparator.js +0 -152
  136. package/dist/test-harness/legacyAtomEngine.d.ts +0 -10
  137. package/dist/test-harness/legacyAtomEngine.js +0 -15
  138. package/dist/test-harness/legacyBlockEngine.d.ts +0 -31
  139. package/dist/test-harness/legacyBlockEngine.js +0 -31
  140. package/dist/test-harness/legacyClipboardEngine.d.ts +0 -14
  141. package/dist/test-harness/legacyClipboardEngine.js +0 -67
  142. package/dist/test-harness/legacyInlineEngine.d.ts +0 -19
  143. package/dist/test-harness/legacyInlineEngine.js +0 -49
  144. package/dist/test-harness/legacyTableEngine.d.ts +0 -12
  145. package/dist/test-harness/legacyTableEngine.js +0 -25
  146. package/dist/test-harness/tableShadowComparator.d.ts +0 -29
  147. package/dist/test-harness/tableShadowComparator.js +0 -101
@@ -1,72 +1,6 @@
1
- import { createSmartEditor, getNodeAtPath, mediaPlugin, } from "smartrte-core";
2
- import { selectionFromDom } from "./domSelectionBridge.js";
3
- import { smartDocumentFromEditorRoot } from "./domSmartDocument.js";
4
- import { inlineAtomPathFromDom } from "./domInlineAtomCommandBridge.js";
5
- /**
6
- * Executes inline image insertion through core and projects only the resulting
7
- * atom into the existing DOM range. Unrelated DOM and metadata stay untouched.
8
- */
9
- export const executeDomInlineImageCommand = (root, input, domSelection = root.ownerDocument.defaultView?.getSelection() || null) => {
10
- if (!domSelection?.rangeCount || !domSelection.isCollapsed)
11
- return null;
12
- const modelSelection = selectionFromDom(root, domSelection);
13
- if (!modelSelection)
14
- return null;
15
- const { document } = smartDocumentFromEditorRoot(root);
16
- const editor = createSmartEditor({
17
- state: { document, selection: modelSelection },
18
- plugins: [mediaPlugin],
19
- });
20
- if (!editor.execute("image.insert-inline", input))
21
- return null;
22
- const selectionAfter = editor.state.selection;
23
- if (selectionAfter.type !== "text")
24
- return null;
25
- const imagePath = [
26
- ...selectionAfter.anchor.path.slice(0, -1),
27
- selectionAfter.anchor.path[selectionAfter.anchor.path.length - 1] - 1,
28
- ];
29
- const atom = getNodeAtPath(editor.state.document, imagePath);
30
- if (atom?.type !== "inlineImage")
31
- return null;
32
- const range = domSelection.getRangeAt(0);
33
- const image = root.ownerDocument.createElement("img");
34
- image.src = atom.src;
35
- image.alt = atom.alt || "";
36
- if (atom.title)
37
- image.title = atom.title;
38
- if (atom.width)
39
- image.width = atom.width;
40
- if (atom.height)
41
- image.height = atom.height;
42
- image.dataset.srteInline = "true";
43
- range.insertNode(image);
44
- range.setStartAfter(image);
45
- range.collapse(true);
46
- domSelection.removeAllRanges();
47
- domSelection.addRange(range);
48
- return image;
49
- };
50
- /** Validates an inline image attribute update through core, then mutates it in place. */
51
- export const executeDomInlineImageUpdate = (root, image, input) => {
52
- const path = inlineAtomPathFromDom(root, image);
53
- if (!path)
54
- return false;
55
- const selection = selectionFromDom(root, root.ownerDocument.defaultView?.getSelection() || null) || { type: "node", path };
56
- const { document } = smartDocumentFromEditorRoot(root);
57
- const editor = createSmartEditor({
58
- state: { document, selection },
59
- plugins: [mediaPlugin],
60
- });
61
- if (!editor.execute("image.update-inline", { path, ...input }))
62
- return false;
63
- if (input.alt !== undefined)
64
- image.alt = input.alt;
65
- if (input.title !== undefined)
66
- image.title = input.title;
67
- if (input.width !== undefined)
68
- image.width = input.width;
69
- if (input.height !== undefined)
70
- image.height = input.height;
71
- return true;
72
- };
1
+ import { executeDomCanonicalAtomInsert, executeDomCanonicalAtomUpdate } from "./domInlineAtomCommandBridge.js";
2
+ export const executeDomInlineImageCommand = (root, input, selection = root.ownerDocument.defaultView?.getSelection() || null) => executeDomCanonicalAtomInsert(root, {
3
+ type: "image",
4
+ attrs: { src: input.src, alt: input.alt ?? "", decorative: input.decorative === true, status: input.status || "ready", ...(input.uploadId ? { uploadId: input.uploadId } : {}), ...(input.error ? { error: input.error } : {}), ...(input.title ? { title: input.title } : {}), ...(input.width ? { width: input.width } : {}), ...(input.height ? { height: input.height } : {}) },
5
+ }, selection);
6
+ export const executeDomInlineImageUpdate = (root, image, input) => executeDomCanonicalAtomUpdate(root, image, input);
@@ -1,4 +1,4 @@
1
- import { createSmartEditor, movePlugin } from "smartrte-core";
1
+ import { createSmartEditor, movePlugin } from "smartrte-core/legacy";
2
2
  import { smartDocumentFromHtml } from "./domSmartDocument.js";
3
3
  /**
4
4
  * Executes model move/indent commands while preserving the existing DOM nodes.
@@ -1,8 +1,8 @@
1
- import type { Path, SmartSelection } from "smartrte-core";
1
+ import type { Path, LegacySmartSelection } from "smartrte-core/legacy";
2
2
  export declare const isEditorOnlyElement: (node: Element) => boolean;
3
3
  /** Returns the semantic model path for a DOM element at the adapter boundary. */
4
4
  export declare const pathForDomElement: (element: Element, editor: HTMLElement) => Path | null;
5
5
  /** Converts browser selection into a core selection without exposing editor UI nodes. */
6
- export declare const selectionFromDom: (editor: HTMLElement, selection: Selection | null) => SmartSelection | null;
6
+ export declare const selectionFromDom: (editor: HTMLElement, selection: Selection | null) => LegacySmartSelection | null;
7
7
  /** Restores a text selection from core paths after the editor DOM is rebuilt. */
8
- export declare const restoreSelectionToDom: (editor: HTMLElement, smartSelection: SmartSelection) => boolean;
8
+ export declare const restoreSelectionToDom: (editor: HTMLElement, smartSelection: LegacySmartSelection) => boolean;
@@ -1,10 +1,10 @@
1
- import { type SmartDocument } from "smartrte-core";
1
+ import { type LegacySmartDocument } from "smartrte-core/legacy";
2
2
  /** Removes editor UI before handing the document to the core parser boundary. */
3
3
  export declare const cleanEditorHtml: (root: HTMLElement) => string;
4
- export declare const smartDocumentFromHtml: (html: string, ownerDocument: Document) => SmartDocument;
4
+ export declare const smartDocumentFromHtml: (html: string, ownerDocument: Document) => LegacySmartDocument;
5
5
  export declare const smartDocumentFromEditorRoot: (root: HTMLElement) => {
6
- document: SmartDocument;
6
+ document: LegacySmartDocument;
7
7
  html: string;
8
8
  };
9
9
  /** Serializes the shadow-only model. It is not used for persisted editor HTML. */
10
- export declare const serializeSmartDocument: (document: SmartDocument) => string;
10
+ export declare const serializeSmartDocument: (document: LegacySmartDocument) => string;
@@ -1,5 +1,5 @@
1
- import { normalizeCompatibilityHtml, sanitizeLinkAttrs, } from "smartrte-core";
2
- import { isSmartListPreset } from "smartrte-core";
1
+ import { normalizeCompatibilityHtml, sanitizeLinkAttrs, } from "smartrte-core/legacy";
2
+ import { isSmartListPreset } from "smartrte-core/legacy";
3
3
  import { isEditorOnlyElement } from "./domSelectionBridge.js";
4
4
  const blockTags = new Set(["p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "blockquote", "pre", "table", "img", "video", "audio"]);
5
5
  const unwrap = (element) => {
@@ -1,35 +1,61 @@
1
- import { type MergeTableCellsInput, type SetTableCellStyleInput, type SetTableColumnWidthInput, type SetTableRowHeightInput, type TableHeaderInput, type SplitTableCellInput, type TableColumnInput, type TableRowInput, type ToggleTableCellBorderInput } from "smartrte-core";
1
+ type Point = {
2
+ row: number;
3
+ column: number;
4
+ };
2
5
  export type DomTableCommand = {
6
+ id: "table.remove";
7
+ input?: Record<string, never>;
8
+ } | {
3
9
  id: "table.row.add" | "table.row.remove";
4
- input: Omit<TableRowInput, "tablePath">;
10
+ input: {
11
+ index: number;
12
+ };
5
13
  } | {
6
14
  id: "table.column.add" | "table.column.remove";
7
- input: Omit<TableColumnInput, "tablePath">;
15
+ input: {
16
+ index: number;
17
+ };
8
18
  } | {
9
19
  id: "table.cell.merge";
10
- input: Omit<MergeTableCellsInput, "tablePath">;
20
+ input: {
21
+ start: Point;
22
+ end: Point;
23
+ };
11
24
  } | {
12
25
  id: "table.cell.split";
13
- input: Omit<SplitTableCellInput, "tablePath">;
26
+ input: Point;
14
27
  } | {
15
28
  id: "table.header.cell.toggle" | "table.header.row.toggle" | "table.header.column.toggle";
16
- input: Omit<TableHeaderInput, "tablePath">;
29
+ input: Point;
17
30
  } | {
18
31
  id: "table.cell.style.set";
19
- input: Omit<SetTableCellStyleInput, "tablePath">;
32
+ input: {
33
+ start: Point;
34
+ end?: Point;
35
+ backgroundColor?: string | null;
36
+ textColor?: string | null;
37
+ };
20
38
  } | {
21
39
  id: "table.column.width.set";
22
- input: Omit<SetTableColumnWidthInput, "tablePath">;
40
+ input: {
41
+ index: number;
42
+ widthPx: number;
43
+ };
23
44
  } | {
24
45
  id: "table.row.height.set";
25
- input: Omit<SetTableRowHeightInput, "tablePath">;
46
+ input: {
47
+ index: number;
48
+ heightPx: number;
49
+ };
26
50
  } | {
27
51
  id: "table.cell.border.toggle";
28
- input: Omit<ToggleTableCellBorderInput, "tablePath">;
52
+ input: {
53
+ start: Point;
54
+ end?: Point;
55
+ visibleBorder?: string;
56
+ };
29
57
  };
30
- /**
31
- * Executes a core table command while replacing only the affected table.
32
- * Visual table/cell attributes are transferred by logical grid position.
33
- */
34
- export declare const executeDomTableCommand: (table: HTMLTableElement, command: DomTableCommand) => HTMLTableElement | null;
35
- export declare const executeDomTableRemoval: (table: HTMLTableElement) => boolean;
58
+ export declare const executeDomTableCommand: (element: HTMLTableElement, command: DomTableCommand) => HTMLTableElement | null;
59
+ export declare const executeDomTableRemoval: (element: HTMLTableElement) => boolean;
60
+ export declare const executeDomTableInsert: (root: HTMLElement, afterBlockIndex: number, rows: number, columns: number, withHeader?: boolean) => HTMLTableElement | null;
61
+ export {};
@@ -1,201 +1,231 @@
1
- import { createSmartEditor, tablePlugin, } from "smartrte-core";
2
- import { serializeSmartDocument, smartDocumentFromHtml } from "./domSmartDocument.js";
3
- const cellsOf = (row) => Array.from(row.children).filter((node) => node instanceof HTMLTableCellElement);
4
- const cellGrid = (table) => {
5
- const rows = Array.from(table.rows);
6
- const grid = [];
7
- rows.forEach((row, rowIndex) => {
8
- grid[rowIndex] || (grid[rowIndex] = []);
1
+ import { applyOperations, createNodeId, createScopeIndex, foundationSchema, insertTableColumnCommand, insertTableCommand, insertTableRowCommand, mergeTableCellsCommand, occupancyGridFor, parseCanonicalTableHtml, removeTableColumnCommand, removeTableRowCommand, removeTableCommand, repair, serializeCanonicalTableHtml, setTableCellAttributesCommand, setTableColumnWidthCommand, setTableHeaderCommand, setTableRowHeightCommand, splitTableCellCommand, } from "smartrte-core/foundation";
2
+ const canonicalTable = (element) => {
3
+ const repaired = repair(parseCanonicalTableHtml(element.outerHTML)).doc;
4
+ const table = repaired.children[0];
5
+ return table && table.type === "table" ? { document: repaired, table: table } : null;
6
+ };
7
+ const tableScope = (table, start, end = start) => {
8
+ const grid = occupancyGridFor(table);
9
+ const top = Math.max(0, Math.min(start.row, end.row));
10
+ const bottom = Math.min(grid.rows - 1, Math.max(start.row, end.row));
11
+ const left = Math.max(0, Math.min(start.column, end.column));
12
+ const right = Math.min(grid.columns - 1, Math.max(start.column, end.column));
13
+ const exclusive = { top, left, bottom: bottom + 1, right: right + 1 };
14
+ return {
15
+ kind: "table-grid", tableId: table.id, rect: { top, left, bottom, right },
16
+ cellIds: grid.anchors.filter((cell) => cell.top <= bottom && cell.bottom > top && cell.left <= right && cell.right > left).map((cell) => cell.cellId),
17
+ coveredCellIds: grid.coveredIn(exclusive).map((cell) => cell.cellId),
18
+ rectangular: grid.isRectangular(exclusive), range: { from: { path: [0], offset: 0 }, to: { path: [0], offset: 1 } },
19
+ isolatingAncestorId: null, clamped: false,
20
+ };
21
+ };
22
+ const commandContext = (document) => ({ schema: foundationSchema, positions: createScopeIndex().positions(document, foundationSchema) });
23
+ const generated = (count, prefix) => Array.from({ length: count }, () => `${prefix}-${createNodeId()}`);
24
+ const domGrid = (table) => {
25
+ const slots = [];
26
+ Array.from(table.rows).forEach((row, rowIndex) => {
27
+ slots[rowIndex] || (slots[rowIndex] = []);
9
28
  let column = 0;
10
- cellsOf(row).forEach((cell) => {
11
- var _a;
12
- while (grid[rowIndex][column])
29
+ Array.from(row.cells).forEach((cell) => {
30
+ while (slots[rowIndex][column])
13
31
  column += 1;
14
- const rowspan = Math.max(1, cell.rowSpan || 1);
15
- const colspan = Math.max(1, cell.colSpan || 1);
16
- for (let rowOffset = 0; rowOffset < rowspan; rowOffset += 1) {
17
- grid[_a = rowIndex + rowOffset] || (grid[_a] = []);
18
- for (let columnOffset = 0; columnOffset < colspan; columnOffset += 1) {
19
- grid[rowIndex + rowOffset][column + columnOffset] = cell;
20
- }
32
+ for (let r = rowIndex; r < rowIndex + Math.max(1, cell.rowSpan); r += 1) {
33
+ slots[r] || (slots[r] = []);
34
+ for (let c = column; c < column + Math.max(1, cell.colSpan); c += 1)
35
+ slots[r][c] = cell;
21
36
  }
22
- column += colspan;
37
+ column += Math.max(1, cell.colSpan);
23
38
  });
24
39
  });
25
- return grid;
40
+ return slots;
26
41
  };
27
42
  const copyVisualAttributes = (from, to) => {
28
- const style = from.getAttribute("style");
29
- const className = from.getAttribute("class");
30
- if (style)
31
- to.setAttribute("style", style);
32
- if (className)
33
- to.setAttribute("class", className);
34
- };
35
- const reconcileHeaderStyle = (from, to) => {
36
- if (from.tagName === to.tagName)
37
- return;
38
- if (to.tagName === "TH") {
39
- to.style.fontWeight = "700";
40
- to.style.background = to.style.background || "#f3f4f6";
41
- to.style.textAlign = to.style.textAlign || "left";
42
- return;
43
- }
44
- if (to.style.fontWeight === "700" || to.style.fontWeight === "bold")
45
- to.style.fontWeight = "";
46
- if (to.style.background === "rgb(243, 244, 246)" || to.style.background === "#f3f4f6") {
47
- to.style.background = "";
48
- }
43
+ if (from.className)
44
+ to.className = from.className;
45
+ const retained = from.getAttribute("style");
46
+ if (retained)
47
+ for (const declaration of retained.split(";")) {
48
+ const separator = declaration.indexOf(":");
49
+ if (separator > 0)
50
+ to.style.setProperty(declaration.slice(0, separator).trim(), declaration.slice(separator + 1).trim());
51
+ }
49
52
  };
50
- /**
51
- * Executes a core table command while replacing only the affected table.
52
- * Visual table/cell attributes are transferred by logical grid position.
53
- */
54
- export const executeDomTableCommand = (table, command) => {
55
- const document = smartDocumentFromHtml(table.outerHTML, table.ownerDocument);
56
- if (document.children.length !== 1 || document.children[0].type !== "table")
57
- return null;
58
- const editor = createSmartEditor({
59
- state: { document, selection: { type: "node", path: [0] } },
60
- plugins: [tablePlugin],
61
- });
62
- if (!editor.execute(command.id, { ...command.input, tablePath: [0] }))
63
- return null;
64
- if (command.id === "table.column.width.set") {
65
- let group = table.querySelector(":scope > colgroup");
53
+ const projectAttributesInPlace = (element, after) => {
54
+ const table = after.children[0];
55
+ const modelGrid = occupancyGridFor(table);
56
+ const grid = domGrid(element);
57
+ const widths = Array.isArray(table.attrs?.columnWidths) ? table.attrs.columnWidths : [];
58
+ if (widths.length) {
59
+ let group = element.querySelector(":scope > colgroup");
66
60
  if (!group) {
67
- group = table.ownerDocument.createElement("colgroup");
68
- table.insertBefore(group, table.firstChild);
69
- }
70
- const logicalWidth = Math.max(...cellGrid(table).map((row) => row.length));
71
- while (group.children.length < logicalWidth) {
72
- group.appendChild(table.ownerDocument.createElement("col"));
73
- }
74
- const column = group.children[command.input.index];
75
- if (!column)
76
- return null;
77
- column.style.width = `${command.input.widthPx}px`;
78
- const seen = new Set();
79
- cellGrid(table).forEach((row) => {
80
- const target = row[command.input.index];
81
- if (!target || seen.has(target))
82
- return;
83
- seen.add(target);
84
- target.style.width = `${command.input.widthPx}px`;
85
- target.style.minWidth = `${command.input.widthPx}px`;
86
- target.style.maxWidth = `${command.input.widthPx}px`;
87
- });
88
- return table;
89
- }
90
- if (command.id === "table.row.height.set") {
91
- const row = table.rows[command.input.index];
92
- if (!row)
93
- return null;
94
- row.style.height = `${command.input.heightPx}px`;
95
- cellsOf(row).forEach((target) => {
96
- target.style.height = `${command.input.heightPx}px`;
97
- });
98
- return table;
99
- }
100
- if (command.id === "table.cell.style.set") {
101
- const end = command.input.end || command.input.start;
102
- const top = Math.min(command.input.start.row, end.row);
103
- const bottom = Math.max(command.input.start.row, end.row);
104
- const left = Math.min(command.input.start.column, end.column);
105
- const right = Math.max(command.input.start.column, end.column);
106
- const grid = cellGrid(table);
107
- const targets = new Set();
108
- for (let row = top; row <= bottom; row += 1) {
109
- for (let column = left; column <= right; column += 1) {
110
- const target = grid[row]?.[column];
111
- if (target)
112
- targets.add(target);
113
- }
61
+ group = element.ownerDocument.createElement("colgroup");
62
+ element.prepend(group);
114
63
  }
115
- targets.forEach((target) => {
116
- if (command.input.backgroundColor !== undefined) {
117
- target.style.backgroundColor = command.input.backgroundColor || "";
118
- }
119
- if (command.input.textColor !== undefined) {
120
- target.style.color = command.input.textColor || "";
121
- }
122
- });
123
- return table;
64
+ while (group.children.length < widths.length)
65
+ group.appendChild(element.ownerDocument.createElement("col"));
66
+ while (group.children.length > widths.length)
67
+ group.lastElementChild?.remove();
68
+ widths.forEach((width, index) => { group.children[index].style.width = `${width}px`; });
124
69
  }
125
- if (command.id === "table.cell.border.toggle") {
126
- const end = command.input.end || command.input.start;
127
- const top = Math.min(command.input.start.row, end.row);
128
- const bottom = Math.max(command.input.start.row, end.row);
129
- const left = Math.min(command.input.start.column, end.column);
130
- const right = Math.max(command.input.start.column, end.column);
131
- const grid = cellGrid(table);
132
- const targets = new Set();
133
- for (let row = top; row <= bottom; row += 1) {
134
- for (let column = left; column <= right; column += 1) {
135
- const target = grid[row]?.[column];
136
- if (target)
137
- targets.add(target);
138
- }
70
+ (table.children || []).forEach((row, rowIndex) => {
71
+ if (row.type !== "table_row")
72
+ return;
73
+ const height = Number(row.attrs?.height);
74
+ if (Number.isFinite(height) && height > 0)
75
+ element.rows[rowIndex].style.height = `${height}px`;
76
+ });
77
+ modelGrid.anchors.forEach((cell) => {
78
+ const target = grid[cell.top]?.[cell.left];
79
+ if (!target)
80
+ return;
81
+ target.style.background = cell.node.attrs?.background ? String(cell.node.attrs.background) : "";
82
+ target.style.color = cell.node.attrs?.textColor ? String(cell.node.attrs.textColor) : "";
83
+ target.style.border = cell.node.attrs?.borders ? String(cell.node.attrs.borders) : "";
84
+ target.style.verticalAlign = cell.node.attrs?.verticalAlign ? String(cell.node.attrs.verticalAlign) : "";
85
+ if (widths.length) {
86
+ const width = widths.slice(cell.left, cell.right).reduce((sum, value) => sum + value, 0);
87
+ target.style.width = `${width}px`;
88
+ target.style.minWidth = `${width}px`;
89
+ target.style.maxWidth = `${width}px`;
139
90
  }
140
- targets.forEach((target) => {
141
- target.style.border = target.style.border && target.style.border !== "none"
142
- ? "none"
143
- : command.input.visibleBorder || "1px solid #d1d5db";
144
- });
145
- return table;
146
- }
91
+ const rowHeight = Number(table.children?.[cell.top]?.attrs?.height);
92
+ if (Number.isFinite(rowHeight) && rowHeight > 0)
93
+ target.style.height = `${rowHeight}px`;
94
+ });
95
+ return element;
96
+ };
97
+ const replaceDomTable = (table, document) => {
147
98
  const container = table.ownerDocument.createElement("div");
148
- container.innerHTML = serializeSmartDocument(editor.state.document);
99
+ container.innerHTML = serializeCanonicalTableHtml(document, { fragment: true });
149
100
  const replacement = container.querySelector("table");
150
101
  if (!(replacement instanceof HTMLTableElement))
151
102
  return null;
152
103
  copyVisualAttributes(table, replacement);
153
- const previousGrid = cellGrid(table);
154
- const nextGrid = cellGrid(replacement);
155
- const previousPosition = (row, column) => {
156
- if (command.id === "table.row.add") {
157
- if (row === command.input.index)
158
- return null;
159
- return { row: row > command.input.index ? row - 1 : row, column };
160
- }
161
- if (command.id === "table.row.remove") {
162
- return { row: row >= command.input.index ? row + 1 : row, column };
163
- }
164
- if (command.id === "table.column.add") {
165
- if (column === command.input.index)
166
- return null;
167
- return { row, column: column > command.input.index ? column - 1 : column };
168
- }
169
- if (command.id === "table.column.remove") {
170
- return { row, column: column >= command.input.index ? column + 1 : column };
171
- }
172
- return { row, column };
173
- };
104
+ const beforeGrid = domGrid(table);
105
+ const afterGrid = domGrid(replacement);
174
106
  const copied = new Set();
175
- nextGrid.forEach((row, rowIndex) => row.forEach((nextCell, column) => {
176
- if (!nextCell || copied.has(nextCell))
107
+ afterGrid.forEach((row, rowIndex) => row.forEach((cell, column) => {
108
+ if (!cell || copied.has(cell))
177
109
  return;
178
- const position = previousPosition(rowIndex, column);
179
- const previousCell = position ? previousGrid[position.row]?.[position.column] : undefined;
180
- if (previousCell) {
181
- copyVisualAttributes(previousCell, nextCell);
182
- reconcileHeaderStyle(previousCell, nextCell);
110
+ const source = beforeGrid[rowIndex]?.[column];
111
+ if (source)
112
+ copyVisualAttributes(source, cell);
113
+ if (cell.tagName === "TH" && source?.tagName !== "TH") {
114
+ cell.style.fontWeight = "700";
115
+ cell.style.background = cell.style.background || "#f3f4f6";
116
+ cell.style.textAlign = cell.style.textAlign || "left";
117
+ }
118
+ else if (cell.tagName === "TD" && source?.tagName === "TH") {
119
+ if (["700", "bold"].includes(cell.style.fontWeight))
120
+ cell.style.fontWeight = "";
121
+ if (["rgb(243, 244, 246)", "#f3f4f6"].includes(cell.style.background))
122
+ cell.style.background = "";
123
+ if (cell.style.textAlign === "left")
124
+ cell.style.textAlign = "";
183
125
  }
184
- copied.add(nextCell);
126
+ copied.add(cell);
185
127
  }));
186
128
  table.replaceWith(replacement);
187
129
  return replacement;
188
130
  };
189
- export const executeDomTableRemoval = (table) => {
190
- const document = smartDocumentFromHtml(table.outerHTML, table.ownerDocument);
191
- if (document.children.length !== 1 || document.children[0].type !== "table")
131
+ export const executeDomTableCommand = (element, command) => {
132
+ if (command.id === "table.remove")
133
+ return null;
134
+ const parsed = canonicalTable(element);
135
+ if (!parsed)
136
+ return null;
137
+ const { document, table } = parsed;
138
+ const grid = occupancyGridFor(table);
139
+ const start = "start" in command.input ? command.input.start : "row" in command.input ? command.input : { row: "index" in command.input ? command.input.index : 0, column: 0 };
140
+ const end = "end" in command.input && command.input.end ? command.input.end : start;
141
+ const scope = tableScope(table, start, end);
142
+ const ctx = commandContext(document);
143
+ let operations = [];
144
+ if (command.id === "table.row.add")
145
+ operations = insertTableRowCommand(document, scope, {
146
+ rowIndex: command.input.index, rowId: `row-${createNodeId()}`,
147
+ cellIds: generated(grid.columns, "cell"), paragraphIds: generated(grid.columns, "paragraph"),
148
+ }, ctx);
149
+ else if (command.id === "table.row.remove")
150
+ operations = removeTableRowCommand(document, scope, { rowIndex: command.input.index }, ctx);
151
+ else if (command.id === "table.column.add")
152
+ operations = insertTableColumnCommand(document, scope, {
153
+ columnIndex: command.input.index, cellIds: generated(grid.rows, "cell"), paragraphIds: generated(grid.rows, "paragraph"),
154
+ }, ctx);
155
+ else if (command.id === "table.column.remove")
156
+ operations = removeTableColumnCommand(document, scope, { columnIndex: command.input.index }, ctx);
157
+ else if (command.id === "table.cell.merge")
158
+ operations = mergeTableCellsCommand(document, scope, {}, ctx);
159
+ else if (command.id === "table.cell.split") {
160
+ const target = grid.at(command.input.row, command.input.column);
161
+ const count = target ? (target.bottom - target.top) * (target.right - target.left) - 1 : 0;
162
+ operations = splitTableCellCommand(document, scope, { cellIds: generated(count, "cell"), paragraphIds: generated(count, "paragraph") }, ctx);
163
+ }
164
+ else if (command.id.startsWith("table.header.")) {
165
+ const point = command.input;
166
+ const targetCells = command.id === "table.header.cell.toggle" ? [grid.at(point.row, point.column)].filter(Boolean)
167
+ : command.id === "table.header.row.toggle" ? grid.anchors.filter((cell) => cell.top <= point.row && cell.bottom > point.row)
168
+ : grid.anchors.filter((cell) => cell.left <= point.column && cell.right > point.column);
169
+ const makeHeader = targetCells.some((cell) => cell?.node.attrs?.header !== true);
170
+ if (command.id === "table.header.cell.toggle" && point.row > 0 && point.column > 0)
171
+ return null;
172
+ const headerScope = { ...scope, cellIds: targetCells.map((cell) => cell.cellId) };
173
+ operations = command.id === "table.header.cell.toggle"
174
+ ? setTableCellAttributesCommand(document, headerScope, { attrs: { header: makeHeader } }, ctx)
175
+ : setTableHeaderCommand(document, scope, { target: makeHeader ? command.id === "table.header.row.toggle" ? "row" : "column" : "none" }, ctx);
176
+ }
177
+ else if (command.id === "table.cell.style.set")
178
+ operations = setTableCellAttributesCommand(document, scope, { attrs: {
179
+ ...(command.input.backgroundColor !== undefined ? { background: command.input.backgroundColor || undefined } : {}),
180
+ ...(command.input.textColor !== undefined ? { textColor: command.input.textColor || undefined } : {}),
181
+ } }, ctx);
182
+ else if (command.id === "table.cell.border.toggle") {
183
+ const visible = scope.cellIds.some((id) => grid.anchors.find((cell) => cell.cellId === id)?.node.attrs?.borders && grid.anchors.find((cell) => cell.cellId === id)?.node.attrs?.borders !== "none");
184
+ operations = setTableCellAttributesCommand(document, scope, { attrs: { borders: visible ? "none" : command.input.visibleBorder || "1px solid #d1d5db" } }, ctx);
185
+ }
186
+ else if (command.id === "table.column.width.set")
187
+ operations = setTableColumnWidthCommand(document, scope, { index: command.input.index, width: command.input.widthPx }, ctx);
188
+ else if (command.id === "table.row.height.set")
189
+ operations = setTableRowHeightCommand(document, scope, { index: command.input.index, height: command.input.heightPx }, ctx);
190
+ if (!operations.length)
191
+ return null;
192
+ const after = applyOperations(document, operations);
193
+ if (["table.cell.style.set", "table.cell.border.toggle", "table.column.width.set", "table.row.height.set"].includes(command.id)) {
194
+ return projectAttributesInPlace(element, after);
195
+ }
196
+ return replaceDomTable(element, after);
197
+ };
198
+ export const executeDomTableRemoval = (element) => {
199
+ const parsed = canonicalTable(element);
200
+ if (!parsed)
192
201
  return false;
193
- const editor = createSmartEditor({
194
- state: { document, selection: { type: "node", path: [0] } },
195
- plugins: [tablePlugin],
196
- });
197
- if (!editor.execute("table.remove", { tablePath: [0] }))
202
+ const scope = tableScope(parsed.table, { row: 0, column: 0 });
203
+ if (!removeTableCommand(parsed.document, scope, {}, commandContext(parsed.document)).length)
198
204
  return false;
199
- table.remove();
205
+ element.remove();
200
206
  return true;
201
207
  };
208
+ export const executeDomTableInsert = (root, afterBlockIndex, rows, columns, withHeader = false) => {
209
+ if (rows < 1 || columns < 1)
210
+ return null;
211
+ const document = repair(parseCanonicalTableHtml(root.innerHTML)).doc;
212
+ const index = Math.max(0, Math.min(afterBlockIndex, document.children.length - 1));
213
+ const target = document.children[index];
214
+ if (!target || !("id" in target))
215
+ return null;
216
+ const blockScope = {
217
+ kind: "block-range", blockIds: [target.id], promotedFromPartial: false,
218
+ commonParentId: document.id, range: { from: { path: [], offset: index }, to: { path: [], offset: index + 1 } },
219
+ isolatingAncestorId: null, clamped: false,
220
+ };
221
+ const total = rows * columns;
222
+ const tableId = `table-${createNodeId()}`;
223
+ const operations = insertTableCommand(document, blockScope, {
224
+ rows, columns, withHeader, placement: "after",
225
+ ids: { tableId, rowIds: generated(rows, "row"), cellIds: generated(total, "cell"), paragraphIds: generated(total, "paragraph") },
226
+ }, commandContext(document));
227
+ if (!operations.length)
228
+ return null;
229
+ root.innerHTML = serializeCanonicalTableHtml(applyOperations(document, operations), { fragment: true });
230
+ return root.querySelector(`table[data-smart-id="${tableId}"]`);
231
+ };
@@ -1,13 +1,5 @@
1
- import type { CoreInlineMark } from "./inlineMarkCoreExecution.js";
2
1
  export interface SmartRteInternalFlags {
3
- coreBold?: boolean;
4
- coreItalic?: boolean;
5
- coreUnderline?: boolean;
6
- coreSuperscript?: boolean;
7
- coreSubscript?: boolean;
8
- coreInlineMarks?: boolean;
9
2
  shadowMode?: boolean;
10
3
  }
11
4
  export declare const getSmartRteInternalFlags: () => SmartRteInternalFlags;
12
- export declare const isCoreInlineMarkFlagEnabled: (mark: CoreInlineMark) => boolean;
13
5
  export declare const isShadowModeFlagEnabled: () => boolean;