@harbour-enterprises/superdoc 0.22.0-next.8 → 0.22.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.
Files changed (38) hide show
  1. package/dist/chunks/{PdfViewer-C1tow4Hk.es.js → PdfViewer-BNWaI4WI.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-BBQm50V3.cjs → PdfViewer-DpkgwUPi.cjs} +1 -1
  3. package/dist/chunks/{index-BkpP-c9A.cjs → index-BbGPYtNy.cjs} +16 -9
  4. package/dist/chunks/{index-CJUwCOxd.es.js → index-DWKEKmiB.es.js} +16 -9
  5. package/dist/chunks/{super-editor.es-Bg0-F_x5.es.js → super-editor.es-BVxfhpAJ.es.js} +1562 -1267
  6. package/dist/chunks/{super-editor.es-D6u8E2n4.cjs → super-editor.es-BoUJEZaF.cjs} +1562 -1267
  7. package/dist/core/types/index.d.ts.map +1 -1
  8. package/dist/stores/comments-store.d.ts +4 -1
  9. package/dist/stores/comments-store.d.ts.map +1 -1
  10. package/dist/style.css +45 -44
  11. package/dist/super-editor/ai-writer.es.js +2 -2
  12. package/dist/super-editor/chunks/{converter-BgedUNCW.js → converter-C-yWLpFM.js} +150 -105
  13. package/dist/super-editor/chunks/{docx-zipper-ByLK3trM.js → docx-zipper-CmGlSUQM.js} +1 -1
  14. package/dist/super-editor/chunks/{editor-M2L6bDyC.js → editor-BBnC1DzI.js} +1532 -1280
  15. package/dist/super-editor/chunks/{toolbar-DsPK5x8a.js → toolbar-QJANo61B.js} +2 -2
  16. package/dist/super-editor/converter.es.js +1 -1
  17. package/dist/super-editor/docx-zipper.es.js +2 -2
  18. package/dist/super-editor/editor.es.js +3 -3
  19. package/dist/super-editor/file-zipper.es.js +1 -1
  20. package/dist/super-editor/src/core/helpers/generateDocxRandomId.d.ts +5 -0
  21. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  22. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  23. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  24. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +6 -0
  25. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +6 -0
  26. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +6 -0
  27. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +7 -0
  28. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  29. package/dist/super-editor/style.css +1 -0
  30. package/dist/super-editor/super-editor.es.js +7 -7
  31. package/dist/super-editor/toolbar.es.js +2 -2
  32. package/dist/super-editor.cjs +1 -1
  33. package/dist/super-editor.es.js +1 -1
  34. package/dist/superdoc.cjs +2 -2
  35. package/dist/superdoc.es.js +2 -2
  36. package/dist/superdoc.umd.js +1573 -1271
  37. package/dist/superdoc.umd.js.map +1 -1
  38. package/package.json +1 -1
@@ -15005,6 +15005,10 @@ function generateDocxRandomId(length2 = 8) {
15005
15005
  }
15006
15006
  return id.join("");
15007
15007
  }
