@harbour-enterprises/superdoc 0.22.0-next.9 → 0.22.1

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