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