15008
+ function generateRandomSigned32BitIntStrId() {
15009
+ const val = Math.floor(Math.random() * 2147483647);
15010
+ return val.toString();
15011
+ }
15008
15012
  function generateRandom32BitHex() {
15009
15013
  const val = Math.floor(Math.random() * 2147483647);
15010
15014
  return val.toString(16).toUpperCase().padStart(8, "0");
@@ -22583,6 +22587,7 @@ const helpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
22583
22587
  generateDocxListAttributes,
22584
22588
  generateDocxRandomId,
22585
22589
  generateRandom32BitHex,
22590
+ generateRandomSigned32BitIntStrId,
22586
22591
  getActiveFormatting,
22587
22592
  getExtensionConfigField,
22588
22593
  getMarkRange,
@@ -28177,6 +28182,9 @@ function handleStructuredContentNode(params2) {
28177
28182
  const node = nodes[0];
28178
28183
  const sdtPr = node.elements.find((el) => el.name === "w:sdtPr");
28179
28184
  const sdtContent = node.elements.find((el) => el.name === "w:sdtContent");
28185
+ const id = sdtPr?.elements?.find((el) => el.name === "w:id");
28186
+ const tag = sdtPr?.elements?.find((el) => el.name === "w:tag");
28187
+ const alias = sdtPr?.elements?.find((el) => el.name === "w:alias");
28180
28188
  if (!sdtContent) {
28181
28189
  return null;
28182
28190
  }
@@ -28188,15 +28196,16 @@ function handleStructuredContentNode(params2) {
28188
28196
  nodes: sdtContent.elements,
28189
28197
  path: [...params2.path || [], sdtContent]
28190
28198
  });
28191
- let sdtContentType = "structuredContent";
28192
- if (paragraph || table) {
28193
- sdtContentType = "structuredContentBlock";
28194
- }
28199
+ const isBlockNode2 = paragraph || table;
28200
+ const sdtContentType = isBlockNode2 ? "structuredContentBlock" : "structuredContent";
28195
28201
  let result = {
28196
28202
  type: sdtContentType,
28197
28203
  content: translatedContent,
28198
28204
  marks,
28199
28205
  attrs: {
28206
+ id: id?.attributes?.["w:val"] || null,
28207
+ tag: tag?.attributes?.["w:val"] || null,
28208
+ alias: alias?.attributes?.["w:val"] || null,
28200
28209
  sdtPr
28201
28210
  }
28202
28211
  };
@@ -30454,21 +30463,55 @@ const generateSdtPrTagForDocumentSection = (id, title, tag) => {
30454
30463
  };
30455
30464
  function translateStructuredContent(params2) {
30456
30465
  const { node } = params2;
30457
- const { attrs = {} } = node;
30458
30466
  const childContent = translateChildNodes({ ...params2, nodes: node.content });
30459
- const nodeElements = [
30460
- {
30461
- name: "w:sdtContent",
30462
- elements: childContent
30463
- }
30464
- ];
30465
- nodeElements.unshift(attrs.sdtPr);
30467
+ const sdtContent = { name: "w:sdtContent", elements: childContent };
30468
+ const sdtPr = generateSdtPrTagForStructuredContent({ node });
30469
+ const nodeElements = [sdtPr, sdtContent];
30466
30470
  const result = {
30467
30471
  name: "w:sdt",
30468
30472
  elements: nodeElements
30469
30473
  };
30470
30474
  return result;
30471
30475
  }
30476
+ function generateSdtPrTagForStructuredContent({ node }) {
30477
+ const { attrs = {} } = node;
30478
+ const id = {
30479
+ name: "w:id",
30480
+ type: "element",
30481
+ attributes: { "w:val": attrs.id }
30482
+ };
30483
+ const alias = {
30484
+ name: "w:alias",
30485
+ type: "element",
30486
+ attributes: { "w:val": attrs.alias }
30487
+ };
30488
+ const tag = {
30489
+ name: "w:tag",
30490
+ type: "element",
30491
+ attributes: { "w:val": attrs.tag }
30492
+ };
30493
+ const resultElements = [];
30494
+ if (attrs.id) resultElements.push(id);
30495
+ if (attrs.alias) resultElements.push(alias);
30496
+ if (attrs.tag) resultElements.push(tag);
30497
+ if (attrs.sdtPr) {
30498
+ const elements = attrs.sdtPr.elements || [];
30499
+ const elementsToExclude = ["w:id", "w:alias", "w:tag"];
30500
+ const restElements = elements.filter((el) => !elementsToExclude.includes(el.name));
30501
+ const result2 = {
30502
+ name: "w:sdtPr",
30503
+ type: "element",
30504
+ elements: [...resultElements, ...restElements]
30505
+ };
30506
+ return result2;
30507
+ }
30508
+ const result = {
30509
+ name: "w:sdtPr",
30510
+ type: "element",
30511
+ elements: resultElements
30512
+ };
30513
+ return result;
30514
+ }
30472
30515
  const XML_NODE_NAME$3 = "w:sdt";
30473
30516
  const SD_NODE_NAME$3 = ["fieldAnnotation", "structuredContent", "structuredContentBlock", "documentSection"];
30474
30517
  const validXmlAttributes$3 = [];
@@ -31529,7 +31572,7 @@ function translateShapeContainer(params2) {
31529
31572
  const pict = {
31530
31573
  name: "w:pict",
31531
31574
  attributes: {
31532
- "w14:anchorId": Math.floor(Math.random() * 4294967295).toString()
31575
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
31533
31576
  },
31534
31577
  elements: [shape]
31535
31578
  };
@@ -31596,7 +31639,7 @@ function translateVRectContentBlock(params2) {
31596
31639
  const pict = {
31597
31640
  name: "w:pict",
31598
31641
  attributes: {
31599
- "w14:anchorId": Math.floor(Math.random() * 4294967295).toString()
31642
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
31600
31643
  },
31601
31644
  elements: [rect]
31602
31645
  };
@@ -33096,7 +33139,7 @@ const DEFAULT_SECTION_PROPS = Object.freeze({
33096
33139
  gutter: "0"
33097
33140
  })
33098
33141
  });
33099
- function ensureSectionProperties(bodyNode, converter) {
33142
+ function ensureSectionProperties(bodyNode) {
33100
33143
  if (!bodyNode.elements) bodyNode.elements = [];
33101
33144
  let sectPr = bodyNode.elements.find((el) => el.name === "w:sectPr");
33102
33145
  if (!sectPr) {
@@ -36595,7 +36638,7 @@ var __privateGet$1 = (obj, member, getter) => (__accessCheck$1(obj, member, "rea
36595
36638
  var __privateAdd$1 = (obj, member, value) => member.has(obj) ? __typeError$1("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
36596
36639
  var __privateSet = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
36597
36640
  var __privateMethod$1 = (obj, member, method) => (__accessCheck$1(obj, member, "access private method"), method);
36598
- var _Attribute_static, getGlobalAttributes_fn, getNodeAndMarksAttributes_fn, _Schema_static, createNodesSchema_fn, createMarksSchema_fn, _events, _ExtensionService_instances, setupExtensions_fn, attachEditorEvents_fn, _editor, _stateValidators, _xmlValidators, _requiredNodeTypes, _requiredMarkTypes, _SuperValidator_instances, initializeValidators_fn, collectValidatorRequirements_fn, analyzeDocument_fn, _commandService, _Editor_instances, initContainerElement_fn, init_fn, initRichText_fn, onFocus_fn, checkHeadless_fn, registerCopyHandler_fn, insertNewFileData_fn, registerPluginByNameIfNotExists_fn, createExtensionService_fn, createCommandService_fn, createConverter_fn, initMedia_fn, initFonts_fn, createSchema_fn, generatePmData_fn, createView_fn, onCollaborationReady_fn, initComments_fn, initPagination_fn, dispatchTransaction_fn, handleNodeSelection_fn, prepareDocumentForImport_fn, prepareDocumentForExport_fn, endCollaboration_fn, validateDocumentInit_fn, validateDocumentExport_fn, initDevTools_fn, _ListItemNodeView_instances, init_fn2, _FieldAnnotationView_instances, createAnnotation_fn, _AutoPageNumberNodeView_instances, renderDom_fn, scheduleUpdateNodeStyle_fn, _DocumentSectionView_instances, init_fn3, addToolTip_fn;
36641
+ var _Attribute_static, getGlobalAttributes_fn, getNodeAndMarksAttributes_fn, _Schema_static, createNodesSchema_fn, createMarksSchema_fn, _events, _ExtensionService_instances, setupExtensions_fn, attachEditorEvents_fn, _editor, _stateValidators, _xmlValidators, _requiredNodeTypes, _requiredMarkTypes, _SuperValidator_instances, initializeValidators_fn, collectValidatorRequirements_fn, analyzeDocument_fn, _commandService, _Editor_instances, initContainerElement_fn, init_fn, initRichText_fn, onFocus_fn, checkHeadless_fn, registerCopyHandler_fn, insertNewFileData_fn, registerPluginByNameIfNotExists_fn, createExtensionService_fn, createCommandService_fn, createConverter_fn, initMedia_fn, initFonts_fn, createSchema_fn, generatePmData_fn, createView_fn, onCollaborationReady_fn, initComments_fn, initPagination_fn, dispatchTransaction_fn, handleNodeSelection_fn, prepareDocumentForImport_fn, prepareDocumentForExport_fn, endCollaboration_fn, validateDocumentInit_fn, validateDocumentExport_fn, initDevTools_fn, _DocumentSectionView_instances, init_fn2, addToolTip_fn, _ListItemNodeView_instances, init_fn3, _FieldAnnotationView_instances, createAnnotation_fn, _AutoPageNumberNodeView_instances, renderDom_fn, scheduleUpdateNodeStyle_fn;
36599
36642
  var GOOD_LEAF_SIZE = 200;
36600
36643
  var RopeSequence = function RopeSequence2() {
36601
36644
  };
@@ -48670,28 +48713,25 @@ const handleTrackedChangeTransaction = (trackedChangeMeta, trackedChanges, newEd
48670
48713
  if (emitParams) editor.emit("commentsUpdate", emitParams);
48671
48714
  return newTrackedChanges;
48672
48715
  };
48673
- const getTrackedChangeText = ({ state: state2, node, mark, marks, trackedChangeType, isDeletionInsertion }) => {
48716
+ const getTrackedChangeText = ({ nodes, mark, trackedChangeType, isDeletionInsertion }) => {
48674
48717
  let trackedChangeText = "";
48675
48718
  let deletionText = "";
48676
48719
  if (trackedChangeType === TrackInsertMarkName) {
48677
- trackedChangeText = node?.text ?? "";
48720
+ trackedChangeText = nodes.reduce((acc, node) => {
48721
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === mark.type.name)) return acc;
48722
+ acc += node?.text || node?.textContent || "";
48723
+ return acc;
48724
+ }, "");
48678
48725
  }
48679
48726
  if (trackedChangeType === TrackFormatMarkName) {
48680
48727
  trackedChangeText = translateFormatChangesToEnglish(mark.attrs);
48681
48728
  }
48682
48729
  if (trackedChangeType === TrackDeleteMarkName || isDeletionInsertion) {
48683
- deletionText = node?.text ?? "";
48684
- if (isDeletionInsertion) {
48685
- let { id } = marks.deletionMark.attrs;
48686
- let deletionNode = findNode$1(state2.doc, (node2) => {
48687
- const { marks: marks2 = [] } = node2;
48688
- const changeMarks = marks2.filter((mark2) => TRACK_CHANGE_MARKS.includes(mark2.type.name));
48689
- if (!changeMarks.length) return false;
48690
- const hasMatchingId = changeMarks.find((mark2) => mark2.attrs.id === id);
48691
- if (hasMatchingId) return true;
48692
- });
48693
- deletionText = deletionNode?.node.text ?? "";
48694
- }
48730
+ deletionText = nodes.reduce((acc, node) => {
48731
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === TrackDeleteMarkName)) return acc;
48732
+ acc += node?.text || node?.textContent || "";
48733
+ return acc;
48734
+ }, "");
48695
48735
  }
48696
48736
  return {
48697
48737
  deletionText,
@@ -48706,20 +48746,17 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
48706
48746
  const id = attrs.id;
48707
48747
  const node = nodes[0];
48708
48748
  const isDeletionInsertion = !!(marks.insertedMark && marks.deletionMark);
48709
- let existingNode;
48749
+ let nodesWithMark = [];
48710
48750
  newEditorState.doc.descendants((node2) => {
48711
48751
  const { marks: marks2 = [] } = node2;
48712
48752
  const changeMarks = marks2.filter((mark) => TRACK_CHANGE_MARKS.includes(mark.type.name));
48713
48753
  if (!changeMarks.length) return;
48714
48754
  const hasMatchingId = changeMarks.find((mark) => mark.attrs.id === id);
48715
- if (hasMatchingId) existingNode = node2;
48716
- if (existingNode) return false;
48755
+ if (hasMatchingId) nodesWithMark.push(node2);
48717
48756
  });
48718
48757
  const { deletionText, trackedChangeText } = getTrackedChangeText({
48719
- state: newEditorState,
48720
- node: existingNode || node,
48758
+ nodes: nodesWithMark.length ? nodesWithMark : [node],
48721
48759
  mark: trackedMark,
48722
- marks,
48723
48760
  trackedChangeType,
48724
48761
  isDeletionInsertion
48725
48762
  });
@@ -48747,14 +48784,6 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
48747
48784
  else if (event === "update") params2.event = comments_module_events.UPDATE;
48748
48785
  return params2;
48749
48786
  };
48750
- function findNode$1(node, predicate) {
48751
- let found2 = null;
48752
- node.descendants((node2, pos) => {
48753
- if (predicate(node2)) found2 = { node: node2, pos };
48754
- if (found2) return false;
48755
- });
48756
- return found2;
48757
- }
48758
48787
  function findRangeById(doc2, id) {
48759
48788
  let from2 = null, to = null;
48760
48789
  doc2.descendants((node, pos) => {
@@ -53382,196 +53411,1191 @@ const SlashMenu = Extension.create({
53382
53411
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
53383
53412
  }
53384
53413
  });
53385
- const Document = Node$1.create({
53386
- name: "doc",
53387
- topNode: true,
53388
- content: "block+",
53389
- parseDOM() {
53390
- return [{ tag: "doc" }];
53391
- },
53392
- renderDOM() {
53393
- return ["doc", 0];
53394
- },
53395
- addAttributes() {
53396
- return {
53397
- attributes: {
53398
- rendered: false,
53399
- "aria-label": "Document node"
53400
- }
53401
- };
53402
- },
53403
- addCommands() {
53404
- return {
53405
- /**
53406
- * Get document statistics
53407
- * @category Command
53408
- * @example
53409
- * // Get word and character count
53410
- * const stats = editor.commands.getDocumentStats()
53411
- * console.log(`${stats.words} words, ${stats.characters} characters`)
53412
- * @note Returns word count, character count, and paragraph count
53413
- */
53414
- getDocumentStats: () => ({ editor }) => {
53415
- const text = editor.getText();
53416
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
53417
- const characters = text.length;
53418
- const paragraphs = editor.state.doc.content.childCount;
53419
- return {
53420
- words,
53421
- characters,
53422
- paragraphs
53423
- };
53424
- },
53425
- /**
53426
- * Clear entire document
53427
- * @category Command
53428
- * @example
53429
- * editor.commands.clearDocument()
53430
- * @note Replaces all content with an empty paragraph
53431
- */
53432
- clearDocument: () => ({ commands: commands2 }) => {
53433
- return commands2.setContent("<p></p>");
53414
+ class StructuredContentViewBase {
53415
+ constructor(props) {
53416
+ __publicField$1(this, "node");
53417
+ __publicField$1(this, "view");
53418
+ __publicField$1(this, "getPos");
53419
+ __publicField$1(this, "decorations");
53420
+ __publicField$1(this, "innerDecorations");
53421
+ __publicField$1(this, "editor");
53422
+ __publicField$1(this, "extension");
53423
+ __publicField$1(this, "htmlAttributes");
53424
+ __publicField$1(this, "root");
53425
+ __publicField$1(this, "isDragging", false);
53426
+ this.node = props.node;
53427
+ this.view = props.editor.view;
53428
+ this.getPos = props.getPos;
53429
+ this.decorations = props.decorations;
53430
+ this.innerDecorations = props.innerDecorations;
53431
+ this.editor = props.editor;
53432
+ this.extension = props.extension;
53433
+ this.htmlAttributes = props.htmlAttributes;
53434
+ this.mount(props);
53435
+ }
53436
+ mount() {
53437
+ return;
53438
+ }
53439
+ get dom() {
53440
+ return this.root;
53441
+ }
53442
+ get contentDOM() {
53443
+ return null;
53444
+ }
53445
+ update(node, decorations, innerDecorations) {
53446
+ if (node.type !== this.node.type) {
53447
+ return false;
53448
+ }
53449
+ this.node = node;
53450
+ this.decorations = decorations;
53451
+ this.innerDecorations = innerDecorations;
53452
+ this.updateHTMLAttributes();
53453
+ return true;
53454
+ }
53455
+ stopEvent(event) {
53456
+ if (!this.dom) return false;
53457
+ const target = event.target;
53458
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
53459
+ if (!isInElement) return false;
53460
+ const isDragEvent = event.type.startsWith("drag");
53461
+ const isDropEvent = event.type === "drop";
53462
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
53463
+ if (isInput && !isDropEvent && !isDragEvent) return true;
53464
+ const { isEditable } = this.editor;
53465
+ const { isDragging } = this;
53466
+ const isDraggable = !!this.node.type.spec.draggable;
53467
+ const isSelectable = NodeSelection.isSelectable(this.node);
53468
+ const isCopyEvent = event.type === "copy";
53469
+ const isPasteEvent = event.type === "paste";
53470
+ const isCutEvent = event.type === "cut";
53471
+ const isClickEvent = event.type === "mousedown";
53472
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
53473
+ event.preventDefault();
53474
+ }
53475
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
53476
+ event.preventDefault();
53477
+ return false;
53478
+ }
53479
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
53480
+ const dragHandle = target.closest("[data-drag-handle]");
53481
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
53482
+ if (isValidDragHandle) {
53483
+ this.isDragging = true;
53484
+ document.addEventListener(
53485
+ "dragend",
53486
+ () => {
53487
+ this.isDragging = false;
53488
+ },
53489
+ { once: true }
53490
+ );
53491
+ document.addEventListener(
53492
+ "drop",
53493
+ () => {
53494
+ this.isDragging = false;
53495
+ },
53496
+ { once: true }
53497
+ );
53498
+ document.addEventListener(
53499
+ "mouseup",
53500
+ () => {
53501
+ this.isDragging = false;
53502
+ },
53503
+ { once: true }
53504
+ );
53434
53505
  }
53435
- };
53506
+ }
53507
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
53508
+ return false;
53509
+ }
53510
+ return true;
53436
53511
  }
53437
- });
53438
- const Text = Node$1.create({
53439
- name: "text",
53440
- group: "inline",
53441
- inline: true,
53442
- addOptions() {
53443
- return {};
53512
+ ignoreMutation(mutation) {
53513
+ if (!this.dom || !this.contentDOM) return true;
53514
+ if (this.node.isLeaf || this.node.isAtom) return true;
53515
+ if (mutation.type === "selection") return false;
53516
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
53517
+ if (this.contentDOM.contains(mutation.target)) return false;
53518
+ return true;
53444
53519
  }
53445
- });
53446
- const splitRun = () => (props) => {
53447
- const { state: state2, view, tr } = props;
53448
- const { $from, empty: empty2 } = state2.selection;
53449
- if (!empty2) return false;
53450
- if ($from.parent.type.name !== "run") return false;
53451
- const handled = splitBlock(state2, (transaction) => {
53520
+ destroy() {
53521
+ this.dom.remove();
53522
+ this.contentDOM?.remove();
53523
+ }
53524
+ updateAttributes(attrs) {
53525
+ const pos = this.getPos();
53526
+ if (typeof pos !== "number") {
53527
+ return;
53528
+ }
53529
+ return this.view.dispatch(
53530
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
53531
+ ...this.node.attrs,
53532
+ ...attrs
53533
+ })
53534
+ );
53535
+ }
53536
+ updateHTMLAttributes() {
53537
+ const { extensionService } = this.editor;
53538
+ const { attributes } = extensionService;
53539
+ const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
53540
+ this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
53541
+ }
53542
+ createDragHandle() {
53543
+ const dragHandle = document.createElement("span");
53544
+ dragHandle.classList.add("sd-structured-content-draggable");
53545
+ dragHandle.draggable = true;
53546
+ dragHandle.contentEditable = "false";
53547
+ dragHandle.dataset.dragHandle = "";
53548
+ const textElement = document.createElement("span");
53549
+ textElement.textContent = this.node.attrs.alias || "Structured content";
53550
+ dragHandle.append(textElement);
53551
+ return dragHandle;
53552
+ }
53553
+ onDragStart(event) {
53554
+ const { view } = this.editor;
53555
+ const target = event.target;
53556
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
53557
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
53558
+ return;
53559
+ }
53560
+ let x = 0;
53561
+ let y2 = 0;
53562
+ if (this.dom !== dragHandle) {
53563
+ const domBox = this.dom.getBoundingClientRect();
53564
+ const handleBox = dragHandle.getBoundingClientRect();
53565
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
53566
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
53567
+ x = handleBox.x - domBox.x + offsetX;
53568
+ y2 = handleBox.y - domBox.y + offsetY;
53569
+ }
53570
+ event.dataTransfer?.setDragImage(this.dom, x, y2);
53571
+ const pos = this.getPos();
53572
+ if (typeof pos !== "number") {
53573
+ return;
53574
+ }
53575
+ const selection = NodeSelection.create(view.state.doc, pos);
53576
+ const transaction = view.state.tr.setSelection(selection);
53452
53577
  view.dispatch(transaction);
53453
- });
53454
- if (handled) {
53455
- tr.setMeta("preventDispatch", true);
53456
53578
  }
53457
- return handled;
53458
- };
53459
- const Run = OxmlNode.create({
53460
- name: "run",
53461
- oXmlName: "w:r",
53462
- group: "inline",
53579
+ }
53580
+ class StructuredContentInlineView extends StructuredContentViewBase {
53581
+ constructor(props) {
53582
+ super(props);
53583
+ }
53584
+ mount() {
53585
+ this.buildView();
53586
+ }
53587
+ get contentDOM() {
53588
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
53589
+ return contentElement || null;
53590
+ }
53591
+ createElement() {
53592
+ const element = document.createElement("span");
53593
+ element.classList.add(structuredContentClass$1);
53594
+ element.setAttribute("data-structured-content", "");
53595
+ const contentElement = document.createElement("span");
53596
+ contentElement.classList.add(structuredContentInnerClass$1);
53597
+ element.append(contentElement);
53598
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53599
+ updateDOMAttributes(element, { ...domAttrs });
53600
+ return { element, contentElement };
53601
+ }
53602
+ buildView() {
53603
+ const { element } = this.createElement();
53604
+ const dragHandle = this.createDragHandle();
53605
+ element.prepend(dragHandle);
53606
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53607
+ this.root = element;
53608
+ }
53609
+ updateView() {
53610
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53611
+ updateDOMAttributes(this.dom, { ...domAttrs });
53612
+ }
53613
+ update(node, decorations, innerDecorations) {
53614
+ const result = super.update(node, decorations, innerDecorations);
53615
+ if (!result) return false;
53616
+ this.updateView();
53617
+ return true;
53618
+ }
53619
+ }
53620
+ const structuredContentClass$1 = "sd-structured-content";
53621
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
53622
+ const StructuredContent = Node$1.create({
53623
+ name: "structuredContent",
53624
+ group: "inline structuredContent",
53463
53625
  inline: true,
53464
53626
  content: "inline*",
53465
- selectable: false,
53466
- childToAttributes: ["runProperties"],
53627
+ isolating: true,
53628
+ atom: false,
53629
+ // false - has editable content.
53630
+ draggable: true,
53467
53631
  addOptions() {
53468
53632
  return {
53469
53633
  htmlAttributes: {
53470
- "data-run": "1"
53634
+ class: structuredContentClass$1,
53635
+ "aria-label": "Structured content node"
53471
53636
  }
53472
53637
  };
53473
53638
  },
53474
53639
  addAttributes() {
53475
53640
  return {
53476
- runProperties: {
53641
+ id: {
53477
53642
  default: null,
53478
- rendered: false,
53479
- keepOnSplit: true
53643
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53644
+ renderDOM: (attrs) => {
53645
+ if (!attrs.id) return {};
53646
+ return { "data-id": attrs.id };
53647
+ }
53480
53648
  },
53481
- rsidR: {
53649
+ tag: {
53482
53650
  default: null,
53483
- rendered: false,
53484
- keepOnSplit: true
53651
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53652
+ renderDOM: (attrs) => {
53653
+ if (!attrs.tag) return {};
53654
+ return { "data-tag": attrs.tag };
53655
+ }
53485
53656
  },
53486
- rsidRPr: {
53657
+ alias: {
53487
53658
  default: null,
53488
- rendered: false,
53489
- keepOnSplit: true
53659
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53660
+ renderDOM: (attrs) => {
53661
+ if (!attrs.alias) return {};
53662
+ return { "data-alias": attrs.alias };
53663
+ }
53490
53664
  },
53491
- rsidDel: {
53492
- default: null,
53493
- rendered: false,
53494
- keepOnSplit: true
53665
+ sdtPr: {
53666
+ rendered: false
53495
53667
  }
53496
53668
  };
53497
53669
  },
53498
- addCommands() {
53499
- return {
53500
- splitRun
53501
- };
53502
- },
53503
53670
  parseDOM() {
53504
- return [{ tag: "span[data-run]" }];
53671
+ return [{ tag: "span[data-structured-content]" }];
53505
53672
  },
53506
53673
  renderDOM({ htmlAttributes }) {
53507
- const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53508
- return ["span", base2, 0];
53674
+ return [
53675
+ "span",
53676
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53677
+ "data-structured-content": ""
53678
+ }),
53679
+ 0
53680
+ ];
53681
+ },
53682
+ addNodeView() {
53683
+ return (props) => {
53684
+ return new StructuredContentInlineView({ ...props });
53685
+ };
53509
53686
  }
53510
53687
  });
53511
- const inputRegex$1 = /^\s*([-+*])\s$/;
53512
- const BulletList = Node$1.create({
53513
- name: "bulletList",
53514
- group: "block list",
53515
- selectable: false,
53516
- content() {
53517
- return `${this.options.itemTypeName}+`;
53518
- },
53688
+ class StructuredContentBlockView extends StructuredContentViewBase {
53689
+ constructor(props) {
53690
+ super(props);
53691
+ }
53692
+ mount() {
53693
+ this.buildView();
53694
+ }
53695
+ get contentDOM() {
53696
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
53697
+ return contentElement || null;
53698
+ }
53699
+ createElement() {
53700
+ const element = document.createElement("div");
53701
+ element.classList.add(structuredContentClass);
53702
+ element.setAttribute("data-structured-content-block", "");
53703
+ const contentElement = document.createElement("div");
53704
+ contentElement.classList.add(structuredContentInnerClass);
53705
+ element.append(contentElement);
53706
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53707
+ updateDOMAttributes(element, { ...domAttrs });
53708
+ return { element, contentElement };
53709
+ }
53710
+ buildView() {
53711
+ const { element } = this.createElement();
53712
+ const dragHandle = this.createDragHandle();
53713
+ element.prepend(dragHandle);
53714
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53715
+ this.root = element;
53716
+ }
53717
+ updateView() {
53718
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53719
+ updateDOMAttributes(this.dom, { ...domAttrs });
53720
+ }
53721
+ update(node, decorations, innerDecorations) {
53722
+ const result = super.update(node, decorations, innerDecorations);
53723
+ if (!result) return false;
53724
+ this.updateView();
53725
+ return true;
53726
+ }
53727
+ }
53728
+ const structuredContentClass = "sd-structured-content-block";
53729
+ const structuredContentInnerClass = "sd-structured-content-block__content";
53730
+ const StructuredContentBlock = Node$1.create({
53731
+ name: "structuredContentBlock",
53732
+ group: "block structuredContent",
53733
+ content: "block*",
53734
+ isolating: true,
53735
+ atom: false,
53736
+ // false - has editable content.
53737
+ draggable: true,
53519
53738
  addOptions() {
53520
53739
  return {
53521
- itemTypeName: "listItem",
53522
53740
  htmlAttributes: {
53523
- "aria-label": "Bullet list node"
53524
- },
53525
- keepMarks: true,
53526
- keepAttributes: false
53741
+ class: structuredContentClass,
53742
+ "aria-label": "Structured content block node"
53743
+ }
53527
53744
  };
53528
53745
  },
53529
- parseDOM() {
53530
- return [{ tag: "ul" }];
53531
- },
53532
- renderDOM({ htmlAttributes }) {
53533
- const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53534
- return ["ul", attributes, 0];
53535
- },
53536
53746
  addAttributes() {
53537
53747
  return {
53538
- "list-style-type": {
53539
- default: "bullet",
53540
- rendered: false
53748
+ id: {
53749
+ default: null,
53750
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53751
+ renderDOM: (attrs) => {
53752
+ if (!attrs.id) return {};
53753
+ return { "data-id": attrs.id };
53754
+ }
53541
53755
  },
53542
- listId: {
53543
- rendered: false
53756
+ tag: {
53757
+ default: null,
53758
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53759
+ renderDOM: (attrs) => {
53760
+ if (!attrs.tag) return {};
53761
+ return { "data-tag": attrs.tag };
53762
+ }
53544
53763
  },
53545
- sdBlockId: {
53764
+ alias: {
53546
53765
  default: null,
53547
- keepOnSplit: false,
53548
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
53766
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53549
53767
  renderDOM: (attrs) => {
53550
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
53768
+ if (!attrs.alias) return {};
53769
+ return { "data-alias": attrs.alias };
53551
53770
  }
53552
53771
  },
53553
- attributes: {
53554
- rendered: false,
53555
- keepOnSplit: true
53772
+ sdtPr: {
53773
+ rendered: false
53556
53774
  }
53557
53775
  };
53558
53776
  },
53777
+ parseDOM() {
53778
+ return [{ tag: "div[data-structured-content-block]" }];
53779
+ },
53780
+ renderDOM({ htmlAttributes }) {
53781
+ return [
53782
+ "div",
53783
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53784
+ "data-structured-content-block": ""
53785
+ }),
53786
+ 0
53787
+ ];
53788
+ },
53789
+ addNodeView() {
53790
+ return (props) => {
53791
+ return new StructuredContentBlockView({ ...props });
53792
+ };
53793
+ }
53794
+ });
53795
+ function getStructuredContentTagsById(idOrIds, state2) {
53796
+ const result = findChildren$5(state2.doc, (node) => {
53797
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
53798
+ if (Array.isArray(idOrIds)) {
53799
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
53800
+ } else {
53801
+ return isStructuredContent && node.attrs.id === idOrIds;
53802
+ }
53803
+ });
53804
+ return result;
53805
+ }
53806
+ function getStructuredContentTags(state2) {
53807
+ const result = findChildren$5(state2.doc, (node) => {
53808
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
53809
+ });
53810
+ return result;
53811
+ }
53812
+ function getStructuredContentInlineTags(state2) {
53813
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContent");
53814
+ return result;
53815
+ }
53816
+ function getStructuredContentBlockTags(state2) {
53817
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContentBlock");
53818
+ return result;
53819
+ }
53820
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
53821
+ __proto__: null,
53822
+ getStructuredContentBlockTags,
53823
+ getStructuredContentInlineTags,
53824
+ getStructuredContentTags,
53825
+ getStructuredContentTagsById
53826
+ }, Symbol.toStringTag, { value: "Module" }));
53827
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
53828
+ const StructuredContentCommands = Extension.create({
53829
+ name: "structuredContentCommands",
53559
53830
  addCommands() {
53560
53831
  return {
53561
53832
  /**
53562
- * Toggle a bullet list at the current selection
53833
+ * Inserts a structured content inline at selection.
53563
53834
  * @category Command
53564
- * @example
53565
- * // Toggle bullet list on selected text
53566
- * editor.commands.toggleBulletList()
53567
- * @note Converts selected paragraphs to list items or removes list formatting
53835
+ * @param {StructuredContentInlineInsert} options
53568
53836
  */
53569
- toggleBulletList: () => (params2) => {
53570
- return toggleList(this.type)(params2);
53571
- }
53572
- };
53573
- },
53574
- addShortcuts() {
53837
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53838
+ const { schema } = editor;
53839
+ let { from: from2, to } = state2.selection;
53840
+ if (dispatch) {
53841
+ const selectionText = state2.doc.textBetween(from2, to);
53842
+ let content = null;
53843
+ if (selectionText) {
53844
+ content = schema.text(selectionText);
53845
+ }
53846
+ if (options.text) {
53847
+ content = schema.text(options.text);
53848
+ }
53849
+ if (options.json) {
53850
+ content = schema.nodeFromJSON(options.json);
53851
+ }
53852
+ if (!content) {
53853
+ content = schema.text(" ");
53854
+ }
53855
+ const attrs = {
53856
+ ...options.attrs,
53857
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
53858
+ tag: "inline_text_sdt",
53859
+ alias: options.attrs?.alias || "Structured content"
53860
+ };
53861
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
53862
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state2.selection);
53863
+ if (parent) {
53864
+ const insertPos = parent.pos + parent.node.nodeSize;
53865
+ from2 = to = insertPos;
53866
+ }
53867
+ tr.replaceWith(from2, to, node);
53868
+ }
53869
+ return true;
53870
+ },
53871
+ /**
53872
+ * Inserts a structured content block at selection.
53873
+ * @category Command
53874
+ * @param {StructuredContentBlockInsert} options
53875
+ */
53876
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53877
+ const { schema } = editor;
53878
+ let { from: from2, to } = state2.selection;
53879
+ if (dispatch) {
53880
+ const selectionContent = state2.selection.content();
53881
+ let content = null;
53882
+ if (selectionContent.size) {
53883
+ content = selectionContent.content;
53884
+ }
53885
+ if (options.html) {
53886
+ const html = htmlHandler(options.html, editor);
53887
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53888
+ content = doc2.content;
53889
+ }
53890
+ if (options.json) {
53891
+ content = schema.nodeFromJSON(options.json);
53892
+ }
53893
+ if (!content) {
53894
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
53895
+ }
53896
+ const attrs = {
53897
+ ...options.attrs,
53898
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
53899
+ tag: "block_table_sdt",
53900
+ alias: options.attrs?.alias || "Structured content"
53901
+ };
53902
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
53903
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state2.selection);
53904
+ if (parent) {
53905
+ const insertPos = parent.pos + parent.node.nodeSize;
53906
+ from2 = to = insertPos;
53907
+ }
53908
+ tr.replaceRangeWith(from2, to, node);
53909
+ }
53910
+ return true;
53911
+ },
53912
+ /**
53913
+ * Updates a structured content attributes or content.
53914
+ * If the updated node does not match the schema, it will not be updated.
53915
+ * @category Command
53916
+ * @param {string} id
53917
+ * @param {StructuredContentUpdate} options
53918
+ */
53919
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53920
+ const structuredContentTags = getStructuredContentTagsById(id, state2);
53921
+ if (!structuredContentTags.length) {
53922
+ return true;
53923
+ }
53924
+ const { schema } = editor;
53925
+ if (dispatch) {
53926
+ const structuredContent = structuredContentTags[0];
53927
+ const { pos, node } = structuredContent;
53928
+ const posFrom = pos;
53929
+ const posTo = pos + node.nodeSize;
53930
+ let content = null;
53931
+ if (options.text) {
53932
+ content = schema.text(options.text);
53933
+ }
53934
+ if (options.html) {
53935
+ const html = htmlHandler(options.html, editor);
53936
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53937
+ content = doc2.content;
53938
+ }
53939
+ if (options.json) {
53940
+ content = schema.nodeFromJSON(options.json);
53941
+ }
53942
+ if (!content) {
53943
+ content = node.content;
53944
+ }
53945
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
53946
+ try {
53947
+ updatedNode.check();
53948
+ } catch {
53949
+ console.error("Updated node does not conform to the schema");
53950
+ return false;
53951
+ }
53952
+ tr.replaceWith(posFrom, posTo, updatedNode);
53953
+ }
53954
+ return true;
53955
+ },
53956
+ /**
53957
+ * Removes a structured content.
53958
+ * @category Command
53959
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
53960
+ */
53961
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
53962
+ if (!structuredContentTags.length) {
53963
+ return true;
53964
+ }
53965
+ if (dispatch) {
53966
+ structuredContentTags.forEach((structuredContent) => {
53967
+ const { pos, node } = structuredContent;
53968
+ const posFrom = tr.mapping.map(pos);
53969
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53970
+ const currentNode = tr.doc.nodeAt(posFrom);
53971
+ if (currentNode && node.eq(currentNode)) {
53972
+ tr.delete(posFrom, posTo);
53973
+ }
53974
+ });
53975
+ }
53976
+ return true;
53977
+ },
53978
+ /**
53979
+ * Removes a structured content by ID.
53980
+ * @category Command
53981
+ * @param {string | string[]} idOrIds
53982
+ */
53983
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state: state2, tr }) => {
53984
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state2);
53985
+ if (!structuredContentTags.length) {
53986
+ return true;
53987
+ }
53988
+ if (dispatch) {
53989
+ structuredContentTags.forEach((structuredContent) => {
53990
+ const { pos, node } = structuredContent;
53991
+ const posFrom = tr.mapping.map(pos);
53992
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53993
+ const currentNode = tr.doc.nodeAt(posFrom);
53994
+ if (currentNode && node.eq(currentNode)) {
53995
+ tr.delete(posFrom, posTo);
53996
+ }
53997
+ });
53998
+ }
53999
+ return true;
54000
+ },
54001
+ /**
54002
+ * Removes a structured content at cursor, preserving its content.
54003
+ * @category Command
54004
+ */
54005
+ deleteStructuredContentAtSelection: () => ({ dispatch, state: state2, tr }) => {
54006
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
54007
+ const structuredContent = findParentNode(predicate)(state2.selection);
54008
+ if (!structuredContent) {
54009
+ return true;
54010
+ }
54011
+ if (dispatch) {
54012
+ const { node, pos } = structuredContent;
54013
+ const posFrom = pos;
54014
+ const posTo = posFrom + node.nodeSize;
54015
+ const content = node.content;
54016
+ tr.replaceWith(posFrom, posTo, content);
54017
+ }
54018
+ return true;
54019
+ }
54020
+ };
54021
+ },
54022
+ addHelpers() {
54023
+ return {
54024
+ ...structuredContentHelpers
54025
+ };
54026
+ }
54027
+ });
54028
+ class DocumentSectionView {
54029
+ constructor(node, getPos, decorations, editor) {
54030
+ __privateAdd$1(this, _DocumentSectionView_instances);
54031
+ this.node = node;
54032
+ this.editor = editor;
54033
+ this.decorations = decorations;
54034
+ this.view = editor.view;
54035
+ this.getPos = getPos;
54036
+ __privateMethod$1(this, _DocumentSectionView_instances, init_fn2).call(this);
54037
+ }
54038
+ }
54039
+ _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
54040
+ init_fn2 = function() {
54041
+ const { attrs } = this.node;
54042
+ const { id, title, description } = attrs;
54043
+ this.dom = document.createElement("div");
54044
+ this.dom.className = "sd-document-section-block";
54045
+ this.dom.setAttribute("data-id", id);
54046
+ this.dom.setAttribute("data-title", title);
54047
+ this.dom.setAttribute("data-description", description);
54048
+ this.dom.setAttribute("aria-label", "Document section");
54049
+ __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
54050
+ this.contentDOM = document.createElement("div");
54051
+ this.contentDOM.className = "sd-document-section-block-content";
54052
+ this.contentDOM.setAttribute("contenteditable", "true");
54053
+ this.dom.appendChild(this.contentDOM);
54054
+ };
54055
+ addToolTip_fn = function() {
54056
+ const { title } = this.node.attrs;
54057
+ this.infoDiv = document.createElement("div");
54058
+ this.infoDiv.className = "sd-document-section-block-info";
54059
+ const textSpan = document.createElement("span");
54060
+ textSpan.textContent = title || "Document section";
54061
+ this.infoDiv.appendChild(textSpan);
54062
+ this.infoDiv.setAttribute("contenteditable", "false");
54063
+ this.dom.appendChild(this.infoDiv);
54064
+ };
54065
+ const getAllSections = (editor) => {
54066
+ if (!editor) return [];
54067
+ const type2 = editor.schema.nodes.documentSection;
54068
+ if (!type2) return [];
54069
+ const sections = [];
54070
+ const { state: state2 } = editor;
54071
+ state2.doc.descendants((node, pos) => {
54072
+ if (node.type.name === type2.name) {
54073
+ sections.push({ node, pos });
54074
+ }
54075
+ });
54076
+ return sections;
54077
+ };
54078
+ const exportSectionsToHTML = (editor) => {
54079
+ const sections = getAllSections(editor);
54080
+ const processedSections = /* @__PURE__ */ new Set();
54081
+ const result = [];
54082
+ sections.forEach(({ node }) => {
54083
+ const { attrs } = node;
54084
+ const { id, title, description } = attrs;
54085
+ if (processedSections.has(id)) return;
54086
+ processedSections.add(id);
54087
+ const html = getHTMLFromNode(node, editor);
54088
+ result.push({
54089
+ id,
54090
+ title,
54091
+ description,
54092
+ html
54093
+ });
54094
+ });
54095
+ return result;
54096
+ };
54097
+ const getHTMLFromNode = (node, editor) => {
54098
+ const tempDocument = document.implementation.createHTMLDocument();
54099
+ const container = tempDocument.createElement("div");
54100
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
54101
+ container.appendChild(fragment);
54102
+ let html = container.innerHTML;
54103
+ return html;
54104
+ };
54105
+ const exportSectionsToJSON = (editor) => {
54106
+ const sections = getAllSections(editor);
54107
+ const processedSections = /* @__PURE__ */ new Set();
54108
+ const result = [];
54109
+ sections.forEach(({ node }) => {
54110
+ const { attrs } = node;
54111
+ const { id, title, description } = attrs;
54112
+ if (processedSections.has(id)) return;
54113
+ processedSections.add(id);
54114
+ result.push({
54115
+ id,
54116
+ title,
54117
+ description,
54118
+ content: node.toJSON()
54119
+ });
54120
+ });
54121
+ return result;
54122
+ };
54123
+ const getLinkedSectionEditor = (id, options, editor) => {
54124
+ const sections = getAllSections(editor);
54125
+ const section = sections.find((s) => s.node.attrs.id === id);
54126
+ if (!section) return null;
54127
+ const child = editor.createChildEditor({
54128
+ ...options,
54129
+ onUpdate: ({ editor: childEditor, transaction }) => {
54130
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
54131
+ if (isFromtLinkedParent) return;
54132
+ const updatedContent = childEditor.state.doc.content;
54133
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54134
+ if (!sectionNode) return;
54135
+ const { pos, node } = sectionNode;
54136
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
54137
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
54138
+ tr.setMeta("fromLinkedChild", true);
54139
+ editor.view.dispatch(tr);
54140
+ }
54141
+ });
54142
+ editor.on("update", ({ transaction }) => {
54143
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
54144
+ if (isFromLinkedChild) return;
54145
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54146
+ if (!sectionNode) return;
54147
+ const sectionContent = sectionNode.node.content;
54148
+ const json = {
54149
+ type: "doc",
54150
+ content: sectionContent.content.map((node) => node.toJSON())
54151
+ };
54152
+ const childTr = child.state.tr;
54153
+ childTr.setMeta("fromLinkedParent", true);
54154
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
54155
+ child.view.dispatch(childTr);
54156
+ });
54157
+ return child;
54158
+ };
54159
+ const SectionHelpers = {
54160
+ getAllSections,
54161
+ exportSectionsToHTML,
54162
+ exportSectionsToJSON,
54163
+ getLinkedSectionEditor
54164
+ };
54165
+ const DocumentSection = Node$1.create({
54166
+ name: "documentSection",
54167
+ group: "block",
54168
+ content: "block*",
54169
+ atom: true,
54170
+ isolating: true,
54171
+ addOptions() {
54172
+ return {
54173
+ htmlAttributes: {
54174
+ class: "sd-document-section-block",
54175
+ "aria-label": "Structured content block"
54176
+ }
54177
+ };
54178
+ },
54179
+ parseDOM() {
54180
+ return [
54181
+ {
54182
+ tag: "div.sd-document-section-block",
54183
+ priority: 60
54184
+ }
54185
+ ];
54186
+ },
54187
+ renderDOM({ htmlAttributes }) {
54188
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
54189
+ },
54190
+ addAttributes() {
54191
+ return {
54192
+ id: {},
54193
+ sdBlockId: {
54194
+ default: null,
54195
+ keepOnSplit: false,
54196
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54197
+ renderDOM: (attrs) => {
54198
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54199
+ }
54200
+ },
54201
+ title: {},
54202
+ description: {},
54203
+ sectionType: {},
54204
+ isLocked: { default: false }
54205
+ };
54206
+ },
54207
+ addNodeView() {
54208
+ return ({ node, editor, getPos, decorations }) => {
54209
+ return new DocumentSectionView(node, getPos, decorations, editor);
54210
+ };
54211
+ },
54212
+ addCommands() {
54213
+ return {
54214
+ /**
54215
+ * Create a lockable content section
54216
+ * @category Command
54217
+ * @param {SectionCreate} [options={}] - Section configuration
54218
+ * @example
54219
+ * editor.commands.createDocumentSection({
54220
+ * id: 1,
54221
+ * title: 'Terms & Conditions',
54222
+ * isLocked: true,
54223
+ * html: '<p>Legal content...</p>'
54224
+ * })
54225
+ */
54226
+ createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
54227
+ const { selection } = state2;
54228
+ let { from: from2, to } = selection;
54229
+ let content = selection.content().content;
54230
+ const { html: optionsHTML, json: optionsJSON } = options;
54231
+ if (optionsHTML) {
54232
+ const html = htmlHandler(optionsHTML, this.editor);
54233
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
54234
+ content = doc2.content;
54235
+ }
54236
+ if (optionsJSON) {
54237
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
54238
+ }
54239
+ if (!content?.content?.length) {
54240
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
54241
+ }
54242
+ if (!options.id) {
54243
+ const allSections = SectionHelpers.getAllSections(editor);
54244
+ options.id = allSections.length + 1;
54245
+ }
54246
+ if (!options.title) {
54247
+ options.title = "Document section";
54248
+ }
54249
+ const node = this.type.createAndFill(options, content);
54250
+ if (!node) return false;
54251
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
54252
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
54253
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
54254
+ from2 = insertPos2;
54255
+ to = insertPos2;
54256
+ }
54257
+ tr.replaceRangeWith(from2, to, node);
54258
+ const nodeEnd = from2 + node.nodeSize;
54259
+ let shouldInsertParagraph = true;
54260
+ let insertPos = nodeEnd;
54261
+ if (nodeEnd >= tr.doc.content.size) {
54262
+ insertPos = tr.doc.content.size;
54263
+ if (insertPos > 0) {
54264
+ const $endPos = tr.doc.resolve(insertPos);
54265
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
54266
+ shouldInsertParagraph = false;
54267
+ }
54268
+ }
54269
+ }
54270
+ if (shouldInsertParagraph) {
54271
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
54272
+ tr.insert(insertPos, emptyParagraph);
54273
+ }
54274
+ if (dispatch) {
54275
+ tr.setMeta("documentSection", { action: "create" });
54276
+ dispatch(tr);
54277
+ setTimeout(() => {
54278
+ try {
54279
+ const currentState = editor.state;
54280
+ const docSize = currentState.doc.content.size;
54281
+ let targetPos = from2 + node.nodeSize;
54282
+ if (shouldInsertParagraph) {
54283
+ targetPos += 1;
54284
+ }
54285
+ targetPos = Math.min(targetPos, docSize);
54286
+ if (targetPos < docSize && targetPos > 0) {
54287
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
54288
+ const newTr = currentState.tr.setSelection(newSelection);
54289
+ editor.view.dispatch(newTr);
54290
+ }
54291
+ } catch (e) {
54292
+ console.warn("Could not set delayed selection:", e);
54293
+ }
54294
+ }, 0);
54295
+ }
54296
+ return true;
54297
+ },
54298
+ /**
54299
+ * Remove section wrapper at cursor, preserving its content
54300
+ * @category Command
54301
+ * @example
54302
+ * editor.commands.removeSectionAtSelection()
54303
+ * @note Content stays in document, only section wrapper is removed
54304
+ */
54305
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
54306
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
54307
+ if (!sdtNode) return false;
54308
+ const { node, pos } = sdtNode;
54309
+ const nodeStart = pos;
54310
+ const nodeEnd = nodeStart + node.nodeSize;
54311
+ const contentToPreserve = node.content;
54312
+ tr.delete(nodeStart, nodeEnd);
54313
+ if (contentToPreserve.size > 0) {
54314
+ tr.insert(nodeStart, contentToPreserve);
54315
+ }
54316
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
54317
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
54318
+ if (dispatch) {
54319
+ tr.setMeta("documentSection", { action: "delete" });
54320
+ dispatch(tr);
54321
+ }
54322
+ return true;
54323
+ },
54324
+ /**
54325
+ * Delete section and all its content
54326
+ * @category Command
54327
+ * @param {number} id - Section to delete
54328
+ * @example
54329
+ * editor.commands.removeSectionById(123)
54330
+ */
54331
+ removeSectionById: (id) => ({ tr, dispatch }) => {
54332
+ const sections = SectionHelpers.getAllSections(this.editor);
54333
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
54334
+ if (!sectionToRemove) return false;
54335
+ const { pos, node } = sectionToRemove;
54336
+ const nodeStart = pos;
54337
+ const nodeEnd = nodeStart + node.nodeSize;
54338
+ tr.delete(nodeStart, nodeEnd);
54339
+ if (dispatch) {
54340
+ tr.setMeta("documentSection", { action: "delete", id });
54341
+ dispatch(tr);
54342
+ }
54343
+ return true;
54344
+ },
54345
+ /**
54346
+ * Lock section against edits
54347
+ * @category Command
54348
+ * @param {number} id - Section to lock
54349
+ * @example
54350
+ * editor.commands.lockSectionById(123)
54351
+ */
54352
+ lockSectionById: (id) => ({ tr, dispatch }) => {
54353
+ const sections = SectionHelpers.getAllSections(this.editor);
54354
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
54355
+ if (!sectionToLock) return false;
54356
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
54357
+ if (dispatch) {
54358
+ tr.setMeta("documentSection", { action: "lock", id });
54359
+ dispatch(tr);
54360
+ }
54361
+ return true;
54362
+ },
54363
+ /**
54364
+ * Modify section attributes or content
54365
+ * @category Command
54366
+ * @param {SectionUpdate} options - Changes to apply
54367
+ * @example
54368
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
54369
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
54370
+ * editor.commands.updateSectionById({
54371
+ * id: 123,
54372
+ * html: '<p>Updated</p>',
54373
+ * attrs: { title: 'New Title' }
54374
+ * })
54375
+ */
54376
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
54377
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
54378
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
54379
+ if (!sectionToUpdate) return false;
54380
+ const { pos, node } = sectionToUpdate;
54381
+ let newContent = null;
54382
+ if (html) {
54383
+ const htmlDoc = htmlHandler(html, editor || this.editor);
54384
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
54385
+ newContent = doc2.content;
54386
+ }
54387
+ if (json) {
54388
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
54389
+ }
54390
+ if (!newContent) {
54391
+ newContent = node.content;
54392
+ }
54393
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
54394
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
54395
+ if (dispatch) {
54396
+ tr.setMeta("documentSection", { action: "update", id, attrs });
54397
+ dispatch(tr);
54398
+ }
54399
+ return true;
54400
+ }
54401
+ };
54402
+ },
54403
+ addHelpers() {
54404
+ return {
54405
+ ...SectionHelpers
54406
+ };
54407
+ }
54408
+ });
54409
+ const Document = Node$1.create({
54410
+ name: "doc",
54411
+ topNode: true,
54412
+ content: "block+",
54413
+ parseDOM() {
54414
+ return [{ tag: "doc" }];
54415
+ },
54416
+ renderDOM() {
54417
+ return ["doc", 0];
54418
+ },
54419
+ addAttributes() {
54420
+ return {
54421
+ attributes: {
54422
+ rendered: false,
54423
+ "aria-label": "Document node"
54424
+ }
54425
+ };
54426
+ },
54427
+ addCommands() {
54428
+ return {
54429
+ /**
54430
+ * Get document statistics
54431
+ * @category Command
54432
+ * @example
54433
+ * // Get word and character count
54434
+ * const stats = editor.commands.getDocumentStats()
54435
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
54436
+ * @note Returns word count, character count, and paragraph count
54437
+ */
54438
+ getDocumentStats: () => ({ editor }) => {
54439
+ const text = editor.getText();
54440
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
54441
+ const characters = text.length;
54442
+ const paragraphs = editor.state.doc.content.childCount;
54443
+ return {
54444
+ words,
54445
+ characters,
54446
+ paragraphs
54447
+ };
54448
+ },
54449
+ /**
54450
+ * Clear entire document
54451
+ * @category Command
54452
+ * @example
54453
+ * editor.commands.clearDocument()
54454
+ * @note Replaces all content with an empty paragraph
54455
+ */
54456
+ clearDocument: () => ({ commands: commands2 }) => {
54457
+ return commands2.setContent("<p></p>");
54458
+ }
54459
+ };
54460
+ }
54461
+ });
54462
+ const Text = Node$1.create({
54463
+ name: "text",
54464
+ group: "inline",
54465
+ inline: true,
54466
+ addOptions() {
54467
+ return {};
54468
+ }
54469
+ });
54470
+ const splitRun = () => (props) => {
54471
+ const { state: state2, view, tr } = props;
54472
+ const { $from, empty: empty2 } = state2.selection;
54473
+ if (!empty2) return false;
54474
+ if ($from.parent.type.name !== "run") return false;
54475
+ const handled = splitBlock(state2, (transaction) => {
54476
+ view.dispatch(transaction);
54477
+ });
54478
+ if (handled) {
54479
+ tr.setMeta("preventDispatch", true);
54480
+ }
54481
+ return handled;
54482
+ };
54483
+ const Run = OxmlNode.create({
54484
+ name: "run",
54485
+ oXmlName: "w:r",
54486
+ group: "inline",
54487
+ inline: true,
54488
+ content: "inline*",
54489
+ selectable: false,
54490
+ childToAttributes: ["runProperties"],
54491
+ addOptions() {
54492
+ return {
54493
+ htmlAttributes: {
54494
+ "data-run": "1"
54495
+ }
54496
+ };
54497
+ },
54498
+ addAttributes() {
54499
+ return {
54500
+ runProperties: {
54501
+ default: null,
54502
+ rendered: false,
54503
+ keepOnSplit: true
54504
+ },
54505
+ rsidR: {
54506
+ default: null,
54507
+ rendered: false,
54508
+ keepOnSplit: true
54509
+ },
54510
+ rsidRPr: {
54511
+ default: null,
54512
+ rendered: false,
54513
+ keepOnSplit: true
54514
+ },
54515
+ rsidDel: {
54516
+ default: null,
54517
+ rendered: false,
54518
+ keepOnSplit: true
54519
+ }
54520
+ };
54521
+ },
54522
+ addCommands() {
54523
+ return {
54524
+ splitRun
54525
+ };
54526
+ },
54527
+ parseDOM() {
54528
+ return [{ tag: "span[data-run]" }];
54529
+ },
54530
+ renderDOM({ htmlAttributes }) {
54531
+ const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54532
+ return ["span", base2, 0];
54533
+ }
54534
+ });
54535
+ const inputRegex$1 = /^\s*([-+*])\s$/;
54536
+ const BulletList = Node$1.create({
54537
+ name: "bulletList",
54538
+ group: "block list",
54539
+ selectable: false,
54540
+ content() {
54541
+ return `${this.options.itemTypeName}+`;
54542
+ },
54543
+ addOptions() {
54544
+ return {
54545
+ itemTypeName: "listItem",
54546
+ htmlAttributes: {
54547
+ "aria-label": "Bullet list node"
54548
+ },
54549
+ keepMarks: true,
54550
+ keepAttributes: false
54551
+ };
54552
+ },
54553
+ parseDOM() {
54554
+ return [{ tag: "ul" }];
54555
+ },
54556
+ renderDOM({ htmlAttributes }) {
54557
+ const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54558
+ return ["ul", attributes, 0];
54559
+ },
54560
+ addAttributes() {
54561
+ return {
54562
+ "list-style-type": {
54563
+ default: "bullet",
54564
+ rendered: false
54565
+ },
54566
+ listId: {
54567
+ rendered: false
54568
+ },
54569
+ sdBlockId: {
54570
+ default: null,
54571
+ keepOnSplit: false,
54572
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54573
+ renderDOM: (attrs) => {
54574
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54575
+ }
54576
+ },
54577
+ attributes: {
54578
+ rendered: false,
54579
+ keepOnSplit: true
54580
+ }
54581
+ };
54582
+ },
54583
+ addCommands() {
54584
+ return {
54585
+ /**
54586
+ * Toggle a bullet list at the current selection
54587
+ * @category Command
54588
+ * @example
54589
+ * // Toggle bullet list on selected text
54590
+ * editor.commands.toggleBulletList()
54591
+ * @note Converts selected paragraphs to list items or removes list formatting
54592
+ */
54593
+ toggleBulletList: () => (params2) => {
54594
+ return toggleList(this.type)(params2);
54595
+ }
54596
+ };
54597
+ },
54598
+ addShortcuts() {
53575
54599
  return {
53576
54600
  "Mod-Shift-8": () => {
53577
54601
  return this.editor.commands.toggleBulletList();
@@ -54964,7 +55988,7 @@ class ListItemNodeView {
54964
55988
  this.decorations = decorations;
54965
55989
  this.view = editor.view;
54966
55990
  this.getPos = getPos;
54967
- __privateMethod$1(this, _ListItemNodeView_instances, init_fn2).call(this);
55991
+ __privateMethod$1(this, _ListItemNodeView_instances, init_fn3).call(this);
54968
55992
  activeListItemNodeViews.add(this);
54969
55993
  }
54970
55994
  refreshIndentStyling() {
@@ -55025,7 +56049,7 @@ class ListItemNodeView {
55025
56049
  }
55026
56050
  }
55027
56051
  _ListItemNodeView_instances = /* @__PURE__ */ new WeakSet();
55028
- init_fn2 = function() {
56052
+ init_fn3 = function() {
55029
56053
  const { attrs } = this.node;
55030
56054
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
55031
56055
  let orderMarker = "";
@@ -62134,984 +63158,335 @@ const PageNumber = Node$1.create({
62134
63158
  }
62135
63159
  };
62136
63160
  },
62137
- addAttributes() {
62138
- return {
62139
- marksAsAttrs: {
62140
- default: null,
62141
- rendered: false
62142
- }
62143
- };
62144
- },
62145
- addNodeView() {
62146
- return ({ node, editor, getPos, decorations }) => {
62147
- const htmlAttributes = this.options.htmlAttributes;
62148
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
62149
- };
62150
- },
62151
- parseDOM() {
62152
- return [{ tag: 'span[data-id="auto-page-number"' }];
62153
- },
62154
- renderDOM({ htmlAttributes }) {
62155
- return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
62156
- },
62157
- addCommands() {
62158
- return {
62159
- /**
62160
- * Insert an automatic page number
62161
- * @category Command
62162
- * @returns {Function} Command function
62163
- * @example
62164
- * editor.commands.addAutoPageNumber()
62165
- * @note Only works in header/footer contexts
62166
- */
62167
- addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
62168
- const { options } = editor;
62169
- if (!options.isHeaderOrFooter) return false;
62170
- const { schema } = state2;
62171
- const pageNumberType = schema?.nodes?.["page-number"];
62172
- if (!pageNumberType) return false;
62173
- const pageNumberNodeJSON = { type: "page-number" };
62174
- const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
62175
- if (dispatch) {
62176
- tr.replaceSelectionWith(pageNumberNode, false);
62177
- tr.setMeta("forceUpdatePagination", true);
62178
- }
62179
- return true;
62180
- }
62181
- };
62182
- },
62183
- addShortcuts() {
62184
- return {
62185
- "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
62186
- };
62187
- }
62188
- });
62189
- const TotalPageCount = Node$1.create({
62190
- name: "total-page-number",
62191
- group: "inline",
62192
- inline: true,
62193
- atom: true,
62194
- draggable: false,
62195
- selectable: false,
62196
- content: "text*",
62197
- addOptions() {
62198
- return {
62199
- htmlAttributes: {
62200
- contenteditable: false,
62201
- "data-id": "auto-total-pages",
62202
- "aria-label": "Total page count node",
62203
- class: "sd-editor-auto-total-pages"
62204
- }
62205
- };
62206
- },
62207
- addAttributes() {
62208
- return {
62209
- marksAsAttrs: {
62210
- default: null,
62211
- rendered: false
62212
- }
62213
- };
62214
- },
62215
- addNodeView() {
62216
- return ({ node, editor, getPos, decorations }) => {
62217
- const htmlAttributes = this.options.htmlAttributes;
62218
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
62219
- };
62220
- },
62221
- parseDOM() {
62222
- return [{ tag: 'span[data-id="auto-total-pages"' }];
62223
- },
62224
- renderDOM({ htmlAttributes }) {
62225
- return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62226
- },
62227
- addCommands() {
62228
- return {
62229
- /**
62230
- * Insert total page count
62231
- * @category Command
62232
- * @returns {Function} Command function
62233
- * @example
62234
- * editor.commands.addTotalPageCount()
62235
- * @note Only works in header/footer contexts
62236
- */
62237
- addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
62238
- const { options } = editor;
62239
- if (!options.isHeaderOrFooter) return false;
62240
- const { schema } = state2;
62241
- const pageNumberType = schema.nodes?.["total-page-number"];
62242
- if (!pageNumberType) return false;
62243
- const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
62244
- const pageNumberNode = {
62245
- type: "total-page-number",
62246
- content: [{ type: "text", text: String(currentPages) }]
62247
- };
62248
- const pageNode = schema.nodeFromJSON(pageNumberNode);
62249
- if (dispatch) {
62250
- tr.replaceSelectionWith(pageNode, false);
62251
- }
62252
- return true;
62253
- }
62254
- };
62255
- },
62256
- addShortcuts() {
62257
- return {
62258
- "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
62259
- };
62260
- }
62261
- });
62262
- const getNodeAttributes = (nodeName, editor) => {
62263
- switch (nodeName) {
62264
- case "page-number":
62265
- return {
62266
- text: editor.options.currentPageNumber || "1",
62267
- className: "sd-editor-auto-page-number",
62268
- dataId: "auto-page-number",
62269
- ariaLabel: "Page number node"
62270
- };
62271
- case "total-page-number":
62272
- return {
62273
- text: editor.options.parentEditor?.currentTotalPages || "1",
62274
- className: "sd-editor-auto-total-pages",
62275
- dataId: "auto-total-pages",
62276
- ariaLabel: "Total page count node"
62277
- };
62278
- default:
62279
- return {};
62280
- }
62281
- };
62282
- class AutoPageNumberNodeView {
62283
- constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
62284
- __privateAdd$1(this, _AutoPageNumberNodeView_instances);
62285
- this.node = node;
62286
- this.editor = editor;
62287
- this.view = editor.view;
62288
- this.getPos = getPos;
62289
- this.editor = editor;
62290
- this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
62291
- }
62292
- update(node) {
62293
- const incomingType = node?.type?.name;
62294
- const currentType = this.node?.type?.name;
62295
- if (!incomingType || incomingType !== currentType) return false;
62296
- this.node = node;
62297
- return true;
62298
- }
62299
- }
62300
- _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
62301
- renderDom_fn = function(node, htmlAttributes) {
62302
- const attrs = getNodeAttributes(this.node.type.name, this.editor);
62303
- const content = document.createTextNode(String(attrs.text));
62304
- const nodeContent = document.createElement("span");
62305
- nodeContent.className = attrs.className;
62306
- nodeContent.setAttribute("data-id", attrs.dataId);
62307
- nodeContent.setAttribute("aria-label", attrs.ariaLabel);
62308
- const currentPos = this.getPos();
62309
- const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
62310
- __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
62311
- Object.assign(nodeContent.style, styles);
62312
- nodeContent.appendChild(content);
62313
- Object.entries(htmlAttributes).forEach(([key2, value]) => {
62314
- if (value) nodeContent.setAttribute(key2, value);
62315
- });
62316
- return nodeContent;
62317
- };
62318
- scheduleUpdateNodeStyle_fn = function(pos, marks) {
62319
- setTimeout(() => {
62320
- const { state: state2 } = this.editor;
62321
- const { dispatch } = this.view;
62322
- const node = state2.doc.nodeAt(pos);
62323
- if (!node || node.isText) return;
62324
- const currentMarks = node.attrs.marksAsAttrs || [];
62325
- const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
62326
- const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
62327
- if (isEqual) return;
62328
- const newAttrs = {
62329
- ...node.attrs,
62330
- marksAsAttrs: newMarks
62331
- };
62332
- const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
62333
- dispatch(tr);
62334
- }, 0);
62335
- };
62336
- const getMarksFromNeighbors = (currentPos, view) => {
62337
- const $pos = view.state.doc.resolve(currentPos);
62338
- const styles = {};
62339
- const marks = [];
62340
- const before = $pos.nodeBefore;
62341
- if (before) {
62342
- Object.assign(styles, processMarks(before.marks));
62343
- marks.push(...before.marks);
62344
- }
62345
- const after = $pos.nodeAfter;
62346
- if (after) {
62347
- Object.assign(styles, { ...styles, ...processMarks(after.marks) });
62348
- marks.push(...after.marks);
62349
- }
62350
- return {
62351
- styles,
62352
- marks
62353
- };
62354
- };
62355
- const processMarks = (marks) => {
62356
- const styles = {};
62357
- marks.forEach((mark) => {
62358
- const { type: type2, attrs } = mark;
62359
- switch (type2.name) {
62360
- case "textStyle":
62361
- if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
62362
- if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
62363
- if (attrs.color) styles["color"] = attrs.color;
62364
- if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
62365
- break;
62366
- case "bold":
62367
- styles["font-weight"] = "bold";
62368
- break;
62369
- case "italic":
62370
- styles["font-style"] = "italic";
62371
- break;
62372
- case "underline":
62373
- styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
62374
- break;
62375
- case "strike":
62376
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
62377
- break;
62378
- default:
62379
- if (attrs?.style) {
62380
- Object.entries(attrs.style).forEach(([key2, value]) => {
62381
- styles[key2] = value;
62382
- });
62383
- }
62384
- break;
62385
- }
62386
- });
62387
- return styles;
62388
- };
62389
- const ShapeContainer = Node$1.create({
62390
- name: "shapeContainer",
62391
- group: "block",
62392
- content: "block+",
62393
- isolating: true,
62394
- addOptions() {
62395
- return {
62396
- htmlAttributes: {
62397
- class: "sd-editor-shape-container",
62398
- "aria-label": "Shape container node"
62399
- }
62400
- };
62401
- },
62402
- addAttributes() {
62403
- return {
62404
- fillcolor: {
62405
- renderDOM: (attrs) => {
62406
- if (!attrs.fillcolor) return {};
62407
- return {
62408
- style: `background-color: ${attrs.fillcolor}`
62409
- };
62410
- }
62411
- },
62412
- sdBlockId: {
62413
- default: null,
62414
- keepOnSplit: false,
62415
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62416
- renderDOM: (attrs) => {
62417
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62418
- }
62419
- },
62420
- style: {
62421
- renderDOM: (attrs) => {
62422
- if (!attrs.style) return {};
62423
- return {
62424
- style: attrs.style
62425
- };
62426
- }
62427
- },
62428
- wrapAttributes: {
62429
- rendered: false
62430
- },
62431
- attributes: {
62432
- rendered: false
62433
- }
62434
- };
62435
- },
62436
- parseDOM() {
62437
- return [
62438
- {
62439
- tag: `div[data-type="${this.name}"]`
62440
- }
62441
- ];
62442
- },
62443
- renderDOM({ htmlAttributes }) {
62444
- return [
62445
- "div",
62446
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62447
- 0
62448
- ];
62449
- }
62450
- });
62451
- const ShapeTextbox = Node$1.create({
62452
- name: "shapeTextbox",
62453
- group: "block",
62454
- content: "paragraph* block*",
62455
- isolating: true,
62456
- addOptions() {
62457
- return {
62458
- htmlAttributes: {
62459
- class: "sd-editor-shape-textbox",
62460
- "aria-label": "Shape textbox node"
62461
- }
62462
- };
62463
- },
62464
- addAttributes() {
62465
- return {
62466
- sdBlockId: {
62467
- default: null,
62468
- keepOnSplit: false,
62469
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62470
- renderDOM: (attrs) => {
62471
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62472
- }
62473
- },
62474
- attributes: {
62475
- rendered: false
62476
- }
62477
- };
62478
- },
62479
- parseDOM() {
62480
- return [
62481
- {
62482
- tag: `div[data-type="${this.name}"]`
62483
- }
62484
- ];
62485
- },
62486
- renderDOM({ htmlAttributes }) {
62487
- return [
62488
- "div",
62489
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62490
- 0
62491
- ];
62492
- }
62493
- });
62494
- const ContentBlock = Node$1.create({
62495
- name: "contentBlock",
62496
- group: "inline",
62497
- content: "",
62498
- isolating: true,
62499
- atom: true,
62500
- inline: true,
62501
- addOptions() {
62502
- return {
62503
- htmlAttributes: {
62504
- contenteditable: false
62505
- }
62506
- };
62507
- },
62508
- addAttributes() {
62509
- return {
62510
- horizontalRule: {
62511
- default: false,
62512
- renderDOM: ({ horizontalRule }) => {
62513
- if (!horizontalRule) return {};
62514
- return { "data-horizontal-rule": "true" };
62515
- }
62516
- },
62517
- size: {
62518
- default: null,
62519
- renderDOM: ({ size: size2 }) => {
62520
- if (!size2) return {};
62521
- let style2 = "";
62522
- if (size2.top) style2 += `top: ${size2.top}px; `;
62523
- if (size2.left) style2 += `left: ${size2.left}px; `;
62524
- if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
62525
- if (size2.height)
62526
- style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
62527
- return { style: style2 };
62528
- }
62529
- },
62530
- background: {
62531
- default: null,
62532
- renderDOM: (attrs) => {
62533
- if (!attrs.background) return {};
62534
- return {
62535
- style: `background-color: ${attrs.background}`
62536
- };
62537
- }
62538
- },
62539
- drawingContent: {
62540
- rendered: false
62541
- },
62542
- attributes: {
63161
+ addAttributes() {
63162
+ return {
63163
+ marksAsAttrs: {
63164
+ default: null,
62543
63165
  rendered: false
62544
63166
  }
62545
63167
  };
62546
63168
  },
63169
+ addNodeView() {
63170
+ return ({ node, editor, getPos, decorations }) => {
63171
+ const htmlAttributes = this.options.htmlAttributes;
63172
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
63173
+ };
63174
+ },
62547
63175
  parseDOM() {
62548
- return [
62549
- {
62550
- tag: `div[data-type="${this.name}"]`
62551
- }
62552
- ];
63176
+ return [{ tag: 'span[data-id="auto-page-number"' }];
62553
63177
  },
62554
63178
  renderDOM({ htmlAttributes }) {
62555
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63179
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
62556
63180
  },
62557
63181
  addCommands() {
62558
63182
  return {
62559
63183
  /**
62560
- * Insert a horizontal rule
62561
- * @category Command
62562
- * @example
62563
- * editor.commands.insertHorizontalRule()
62564
- * @note Creates a visual separator between content sections
62565
- */
62566
- insertHorizontalRule: () => ({ commands: commands2 }) => {
62567
- return commands2.insertContent({
62568
- type: this.name,
62569
- attrs: {
62570
- horizontalRule: true,
62571
- size: { width: "100%", height: 2 },
62572
- background: "#e5e7eb"
62573
- }
62574
- });
62575
- },
62576
- /**
62577
- * Insert a content block
63184
+ * Insert an automatic page number
62578
63185
  * @category Command
62579
- * @param {ContentBlockConfig} config - Block configuration
62580
- * @example
62581
- * // Insert a spacer block
62582
- * editor.commands.insertContentBlock({ size: { height: 20 } })
62583
- *
63186
+ * @returns {Function} Command function
62584
63187
  * @example
62585
- * // Insert a colored divider
62586
- * editor.commands.insertContentBlock({
62587
- * size: { width: '50%', height: 3 },
62588
- * background: '#3b82f6'
62589
- * })
62590
- * @note Used for spacing, dividers, and special inline content
63188
+ * editor.commands.addAutoPageNumber()
63189
+ * @note Only works in header/footer contexts
62591
63190
  */
62592
- insertContentBlock: (config2) => ({ commands: commands2 }) => {
62593
- return commands2.insertContent({
62594
- type: this.name,
62595
- attrs: config2
62596
- });
63191
+ addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
63192
+ const { options } = editor;
63193
+ if (!options.isHeaderOrFooter) return false;
63194
+ const { schema } = state2;
63195
+ const pageNumberType = schema?.nodes?.["page-number"];
63196
+ if (!pageNumberType) return false;
63197
+ const pageNumberNodeJSON = { type: "page-number" };
63198
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
63199
+ if (dispatch) {
63200
+ tr.replaceSelectionWith(pageNumberNode, false);
63201
+ tr.setMeta("forceUpdatePagination", true);
63202
+ }
63203
+ return true;
62597
63204
  }
62598
63205
  };
63206
+ },
63207
+ addShortcuts() {
63208
+ return {
63209
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
63210
+ };
62599
63211
  }
62600
63212
  });
62601
- class StructuredContentViewBase {
62602
- constructor(props) {
62603
- __publicField$1(this, "node");
62604
- __publicField$1(this, "view");
62605
- __publicField$1(this, "getPos");
62606
- __publicField$1(this, "decorations");
62607
- __publicField$1(this, "innerDecorations");
62608
- __publicField$1(this, "editor");
62609
- __publicField$1(this, "extension");
62610
- __publicField$1(this, "htmlAttributes");
62611
- __publicField$1(this, "root");
62612
- __publicField$1(this, "isDragging", false);
62613
- this.node = props.node;
62614
- this.view = props.editor.view;
62615
- this.getPos = props.getPos;
62616
- this.decorations = props.decorations;
62617
- this.innerDecorations = props.innerDecorations;
62618
- this.editor = props.editor;
62619
- this.extension = props.extension;
62620
- this.htmlAttributes = props.htmlAttributes;
62621
- this.mount(props);
62622
- }
62623
- mount() {
62624
- return;
62625
- }
62626
- get dom() {
62627
- return this.root;
62628
- }
62629
- get contentDOM() {
62630
- return null;
62631
- }
62632
- update(node, decorations, innerDecorations) {
62633
- if (node.type !== this.node.type) {
62634
- return false;
62635
- }
62636
- this.node = node;
62637
- this.decorations = decorations;
62638
- this.innerDecorations = innerDecorations;
62639
- this.updateHTMLAttributes();
62640
- return true;
62641
- }
62642
- stopEvent(event) {
62643
- if (!this.dom) return false;
62644
- const target = event.target;
62645
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
62646
- if (!isInElement) return false;
62647
- const isDragEvent = event.type.startsWith("drag");
62648
- const isDropEvent = event.type === "drop";
62649
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
62650
- if (isInput && !isDropEvent && !isDragEvent) return true;
62651
- const { isEditable } = this.editor;
62652
- const { isDragging } = this;
62653
- const isDraggable = !!this.node.type.spec.draggable;
62654
- const isSelectable = NodeSelection.isSelectable(this.node);
62655
- const isCopyEvent = event.type === "copy";
62656
- const isPasteEvent = event.type === "paste";
62657
- const isCutEvent = event.type === "cut";
62658
- const isClickEvent = event.type === "mousedown";
62659
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
62660
- event.preventDefault();
62661
- }
62662
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
62663
- event.preventDefault();
62664
- return false;
62665
- }
62666
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
62667
- const dragHandle = target.closest("[data-drag-handle]");
62668
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
62669
- if (isValidDragHandle) {
62670
- this.isDragging = true;
62671
- document.addEventListener(
62672
- "dragend",
62673
- () => {
62674
- this.isDragging = false;
62675
- },
62676
- { once: true }
62677
- );
62678
- document.addEventListener(
62679
- "drop",
62680
- () => {
62681
- this.isDragging = false;
62682
- },
62683
- { once: true }
62684
- );
62685
- document.addEventListener(
62686
- "mouseup",
62687
- () => {
62688
- this.isDragging = false;
62689
- },
62690
- { once: true }
62691
- );
62692
- }
62693
- }
62694
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
62695
- return false;
62696
- }
62697
- return true;
62698
- }
62699
- ignoreMutation(mutation) {
62700
- if (!this.dom || !this.contentDOM) return true;
62701
- if (this.node.isLeaf || this.node.isAtom) return true;
62702
- if (mutation.type === "selection") return false;
62703
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
62704
- if (this.contentDOM.contains(mutation.target)) return false;
62705
- return true;
62706
- }
62707
- destroy() {
62708
- this.dom.remove();
62709
- this.contentDOM?.remove();
62710
- }
62711
- updateAttributes(attrs) {
62712
- const pos = this.getPos();
62713
- if (typeof pos !== "number") {
62714
- return;
62715
- }
62716
- return this.view.dispatch(
62717
- this.view.state.tr.setNodeMarkup(pos, void 0, {
62718
- ...this.node.attrs,
62719
- ...attrs
62720
- })
62721
- );
62722
- }
62723
- updateHTMLAttributes() {
62724
- const { extensionService } = this.editor;
62725
- const { attributes } = extensionService;
62726
- const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
62727
- this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
62728
- }
62729
- createDragHandle() {
62730
- const dragHandle = document.createElement("span");
62731
- dragHandle.classList.add("sd-structured-content-draggable");
62732
- dragHandle.draggable = true;
62733
- dragHandle.contentEditable = "false";
62734
- dragHandle.dataset.dragHandle = "";
62735
- const textElement = document.createElement("span");
62736
- textElement.textContent = "Structured content";
62737
- dragHandle.append(textElement);
62738
- return dragHandle;
62739
- }
62740
- onDragStart(event) {
62741
- const { view } = this.editor;
62742
- const target = event.target;
62743
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
62744
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
62745
- return;
62746
- }
62747
- let x = 0;
62748
- let y2 = 0;
62749
- if (this.dom !== dragHandle) {
62750
- const domBox = this.dom.getBoundingClientRect();
62751
- const handleBox = dragHandle.getBoundingClientRect();
62752
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
62753
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
62754
- x = handleBox.x - domBox.x + offsetX;
62755
- y2 = handleBox.y - domBox.y + offsetY;
62756
- }
62757
- event.dataTransfer?.setDragImage(this.dom, x, y2);
62758
- const pos = this.getPos();
62759
- if (typeof pos !== "number") {
62760
- return;
62761
- }
62762
- const selection = NodeSelection.create(view.state.doc, pos);
62763
- const transaction = view.state.tr.setSelection(selection);
62764
- view.dispatch(transaction);
62765
- }
62766
- }
62767
- class StructuredContentInlineView extends StructuredContentViewBase {
62768
- constructor(props) {
62769
- super(props);
62770
- }
62771
- mount() {
62772
- this.buildView();
62773
- }
62774
- get contentDOM() {
62775
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
62776
- return contentElement || null;
62777
- }
62778
- createElement() {
62779
- const element = document.createElement("span");
62780
- element.classList.add(structuredContentClass$1);
62781
- element.setAttribute("data-structured-content", "");
62782
- const contentElement = document.createElement("span");
62783
- contentElement.classList.add(structuredContentInnerClass$1);
62784
- element.append(contentElement);
62785
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62786
- updateDOMAttributes(element, { ...domAttrs });
62787
- return { element, contentElement };
62788
- }
62789
- buildView() {
62790
- const { element } = this.createElement();
62791
- const dragHandle = this.createDragHandle();
62792
- element.prepend(dragHandle);
62793
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
62794
- this.root = element;
62795
- }
62796
- updateView() {
62797
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62798
- updateDOMAttributes(this.dom, { ...domAttrs });
62799
- }
62800
- update(node, decorations, innerDecorations) {
62801
- const result = super.update(node, decorations, innerDecorations);
62802
- if (!result) return false;
62803
- this.updateView();
62804
- return true;
62805
- }
62806
- }
62807
- const structuredContentClass$1 = "sd-structured-content";
62808
- const structuredContentInnerClass$1 = "sd-structured-content__content";
62809
- const StructuredContent = Node$1.create({
62810
- name: "structuredContent",
62811
- group: "inline structuredContent",
63213
+ const TotalPageCount = Node$1.create({
63214
+ name: "total-page-number",
63215
+ group: "inline",
62812
63216
  inline: true,
62813
- content: "inline*",
62814
- isolating: true,
62815
- atom: false,
62816
- // false - has editable content.
62817
- draggable: true,
63217
+ atom: true,
63218
+ draggable: false,
63219
+ selectable: false,
63220
+ content: "text*",
62818
63221
  addOptions() {
62819
63222
  return {
62820
63223
  htmlAttributes: {
62821
- class: structuredContentClass$1,
62822
- "aria-label": "Structured content node"
63224
+ contenteditable: false,
63225
+ "data-id": "auto-total-pages",
63226
+ "aria-label": "Total page count node",
63227
+ class: "sd-editor-auto-total-pages"
62823
63228
  }
62824
63229
  };
62825
63230
  },
62826
63231
  addAttributes() {
62827
63232
  return {
62828
- id: {
63233
+ marksAsAttrs: {
62829
63234
  default: null,
62830
- parseDOM: (elem) => elem.getAttribute("data-id"),
62831
- renderDOM: (attrs) => {
62832
- if (!attrs.id) return {};
62833
- return { "data-id": attrs.id };
62834
- }
62835
- },
62836
- sdtPr: {
62837
63235
  rendered: false
62838
63236
  }
62839
63237
  };
62840
63238
  },
63239
+ addNodeView() {
63240
+ return ({ node, editor, getPos, decorations }) => {
63241
+ const htmlAttributes = this.options.htmlAttributes;
63242
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
63243
+ };
63244
+ },
62841
63245
  parseDOM() {
62842
- return [{ tag: "span[data-structured-content]" }];
63246
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
62843
63247
  },
62844
63248
  renderDOM({ htmlAttributes }) {
62845
- return [
62846
- "span",
62847
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62848
- "data-structured-content": ""
62849
- }),
62850
- 0
62851
- ];
63249
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62852
63250
  },
62853
- addNodeView() {
62854
- return (props) => {
62855
- return new StructuredContentInlineView({ ...props });
63251
+ addCommands() {
63252
+ return {
63253
+ /**
63254
+ * Insert total page count
63255
+ * @category Command
63256
+ * @returns {Function} Command function
63257
+ * @example
63258
+ * editor.commands.addTotalPageCount()
63259
+ * @note Only works in header/footer contexts
63260
+ */
63261
+ addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
63262
+ const { options } = editor;
63263
+ if (!options.isHeaderOrFooter) return false;
63264
+ const { schema } = state2;
63265
+ const pageNumberType = schema.nodes?.["total-page-number"];
63266
+ if (!pageNumberType) return false;
63267
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
63268
+ const pageNumberNode = {
63269
+ type: "total-page-number",
63270
+ content: [{ type: "text", text: String(currentPages) }]
63271
+ };
63272
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
63273
+ if (dispatch) {
63274
+ tr.replaceSelectionWith(pageNode, false);
63275
+ }
63276
+ return true;
63277
+ }
63278
+ };
63279
+ },
63280
+ addShortcuts() {
63281
+ return {
63282
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
62856
63283
  };
62857
63284
  }
62858
63285
  });
62859
- class StructuredContentBlockView extends StructuredContentViewBase {
62860
- constructor(props) {
62861
- super(props);
62862
- }
62863
- mount() {
62864
- this.buildView();
62865
- }
62866
- get contentDOM() {
62867
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
62868
- return contentElement || null;
62869
- }
62870
- createElement() {
62871
- const element = document.createElement("div");
62872
- element.classList.add(structuredContentClass);
62873
- element.setAttribute("data-structured-content-block", "");
62874
- const contentElement = document.createElement("div");
62875
- contentElement.classList.add(structuredContentInnerClass);
62876
- element.append(contentElement);
62877
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62878
- updateDOMAttributes(element, { ...domAttrs });
62879
- return { element, contentElement };
62880
- }
62881
- buildView() {
62882
- const { element } = this.createElement();
62883
- const dragHandle = this.createDragHandle();
62884
- element.prepend(dragHandle);
62885
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
62886
- this.root = element;
63286
+ const getNodeAttributes = (nodeName, editor) => {
63287
+ switch (nodeName) {
63288
+ case "page-number":
63289
+ return {
63290
+ text: editor.options.currentPageNumber || "1",
63291
+ className: "sd-editor-auto-page-number",
63292
+ dataId: "auto-page-number",
63293
+ ariaLabel: "Page number node"
63294
+ };
63295
+ case "total-page-number":
63296
+ return {
63297
+ text: editor.options.parentEditor?.currentTotalPages || "1",
63298
+ className: "sd-editor-auto-total-pages",
63299
+ dataId: "auto-total-pages",
63300
+ ariaLabel: "Total page count node"
63301
+ };
63302
+ default:
63303
+ return {};
62887
63304
  }
62888
- updateView() {
62889
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62890
- updateDOMAttributes(this.dom, { ...domAttrs });
63305
+ };
63306
+ class AutoPageNumberNodeView {
63307
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
63308
+ __privateAdd$1(this, _AutoPageNumberNodeView_instances);
63309
+ this.node = node;
63310
+ this.editor = editor;
63311
+ this.view = editor.view;
63312
+ this.getPos = getPos;
63313
+ this.editor = editor;
63314
+ this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
62891
63315
  }
62892
- update(node, decorations, innerDecorations) {
62893
- const result = super.update(node, decorations, innerDecorations);
62894
- if (!result) return false;
62895
- this.updateView();
63316
+ update(node) {
63317
+ const incomingType = node?.type?.name;
63318
+ const currentType = this.node?.type?.name;
63319
+ if (!incomingType || incomingType !== currentType) return false;
63320
+ this.node = node;
62896
63321
  return true;
62897
63322
  }
62898
63323
  }
62899
- const structuredContentClass = "sd-structured-content-block";
62900
- const structuredContentInnerClass = "sd-structured-content-block__content";
62901
- const StructuredContentBlock = Node$1.create({
62902
- name: "structuredContentBlock",
62903
- group: "block structuredContent",
62904
- content: "block*",
63324
+ _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
63325
+ renderDom_fn = function(node, htmlAttributes) {
63326
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
63327
+ const content = document.createTextNode(String(attrs.text));
63328
+ const nodeContent = document.createElement("span");
63329
+ nodeContent.className = attrs.className;
63330
+ nodeContent.setAttribute("data-id", attrs.dataId);
63331
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
63332
+ const currentPos = this.getPos();
63333
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
63334
+ __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
63335
+ Object.assign(nodeContent.style, styles);
63336
+ nodeContent.appendChild(content);
63337
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
63338
+ if (value) nodeContent.setAttribute(key2, value);
63339
+ });
63340
+ return nodeContent;
63341
+ };
63342
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
63343
+ setTimeout(() => {
63344
+ const { state: state2 } = this.editor;
63345
+ const { dispatch } = this.view;
63346
+ const node = state2.doc.nodeAt(pos);
63347
+ if (!node || node.isText) return;
63348
+ const currentMarks = node.attrs.marksAsAttrs || [];
63349
+ const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
63350
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
63351
+ if (isEqual) return;
63352
+ const newAttrs = {
63353
+ ...node.attrs,
63354
+ marksAsAttrs: newMarks
63355
+ };
63356
+ const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
63357
+ dispatch(tr);
63358
+ }, 0);
63359
+ };
63360
+ const getMarksFromNeighbors = (currentPos, view) => {
63361
+ const $pos = view.state.doc.resolve(currentPos);
63362
+ const styles = {};
63363
+ const marks = [];
63364
+ const before = $pos.nodeBefore;
63365
+ if (before) {
63366
+ Object.assign(styles, processMarks(before.marks));
63367
+ marks.push(...before.marks);
63368
+ }
63369
+ const after = $pos.nodeAfter;
63370
+ if (after) {
63371
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
63372
+ marks.push(...after.marks);
63373
+ }
63374
+ return {
63375
+ styles,
63376
+ marks
63377
+ };
63378
+ };
63379
+ const processMarks = (marks) => {
63380
+ const styles = {};
63381
+ marks.forEach((mark) => {
63382
+ const { type: type2, attrs } = mark;
63383
+ switch (type2.name) {
63384
+ case "textStyle":
63385
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
63386
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
63387
+ if (attrs.color) styles["color"] = attrs.color;
63388
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
63389
+ break;
63390
+ case "bold":
63391
+ styles["font-weight"] = "bold";
63392
+ break;
63393
+ case "italic":
63394
+ styles["font-style"] = "italic";
63395
+ break;
63396
+ case "underline":
63397
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
63398
+ break;
63399
+ case "strike":
63400
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
63401
+ break;
63402
+ default:
63403
+ if (attrs?.style) {
63404
+ Object.entries(attrs.style).forEach(([key2, value]) => {
63405
+ styles[key2] = value;
63406
+ });
63407
+ }
63408
+ break;
63409
+ }
63410
+ });
63411
+ return styles;
63412
+ };
63413
+ const ShapeContainer = Node$1.create({
63414
+ name: "shapeContainer",
63415
+ group: "block",
63416
+ content: "block+",
62905
63417
  isolating: true,
62906
- atom: false,
62907
- // false - has editable content.
62908
- draggable: true,
62909
63418
  addOptions() {
62910
63419
  return {
62911
63420
  htmlAttributes: {
62912
- class: structuredContentClass,
62913
- "aria-label": "Structured content block node"
63421
+ class: "sd-editor-shape-container",
63422
+ "aria-label": "Shape container node"
62914
63423
  }
62915
63424
  };
62916
63425
  },
62917
63426
  addAttributes() {
62918
63427
  return {
62919
- id: {
63428
+ fillcolor: {
63429
+ renderDOM: (attrs) => {
63430
+ if (!attrs.fillcolor) return {};
63431
+ return {
63432
+ style: `background-color: ${attrs.fillcolor}`
63433
+ };
63434
+ }
63435
+ },
63436
+ sdBlockId: {
62920
63437
  default: null,
62921
- parseDOM: (elem) => elem.getAttribute("data-id"),
63438
+ keepOnSplit: false,
63439
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62922
63440
  renderDOM: (attrs) => {
62923
- if (!attrs.id) return {};
62924
- return { "data-id": attrs.id };
63441
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62925
63442
  }
62926
63443
  },
62927
- sdtPr: {
63444
+ style: {
63445
+ renderDOM: (attrs) => {
63446
+ if (!attrs.style) return {};
63447
+ return {
63448
+ style: attrs.style
63449
+ };
63450
+ }
63451
+ },
63452
+ wrapAttributes: {
63453
+ rendered: false
63454
+ },
63455
+ attributes: {
62928
63456
  rendered: false
62929
63457
  }
62930
63458
  };
62931
63459
  },
62932
63460
  parseDOM() {
62933
- return [{ tag: "div[data-structured-content-block]" }];
63461
+ return [
63462
+ {
63463
+ tag: `div[data-type="${this.name}"]`
63464
+ }
63465
+ ];
62934
63466
  },
62935
63467
  renderDOM({ htmlAttributes }) {
62936
63468
  return [
62937
63469
  "div",
62938
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62939
- "data-structured-content-block": ""
62940
- }),
63470
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62941
63471
  0
62942
63472
  ];
62943
- },
62944
- addNodeView() {
62945
- return (props) => {
62946
- return new StructuredContentBlockView({ ...props });
62947
- };
62948
63473
  }
62949
63474
  });
62950
- class DocumentSectionView {
62951
- constructor(node, getPos, decorations, editor) {
62952
- __privateAdd$1(this, _DocumentSectionView_instances);
62953
- this.node = node;
62954
- this.editor = editor;
62955
- this.decorations = decorations;
62956
- this.view = editor.view;
62957
- this.getPos = getPos;
62958
- __privateMethod$1(this, _DocumentSectionView_instances, init_fn3).call(this);
62959
- }
62960
- }
62961
- _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
62962
- init_fn3 = function() {
62963
- const { attrs } = this.node;
62964
- const { id, title, description } = attrs;
62965
- this.dom = document.createElement("div");
62966
- this.dom.className = "sd-document-section-block";
62967
- this.dom.setAttribute("data-id", id);
62968
- this.dom.setAttribute("data-title", title);
62969
- this.dom.setAttribute("data-description", description);
62970
- this.dom.setAttribute("aria-label", "Document section");
62971
- __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
62972
- this.contentDOM = document.createElement("div");
62973
- this.contentDOM.className = "sd-document-section-block-content";
62974
- this.contentDOM.setAttribute("contenteditable", "true");
62975
- this.dom.appendChild(this.contentDOM);
62976
- };
62977
- addToolTip_fn = function() {
62978
- const { title } = this.node.attrs;
62979
- this.infoDiv = document.createElement("div");
62980
- this.infoDiv.className = "sd-document-section-block-info";
62981
- const textSpan = document.createElement("span");
62982
- textSpan.textContent = title || "Document section";
62983
- this.infoDiv.appendChild(textSpan);
62984
- this.infoDiv.setAttribute("contenteditable", "false");
62985
- this.dom.appendChild(this.infoDiv);
62986
- };
62987
- const getAllSections = (editor) => {
62988
- if (!editor) return [];
62989
- const type2 = editor.schema.nodes.documentSection;
62990
- if (!type2) return [];
62991
- const sections = [];
62992
- const { state: state2 } = editor;
62993
- state2.doc.descendants((node, pos) => {
62994
- if (node.type.name === type2.name) {
62995
- sections.push({ node, pos });
62996
- }
62997
- });
62998
- return sections;
62999
- };
63000
- const exportSectionsToHTML = (editor) => {
63001
- const sections = getAllSections(editor);
63002
- const processedSections = /* @__PURE__ */ new Set();
63003
- const result = [];
63004
- sections.forEach(({ node }) => {
63005
- const { attrs } = node;
63006
- const { id, title, description } = attrs;
63007
- if (processedSections.has(id)) return;
63008
- processedSections.add(id);
63009
- const html = getHTMLFromNode(node, editor);
63010
- result.push({
63011
- id,
63012
- title,
63013
- description,
63014
- html
63015
- });
63016
- });
63017
- return result;
63018
- };
63019
- const getHTMLFromNode = (node, editor) => {
63020
- const tempDocument = document.implementation.createHTMLDocument();
63021
- const container = tempDocument.createElement("div");
63022
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
63023
- container.appendChild(fragment);
63024
- let html = container.innerHTML;
63025
- return html;
63026
- };
63027
- const exportSectionsToJSON = (editor) => {
63028
- const sections = getAllSections(editor);
63029
- const processedSections = /* @__PURE__ */ new Set();
63030
- const result = [];
63031
- sections.forEach(({ node }) => {
63032
- const { attrs } = node;
63033
- const { id, title, description } = attrs;
63034
- if (processedSections.has(id)) return;
63035
- processedSections.add(id);
63036
- result.push({
63037
- id,
63038
- title,
63039
- description,
63040
- content: node.toJSON()
63041
- });
63042
- });
63043
- return result;
63044
- };
63045
- const getLinkedSectionEditor = (id, options, editor) => {
63046
- const sections = getAllSections(editor);
63047
- const section = sections.find((s) => s.node.attrs.id === id);
63048
- if (!section) return null;
63049
- const child = editor.createChildEditor({
63050
- ...options,
63051
- onUpdate: ({ editor: childEditor, transaction }) => {
63052
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
63053
- if (isFromtLinkedParent) return;
63054
- const updatedContent = childEditor.state.doc.content;
63055
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
63056
- if (!sectionNode) return;
63057
- const { pos, node } = sectionNode;
63058
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
63059
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
63060
- tr.setMeta("fromLinkedChild", true);
63061
- editor.view.dispatch(tr);
63062
- }
63063
- });
63064
- editor.on("update", ({ transaction }) => {
63065
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
63066
- if (isFromLinkedChild) return;
63067
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
63068
- if (!sectionNode) return;
63069
- const sectionContent = sectionNode.node.content;
63070
- const json = {
63071
- type: "doc",
63072
- content: sectionContent.content.map((node) => node.toJSON())
63073
- };
63074
- const childTr = child.state.tr;
63075
- childTr.setMeta("fromLinkedParent", true);
63076
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
63077
- child.view.dispatch(childTr);
63078
- });
63079
- return child;
63080
- };
63081
- const SectionHelpers = {
63082
- getAllSections,
63083
- exportSectionsToHTML,
63084
- exportSectionsToJSON,
63085
- getLinkedSectionEditor
63086
- };
63087
- const DocumentSection = Node$1.create({
63088
- name: "documentSection",
63475
+ const ShapeTextbox = Node$1.create({
63476
+ name: "shapeTextbox",
63089
63477
  group: "block",
63090
- content: "block*",
63091
- atom: true,
63478
+ content: "paragraph* block*",
63092
63479
  isolating: true,
63093
63480
  addOptions() {
63094
63481
  return {
63095
63482
  htmlAttributes: {
63096
- class: "sd-document-section-block",
63097
- "aria-label": "Structured content block"
63483
+ class: "sd-editor-shape-textbox",
63484
+ "aria-label": "Shape textbox node"
63098
63485
  }
63099
63486
  };
63100
63487
  },
63101
- parseDOM() {
63102
- return [
63103
- {
63104
- tag: "div.sd-document-section-block",
63105
- priority: 60
63106
- }
63107
- ];
63108
- },
63109
- renderDOM({ htmlAttributes }) {
63110
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
63111
- },
63112
63488
  addAttributes() {
63113
63489
  return {
63114
- id: {},
63115
63490
  sdBlockId: {
63116
63491
  default: null,
63117
63492
  keepOnSplit: false,
@@ -63120,212 +63495,131 @@ const DocumentSection = Node$1.create({
63120
63495
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
63121
63496
  }
63122
63497
  },
63123
- title: {},
63124
- description: {},
63125
- sectionType: {},
63126
- isLocked: { default: false }
63498
+ attributes: {
63499
+ rendered: false
63500
+ }
63127
63501
  };
63128
63502
  },
63129
- addNodeView() {
63130
- return ({ node, editor, getPos, decorations }) => {
63131
- return new DocumentSectionView(node, getPos, decorations, editor);
63503
+ parseDOM() {
63504
+ return [
63505
+ {
63506
+ tag: `div[data-type="${this.name}"]`
63507
+ }
63508
+ ];
63509
+ },
63510
+ renderDOM({ htmlAttributes }) {
63511
+ return [
63512
+ "div",
63513
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
63514
+ 0
63515
+ ];
63516
+ }
63517
+ });
63518
+ const ContentBlock = Node$1.create({
63519
+ name: "contentBlock",
63520
+ group: "inline",
63521
+ content: "",
63522
+ isolating: true,
63523
+ atom: true,
63524
+ inline: true,
63525
+ addOptions() {
63526
+ return {
63527
+ htmlAttributes: {
63528
+ contenteditable: false
63529
+ }
63132
63530
  };
63133
63531
  },
63134
- addCommands() {
63532
+ addAttributes() {
63135
63533
  return {
63136
- /**
63137
- * Create a lockable content section
63138
- * @category Command
63139
- * @param {SectionCreate} [options={}] - Section configuration
63140
- * @example
63141
- * editor.commands.createDocumentSection({
63142
- * id: 1,
63143
- * title: 'Terms & Conditions',
63144
- * isLocked: true,
63145
- * html: '<p>Legal content...</p>'
63146
- * })
63147
- */
63148
- createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
63149
- const { selection } = state2;
63150
- let { from: from2, to } = selection;
63151
- let content = selection.content().content;
63152
- const { html: optionsHTML, json: optionsJSON } = options;
63153
- if (optionsHTML) {
63154
- const html = htmlHandler(optionsHTML, this.editor);
63155
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
63156
- content = doc2.content;
63157
- }
63158
- if (optionsJSON) {
63159
- content = this.editor.schema.nodeFromJSON(optionsJSON);
63160
- }
63161
- if (!content?.content?.length) {
63162
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
63163
- }
63164
- if (!options.id) {
63165
- const allSections = SectionHelpers.getAllSections(editor);
63166
- options.id = allSections.length + 1;
63167
- }
63168
- if (!options.title) {
63169
- options.title = "Document section";
63170
- }
63171
- const node = this.type.createAndFill(options, content);
63172
- if (!node) return false;
63173
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
63174
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
63175
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
63176
- from2 = insertPos2;
63177
- to = insertPos2;
63178
- }
63179
- tr.replaceRangeWith(from2, to, node);
63180
- const nodeEnd = from2 + node.nodeSize;
63181
- let shouldInsertParagraph = true;
63182
- let insertPos = nodeEnd;
63183
- if (nodeEnd >= tr.doc.content.size) {
63184
- insertPos = tr.doc.content.size;
63185
- if (insertPos > 0) {
63186
- const $endPos = tr.doc.resolve(insertPos);
63187
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
63188
- shouldInsertParagraph = false;
63189
- }
63190
- }
63191
- }
63192
- if (shouldInsertParagraph) {
63193
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
63194
- tr.insert(insertPos, emptyParagraph);
63195
- }
63196
- if (dispatch) {
63197
- tr.setMeta("documentSection", { action: "create" });
63198
- dispatch(tr);
63199
- setTimeout(() => {
63200
- try {
63201
- const currentState = editor.state;
63202
- const docSize = currentState.doc.content.size;
63203
- let targetPos = from2 + node.nodeSize;
63204
- if (shouldInsertParagraph) {
63205
- targetPos += 1;
63206
- }
63207
- targetPos = Math.min(targetPos, docSize);
63208
- if (targetPos < docSize && targetPos > 0) {
63209
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
63210
- const newTr = currentState.tr.setSelection(newSelection);
63211
- editor.view.dispatch(newTr);
63212
- }
63213
- } catch (e) {
63214
- console.warn("Could not set delayed selection:", e);
63215
- }
63216
- }, 0);
63534
+ horizontalRule: {
63535
+ default: false,
63536
+ renderDOM: ({ horizontalRule }) => {
63537
+ if (!horizontalRule) return {};
63538
+ return { "data-horizontal-rule": "true" };
63217
63539
  }
63218
- return true;
63219
63540
  },
63220
- /**
63221
- * Remove section wrapper at cursor, preserving its content
63222
- * @category Command
63223
- * @example
63224
- * editor.commands.removeSectionAtSelection()
63225
- * @note Content stays in document, only section wrapper is removed
63226
- */
63227
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
63228
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
63229
- if (!sdtNode) return false;
63230
- const { node, pos } = sdtNode;
63231
- const nodeStart = pos;
63232
- const nodeEnd = nodeStart + node.nodeSize;
63233
- const contentToPreserve = node.content;
63234
- tr.delete(nodeStart, nodeEnd);
63235
- if (contentToPreserve.size > 0) {
63236
- tr.insert(nodeStart, contentToPreserve);
63237
- }
63238
- const newPos = Math.min(nodeStart, tr.doc.content.size);
63239
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
63240
- if (dispatch) {
63241
- tr.setMeta("documentSection", { action: "delete" });
63242
- dispatch(tr);
63541
+ size: {
63542
+ default: null,
63543
+ renderDOM: ({ size: size2 }) => {
63544
+ if (!size2) return {};
63545
+ let style2 = "";
63546
+ if (size2.top) style2 += `top: ${size2.top}px; `;
63547
+ if (size2.left) style2 += `left: ${size2.left}px; `;
63548
+ if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
63549
+ if (size2.height)
63550
+ style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
63551
+ return { style: style2 };
63243
63552
  }
63244
- return true;
63245
63553
  },
63246
- /**
63247
- * Delete section and all its content
63248
- * @category Command
63249
- * @param {number} id - Section to delete
63250
- * @example
63251
- * editor.commands.removeSectionById(123)
63252
- */
63253
- removeSectionById: (id) => ({ tr, dispatch }) => {
63254
- const sections = SectionHelpers.getAllSections(this.editor);
63255
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
63256
- if (!sectionToRemove) return false;
63257
- const { pos, node } = sectionToRemove;
63258
- const nodeStart = pos;
63259
- const nodeEnd = nodeStart + node.nodeSize;
63260
- tr.delete(nodeStart, nodeEnd);
63261
- if (dispatch) {
63262
- tr.setMeta("documentSection", { action: "delete", id });
63263
- dispatch(tr);
63554
+ background: {
63555
+ default: null,
63556
+ renderDOM: (attrs) => {
63557
+ if (!attrs.background) return {};
63558
+ return {
63559
+ style: `background-color: ${attrs.background}`
63560
+ };
63264
63561
  }
63265
- return true;
63266
63562
  },
63563
+ drawingContent: {
63564
+ rendered: false
63565
+ },
63566
+ attributes: {
63567
+ rendered: false
63568
+ }
63569
+ };
63570
+ },
63571
+ parseDOM() {
63572
+ return [
63573
+ {
63574
+ tag: `div[data-type="${this.name}"]`
63575
+ }
63576
+ ];
63577
+ },
63578
+ renderDOM({ htmlAttributes }) {
63579
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63580
+ },
63581
+ addCommands() {
63582
+ return {
63267
63583
  /**
63268
- * Lock section against edits
63584
+ * Insert a horizontal rule
63269
63585
  * @category Command
63270
- * @param {number} id - Section to lock
63271
63586
  * @example
63272
- * editor.commands.lockSectionById(123)
63587
+ * editor.commands.insertHorizontalRule()
63588
+ * @note Creates a visual separator between content sections
63273
63589
  */
63274
- lockSectionById: (id) => ({ tr, dispatch }) => {
63275
- const sections = SectionHelpers.getAllSections(this.editor);
63276
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
63277
- if (!sectionToLock) return false;
63278
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
63279
- if (dispatch) {
63280
- tr.setMeta("documentSection", { action: "lock", id });
63281
- dispatch(tr);
63282
- }
63283
- return true;
63590
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
63591
+ return commands2.insertContent({
63592
+ type: this.name,
63593
+ attrs: {
63594
+ horizontalRule: true,
63595
+ size: { width: "100%", height: 2 },
63596
+ background: "#e5e7eb"
63597
+ }
63598
+ });
63284
63599
  },
63285
63600
  /**
63286
- * Modify section attributes or content
63601
+ * Insert a content block
63287
63602
  * @category Command
63288
- * @param {SectionUpdate} options - Changes to apply
63603
+ * @param {ContentBlockConfig} config - Block configuration
63289
63604
  * @example
63290
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
63291
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
63292
- * editor.commands.updateSectionById({
63293
- * id: 123,
63294
- * html: '<p>Updated</p>',
63295
- * attrs: { title: 'New Title' }
63605
+ * // Insert a spacer block
63606
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
63607
+ *
63608
+ * @example
63609
+ * // Insert a colored divider
63610
+ * editor.commands.insertContentBlock({
63611
+ * size: { width: '50%', height: 3 },
63612
+ * background: '#3b82f6'
63296
63613
  * })
63614
+ * @note Used for spacing, dividers, and special inline content
63297
63615
  */
63298
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
63299
- const sections = SectionHelpers.getAllSections(editor || this.editor);
63300
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
63301
- if (!sectionToUpdate) return false;
63302
- const { pos, node } = sectionToUpdate;
63303
- let newContent = null;
63304
- if (html) {
63305
- const htmlDoc = htmlHandler(html, editor || this.editor);
63306
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
63307
- newContent = doc2.content;
63308
- }
63309
- if (json) {
63310
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
63311
- }
63312
- if (!newContent) {
63313
- newContent = node.content;
63314
- }
63315
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
63316
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
63317
- if (dispatch) {
63318
- tr.setMeta("documentSection", { action: "update", id, attrs });
63319
- dispatch(tr);
63320
- }
63321
- return true;
63616
+ insertContentBlock: (config2) => ({ commands: commands2 }) => {
63617
+ return commands2.insertContent({
63618
+ type: this.name,
63619
+ attrs: config2
63620
+ });
63322
63621
  }
63323
63622
  };
63324
- },
63325
- addHelpers() {
63326
- return {
63327
- ...SectionHelpers
63328
- };
63329
63623
  }
63330
63624
  });
63331
63625
  const { findChildren } = helpers;
@@ -70175,6 +70469,7 @@ const getStarterExtensions = () => {
70175
70469
  Search,
70176
70470
  StructuredContent,
70177
70471
  StructuredContentBlock,
70472
+ StructuredContentCommands,
70178
70473
  DocumentSection,
70179
70474
  NodeResizer,
70180
70475
  CustomSelection,