@estiva-app/ui 0.65.3 → 0.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/BlockNodes.d.ts +33 -0
  2. package/dist/BlockNodes.d.ts.map +1 -1
  3. package/dist/ComposerTriggers.d.ts +11 -2
  4. package/dist/ComposerTriggers.d.ts.map +1 -1
  5. package/dist/EditorTableControls.d.ts +38 -0
  6. package/dist/EditorTableControls.d.ts.map +1 -0
  7. package/dist/NavItem.d.ts +11 -1
  8. package/dist/NavItem.d.ts.map +1 -1
  9. package/dist/RichText.d.ts +13 -1
  10. package/dist/RichText.d.ts.map +1 -1
  11. package/dist/editor.d.ts +2 -1
  12. package/dist/editor.d.ts.map +1 -1
  13. package/dist/editor.js +363 -18
  14. package/dist/editor.js.map +4 -4
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +71 -23
  18. package/dist/index.js.map +3 -3
  19. package/package.json +2 -1
  20. package/registry.json +209 -4
  21. package/src/BlockNodes.mdx +32 -1
  22. package/src/BlockNodes.stories.tsx +29 -1
  23. package/src/BlockNodes.test.tsx +141 -2
  24. package/src/BlockNodes.tsx +227 -4
  25. package/src/ComposerTriggers.test.tsx +23 -0
  26. package/src/ComposerTriggers.tsx +61 -5
  27. package/src/EditorTableControls.mdx +78 -0
  28. package/src/EditorTableControls.stories.tsx +63 -0
  29. package/src/EditorTableControls.test.tsx +288 -0
  30. package/src/EditorTableControls.tsx +255 -0
  31. package/src/NavItem.mdx +10 -0
  32. package/src/NavItem.stories.tsx +23 -1
  33. package/src/NavItem.test.tsx +36 -0
  34. package/src/NavItem.tsx +40 -16
  35. package/src/RichText.mdx +12 -0
  36. package/src/RichText.stories.tsx +50 -0
  37. package/src/RichText.test.tsx +56 -0
  38. package/src/RichText.tsx +83 -10
  39. package/src/editor.ts +4 -0
  40. package/src/index.ts +1 -1
  41. package/src/registry/registry.test.ts +7 -7
  42. package/src/registry/siblings.test.ts +2 -2
  43. package/stories/Choosing.mdx +1 -0
package/dist/editor.js CHANGED
@@ -580,6 +580,155 @@ function EditorBlockHandle({ editor, turnInto = [], actions, children, className
580
580
  return /* @__PURE__ */ jsx3(BlockHandle, { editing, actions, className, children });
581
581
  }
582
582
 
583
+ // src/EditorTableControls.tsx
584
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
585
+ import { TextSelection } from "@tiptap/pm/state";
586
+ import { addColumnAfter, addColumnBefore, addRowAfter, addRowBefore, deleteColumn, deleteRow, deleteTable, toggleHeader } from "@tiptap/pm/tables";
587
+ import { IconColumnInsertLeft, IconColumnInsertRight, IconGripHorizontal, IconGripVertical, IconPlus, IconRowInsertBottom, IconRowInsertTop, IconTrash as IconTrash2 } from "@tabler/icons-react";
588
+ import { cn } from "@estiva-app/ui";
589
+ import { IconButton } from "@estiva-app/ui";
590
+
591
+ // src/looks.ts
592
+ var FLOATING_SURFACE_CLASSES = "rounded-lg border border-border-default bg-bg-elevated shadow-lg";
593
+
594
+ // src/EditorTableControls.tsx
595
+ import { Menu, MenuItem as MenuItem2, MenuSeparator } from "@estiva-app/ui";
596
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
597
+ function inCell(editor, cell, command) {
598
+ const { view } = editor;
599
+ if (!view.dom.contains(cell)) return false;
600
+ const at = view.posAtDOM(cell, 0);
601
+ view.dispatch(view.state.tr.setSelection(TextSelection.near(view.state.doc.resolve(at))));
602
+ const done = command(view.state, view.dispatch, view);
603
+ editor.commands.focus();
604
+ return done;
605
+ }
606
+ function isHeaderRow(row) {
607
+ return row.cells.length > 0 && Array.from(row.cells).every((c) => c.tagName === "TH");
608
+ }
609
+ function deleteTableRow(editor, cell) {
610
+ const row = cell.closest("tr");
611
+ const table = cell.closest("table");
612
+ if (!row || !table || table.rows.length <= 1) return inCell(editor, cell, deleteTable);
613
+ const header = isHeaderRow(row);
614
+ return inCell(editor, cell, header ? (state, dispatch, view) => deleteRow(state, dispatch) && (!view || toggleHeader("row")(view.state, view.dispatch)) : deleteRow);
615
+ }
616
+ function deleteTableColumn(editor, cell) {
617
+ const row = cell.closest("tr");
618
+ return inCell(editor, cell, row && row.cells.length > 1 ? deleteColumn : deleteTable);
619
+ }
620
+ function appendTableRow(editor, table) {
621
+ const last = table.rows[table.rows.length - 1]?.cells[0];
622
+ return !!last && inCell(editor, last, addRowAfter);
623
+ }
624
+ function appendTableColumn(editor, table) {
625
+ const first = table.rows[0];
626
+ const last = first?.cells[first.cells.length - 1];
627
+ return !!last && inCell(editor, last, addColumnAfter);
628
+ }
629
+ function place(frame, { table, row, cell }) {
630
+ const at = frame.getBoundingClientRect();
631
+ const t = table.getBoundingClientRect();
632
+ const r = row.getBoundingClientRect();
633
+ const c = cell.getBoundingClientRect();
634
+ return {
635
+ tableBox: { top: t.top - at.top, left: t.left - at.left, width: t.width, height: t.height },
636
+ rowBox: { top: r.top - at.top, height: r.height },
637
+ columnBox: { left: c.left - at.left, width: c.width }
638
+ };
639
+ }
640
+ var ICON = { size: 16, stroke: 1.5, className: "text-text-secondary" };
641
+ var keepFocus = (e) => e.preventDefault();
642
+ var HANDLE_BOX = cn(FLOATING_SURFACE_CLASSES, "shadow-none");
643
+ function EditorTableControls({ editor, children, className }) {
644
+ const frame = useRef3(null);
645
+ const [hovered, setHovered] = useState2(null);
646
+ const [menu, setMenu] = useState2(null);
647
+ const editable = !!editor && editor.isEditable;
648
+ const follow = useCallback2(
649
+ (target) => {
650
+ if (!editor || !frame.current || !(target instanceof Element)) return;
651
+ if (target.closest("[data-table-control]")) return;
652
+ const cell = target.closest("td, th");
653
+ const row = cell?.closest("tr");
654
+ const table = row?.closest("table");
655
+ if (!cell || !row || !table || !editor.view.dom.contains(table)) {
656
+ setHovered(null);
657
+ return;
658
+ }
659
+ const next = { table, row, cell, ...place(frame.current, { table, row, cell }) };
660
+ setHovered((was) => was && was.cell === cell && JSON.stringify([was.tableBox, was.rowBox, was.columnBox]) === JSON.stringify([next.tableBox, next.rowBox, next.columnBox]) ? was : next);
661
+ },
662
+ [editor]
663
+ );
664
+ useEffect2(() => {
665
+ if (!editor) return;
666
+ const changed = ({ transaction }) => {
667
+ if (transaction.docChanged && !menu) setHovered(null);
668
+ };
669
+ editor.on("transaction", changed);
670
+ return () => {
671
+ editor.off("transaction", changed);
672
+ };
673
+ }, [editor, menu]);
674
+ const shown = editable ? hovered : null;
675
+ const run = (act) => () => {
676
+ if (editor) act();
677
+ setMenu(null);
678
+ setHovered(null);
679
+ };
680
+ return /* @__PURE__ */ jsxs3(
681
+ "div",
682
+ {
683
+ ref: frame,
684
+ className: cn("relative", className),
685
+ onMouseMove: (e) => {
686
+ if (!menu) follow(e.target);
687
+ },
688
+ onMouseLeave: () => {
689
+ if (!menu) setHovered(null);
690
+ },
691
+ children: [
692
+ children,
693
+ shown && editor && /* @__PURE__ */ jsxs3(Fragment2, { children: [
694
+ /* @__PURE__ */ jsx4("div", { "data-table-control": true, className: cn(HANDLE_BOX, "absolute -translate-x-1/2 -translate-y-1/2"), style: { left: shown.tableBox.left, top: shown.rowBox.top + shown.rowBox.height / 2 }, children: /* @__PURE__ */ jsxs3(
695
+ Menu,
696
+ {
697
+ open: menu === "row",
698
+ onOpenChange: (open) => setMenu(open ? "row" : null),
699
+ trigger: /* @__PURE__ */ jsx4(IconButton, { "aria-label": "Row menu", tooltip: "Row", className: "h-6 w-4 p-0", children: /* @__PURE__ */ jsx4(IconGripVertical, { size: 16, stroke: 1.5 }) }),
700
+ children: [
701
+ !isHeaderRow(shown.row) && /* @__PURE__ */ jsx4(MenuItem2, { label: "Insert above", leading: /* @__PURE__ */ jsx4(IconRowInsertTop, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addRowBefore)) }),
702
+ /* @__PURE__ */ jsx4(MenuItem2, { label: "Insert below", leading: /* @__PURE__ */ jsx4(IconRowInsertBottom, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addRowAfter)) }),
703
+ /* @__PURE__ */ jsx4(MenuSeparator, {}),
704
+ /* @__PURE__ */ jsx4(MenuItem2, { label: "Delete", destructive: true, leading: /* @__PURE__ */ jsx4(IconTrash2, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: run(() => deleteTableRow(editor, shown.cell)) }),
705
+ /* @__PURE__ */ jsx4(MenuItem2, { label: "Delete table", destructive: true, leading: /* @__PURE__ */ jsx4(IconTrash2, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: run(() => inCell(editor, shown.cell, deleteTable)) })
706
+ ]
707
+ }
708
+ ) }),
709
+ /* @__PURE__ */ jsx4("div", { "data-table-control": true, className: cn(HANDLE_BOX, "absolute -translate-x-1/2 -translate-y-1/2"), style: { left: shown.columnBox.left + shown.columnBox.width / 2, top: shown.tableBox.top }, children: /* @__PURE__ */ jsxs3(
710
+ Menu,
711
+ {
712
+ open: menu === "column",
713
+ onOpenChange: (open) => setMenu(open ? "column" : null),
714
+ trigger: /* @__PURE__ */ jsx4(IconButton, { "aria-label": "Column menu", tooltip: "Column", className: "h-4 w-6 p-0", children: /* @__PURE__ */ jsx4(IconGripHorizontal, { size: 16, stroke: 1.5 }) }),
715
+ children: [
716
+ /* @__PURE__ */ jsx4(MenuItem2, { label: "Insert left", leading: /* @__PURE__ */ jsx4(IconColumnInsertLeft, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addColumnBefore)) }),
717
+ /* @__PURE__ */ jsx4(MenuItem2, { label: "Insert right", leading: /* @__PURE__ */ jsx4(IconColumnInsertRight, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addColumnAfter)) }),
718
+ /* @__PURE__ */ jsx4(MenuSeparator, {}),
719
+ /* @__PURE__ */ jsx4(MenuItem2, { label: "Delete", destructive: true, leading: /* @__PURE__ */ jsx4(IconTrash2, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: run(() => deleteTableColumn(editor, shown.cell)) }),
720
+ /* @__PURE__ */ jsx4(MenuItem2, { label: "Delete table", destructive: true, leading: /* @__PURE__ */ jsx4(IconTrash2, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: run(() => inCell(editor, shown.cell, deleteTable)) })
721
+ ]
722
+ }
723
+ ) }),
724
+ shown.row === shown.table.rows[shown.table.rows.length - 1] && /* @__PURE__ */ jsx4("div", { "data-table-control": true, className: cn(HANDLE_BOX, "absolute flex h-4"), style: { left: shown.tableBox.left, top: shown.tableBox.top + shown.tableBox.height, width: shown.tableBox.width }, children: /* @__PURE__ */ jsx4(IconButton, { "aria-label": "Add row", tooltip: "Add row", className: "h-full w-full p-0", onMouseDown: keepFocus, onClick: run(() => appendTableRow(editor, shown.table)), children: /* @__PURE__ */ jsx4(IconPlus, { size: 12, stroke: 1.5 }) }) }),
725
+ shown.cell === shown.row.cells[shown.row.cells.length - 1] && /* @__PURE__ */ jsx4("div", { "data-table-control": true, className: cn(HANDLE_BOX, "absolute flex w-4"), style: { left: shown.tableBox.left + shown.tableBox.width, top: shown.tableBox.top, height: shown.tableBox.height }, children: /* @__PURE__ */ jsx4(IconButton, { "aria-label": "Add column", tooltip: "Add column", className: "h-full w-full p-0", onMouseDown: keepFocus, onClick: run(() => appendTableColumn(editor, shown.table)), children: /* @__PURE__ */ jsx4(IconPlus, { size: 12, stroke: 1.5 }) }) })
726
+ ] })
727
+ ]
728
+ }
729
+ );
730
+ }
731
+
583
732
  // src/ComposerTriggers.tsx
584
733
  import { Extension as Extension3 } from "@tiptap/core";
585
734
  import { PluginKey as PluginKey3 } from "@tiptap/pm/state";
@@ -589,7 +738,7 @@ import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
589
738
  import { Avatar } from "@estiva-app/ui";
590
739
  import { EnterHint } from "@estiva-app/ui";
591
740
  import { inlineChipClassName } from "@estiva-app/ui";
592
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
741
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
593
742
  var PEOPLE_LIMIT = 6;
594
743
  function filterPeople(people, query) {
595
744
  const q = query.toLowerCase();
@@ -607,8 +756,8 @@ function peopleMenu(urgent) {
607
756
  row: (person) => ({
608
757
  label: person.name,
609
758
  description: person.description,
610
- leading: person.avatar ?? /* @__PURE__ */ jsx4(Avatar, { size: 32, alt: person.name }),
611
- hint: /* @__PURE__ */ jsx4(EnterHint, {}),
759
+ leading: person.avatar ?? /* @__PURE__ */ jsx5(Avatar, { size: 32, alt: person.name }),
760
+ hint: /* @__PURE__ */ jsx5(EnterHint, {}),
612
761
  size: "tall"
613
762
  })
614
763
  };
@@ -735,9 +884,9 @@ var CaptionedReference = Mention.extend({
735
884
  addNodeView() {
736
885
  const { icon, iconFor } = this.options;
737
886
  return ReactNodeViewRenderer(
738
- ({ node }) => /* @__PURE__ */ jsxs3(NodeViewWrapper, { as: "span", className: inlineChipClassName("neutral", "max-w-[24ch] cursor-default"), children: [
739
- /* @__PURE__ */ jsx4("span", { className: "flex items-center justify-center w-4 h-4 shrink-0 text-text-secondary", children: iconFor?.(node.attrs.uri ?? null) ?? icon }),
740
- /* @__PURE__ */ jsx4("span", { className: "truncate", children: captionOf(node.attrs.label, node.attrs.snippet) })
887
+ ({ node }) => /* @__PURE__ */ jsxs4(NodeViewWrapper, { as: "span", className: inlineChipClassName("neutral", "max-w-[24ch] cursor-default"), children: [
888
+ /* @__PURE__ */ jsx5("span", { className: "flex items-center justify-center w-4 h-4 shrink-0 text-text-secondary", children: iconFor?.(node.attrs.uri ?? null) ?? icon }),
889
+ /* @__PURE__ */ jsx5("span", { className: "truncate", children: captionOf(node.attrs.label, node.attrs.snippet) })
741
890
  ] }),
742
891
  { as: "span" }
743
892
  );
@@ -759,7 +908,7 @@ function referenceMenu({ ariaLabel, sectionLabel, icon, iconFor }) {
759
908
  return groups;
760
909
  },
761
910
  itemKey: (item) => `reference-${item.id}`,
762
- row: (item) => ({ leading: item.icon ?? iconFor?.(item.uri) ?? icon, label: captionOf(item.label, item.snippet), description: item.description, hint: /* @__PURE__ */ jsx4(EnterHint, {}) })
911
+ row: (item) => ({ leading: item.icon ?? iconFor?.(item.uri) ?? icon, label: captionOf(item.label, item.snippet), description: item.description, hint: /* @__PURE__ */ jsx5(EnterHint, {}) })
763
912
  };
764
913
  }
765
914
  var listed = (options, query) => options.sections ? options.sections(query).flatMap((section) => section.items.map((item) => ({ ...item, section: section.label }))) : filterReferences(options.items(), query);
@@ -793,6 +942,7 @@ var ReferenceTrigger = Extension3.create({
793
942
  ];
794
943
  }
795
944
  });
945
+ var offers = (editor, command) => !command.needs || command.needs.every((name) => editor.extensionManager.extensions.some((extension) => extension.name === name));
796
946
  var FORMATS = [
797
947
  { id: "paragraph", label: "Text" },
798
948
  { id: "heading", label: "Heading", trigger: "#" },
@@ -802,11 +952,16 @@ var FORMATS = [
802
952
  { id: "bulletList", label: "Bulleted list", trigger: "-" },
803
953
  { id: "orderedList", label: "Numbered list", trigger: "1." }
804
954
  ];
955
+ var DOCUMENT_FORMATS = [
956
+ { id: "checklist", label: "Checklist", trigger: "[]" },
957
+ { id: "note", label: "Note" },
958
+ { id: "warning", label: "Warning" }
959
+ ];
805
960
  function applyFormat(editor, id, code = "inline") {
806
961
  const chain = editor.chain().focus();
807
962
  const run = (command, ...args) => {
808
- const named = chain[command];
809
- return named ? named(...args).run() : false;
963
+ const named2 = chain[command];
964
+ return named2 ? named2(...args).run() : false;
810
965
  };
811
966
  switch (id) {
812
967
  case "paragraph":
@@ -823,17 +978,38 @@ function applyFormat(editor, id, code = "inline") {
823
978
  return run("toggleOrderedList");
824
979
  case "code":
825
980
  return run(code === "block" ? "toggleCodeBlock" : "toggleCode");
981
+ case "checklist": {
982
+ if (!editor.isActive("listItem") && !run("toggleBulletList")) return false;
983
+ if (typeof editor.getAttributes("listItem").checked === "boolean") return true;
984
+ return editor.chain().updateAttributes("listItem", { checked: false }).run();
985
+ }
986
+ case "note":
987
+ case "warning": {
988
+ if (!editor.isActive("blockquote") && !run("toggleBlockquote")) return false;
989
+ return editor.chain().updateAttributes("blockquote", { tone: id }).run();
990
+ }
826
991
  default:
827
992
  return false;
828
993
  }
829
994
  }
995
+ var KEYWORDS = {
996
+ checklist: ["todo", "task"],
997
+ note: ["callout"],
998
+ warning: ["callout"]
999
+ };
1000
+ var NEEDS = {
1001
+ checklist: ["checklist"],
1002
+ note: ["callout"],
1003
+ warning: ["callout"]
1004
+ };
830
1005
  function formatSection(code = "inline") {
831
1006
  return {
832
1007
  label: "Format",
833
- commands: FORMATS.map((format) => ({
1008
+ commands: (code === "block" ? [...FORMATS, ...DOCUMENT_FORMATS] : FORMATS).map((format) => ({
834
1009
  key: `format-${format.id}`,
835
1010
  row: { label: format.label, shortcut: code === "block" && format.blockTrigger || format.trigger },
836
- keywords: [format.id],
1011
+ keywords: [format.id, ...KEYWORDS[format.id] ?? []],
1012
+ ...NEEDS[format.id] ? { needs: NEEDS[format.id] } : {},
837
1013
  run: (editor) => {
838
1014
  applyFormat(editor, format.id, code);
839
1015
  }
@@ -865,7 +1041,9 @@ var SlashCommands = Extension3.create({
865
1041
  char: "/",
866
1042
  startOfLine: false,
867
1043
  pluginKey: new PluginKey3("slashCommands"),
868
- items: ({ query }) => filterSlashSections(sections, query).flatMap((section, at) => section.commands.map((command) => ({ ...command, section: at }))),
1044
+ items: ({ query, editor }) => filterSlashSections(sections, query).flatMap(
1045
+ (section, at) => section.commands.filter((command) => offers(editor, command)).map((command) => ({ ...command, section: at }))
1046
+ ),
869
1047
  command: ({ editor, range, props }) => {
870
1048
  editor.chain().focus().deleteRange(range).run();
871
1049
  props.run(editor);
@@ -884,11 +1062,14 @@ var SlashCommands = Extension3.create({
884
1062
  });
885
1063
 
886
1064
  // src/BlockNodes.tsx
887
- import { Extension as Extension4, Node as Node2 } from "@tiptap/core";
1065
+ import { Extension as Extension4, InputRule, Node as Node2 } from "@tiptap/core";
1066
+ import { DOMSerializer } from "@tiptap/pm/model";
888
1067
  import { Plugin as Plugin3, PluginKey as PluginKey4 } from "@tiptap/pm/state";
889
1068
  import { Mapping, ReplaceAroundStep } from "@tiptap/pm/transform";
890
- import { NodeViewWrapper as NodeViewWrapper2, ReactNodeViewRenderer as ReactNodeViewRenderer2 } from "@tiptap/react";
891
- import { jsx as jsx5 } from "react/jsx-runtime";
1069
+ import { NodeViewWrapper as NodeViewWrapper2, ReactNodeViewRenderer as ReactNodeViewRenderer2, ReactRenderer as ReactRenderer2 } from "@tiptap/react";
1070
+ import { Checkbox } from "@estiva-app/ui";
1071
+ import { calloutLook } from "@estiva-app/ui";
1072
+ import { jsx as jsx6 } from "react/jsx-runtime";
892
1073
  var BLOCK_TYPES = [
893
1074
  "paragraph",
894
1075
  "heading",
@@ -1118,7 +1299,7 @@ var ReferenceNode = Node2.create({
1118
1299
  addNodeView() {
1119
1300
  const View = this.options.view;
1120
1301
  if (!View) return null;
1121
- return ReactNodeViewRenderer2(({ node }) => /* @__PURE__ */ jsx5(NodeViewWrapper2, { as: "span", className: "inline", children: /* @__PURE__ */ jsx5(View, { uri: String(node.attrs.uri ?? "") }) }));
1302
+ return ReactNodeViewRenderer2(({ node }) => /* @__PURE__ */ jsx6(NodeViewWrapper2, { as: "span", className: "inline", children: /* @__PURE__ */ jsx6(View, { uri: String(node.attrs.uri ?? "") }) }));
1122
1303
  }
1123
1304
  });
1124
1305
  var UnknownBlock = Node2.create({
@@ -1146,7 +1327,7 @@ var UnknownBlock = Node2.create({
1146
1327
  function UnknownBlockView({ node }) {
1147
1328
  const source = node.attrs.source;
1148
1329
  const text = String(node.attrs.text ?? "");
1149
- return /* @__PURE__ */ jsx5(
1330
+ return /* @__PURE__ */ jsx6(
1150
1331
  NodeViewWrapper2,
1151
1332
  {
1152
1333
  "data-block-id": source?.id,
@@ -1186,15 +1367,174 @@ var AttachmentNode = Node2.create({
1186
1367
  addNodeView() {
1187
1368
  const View = this.options.view;
1188
1369
  if (!View) return null;
1189
- return ReactNodeViewRenderer2(({ node }) => /* @__PURE__ */ jsx5(NodeViewWrapper2, { className: "my-2", children: /* @__PURE__ */ jsx5(View, { attrs: node.attrs }) }));
1370
+ return ReactNodeViewRenderer2(({ node }) => /* @__PURE__ */ jsx6(NodeViewWrapper2, { className: "my-2", children: /* @__PURE__ */ jsx6(View, { attrs: node.attrs }) }));
1371
+ }
1372
+ });
1373
+ var TICK = "checklistTick";
1374
+ function isChecklistTick(transaction) {
1375
+ return transaction.getMeta(TICK) === true;
1376
+ }
1377
+ function named(chain, command, ...args) {
1378
+ const own = chain[command];
1379
+ return own ? own(...args) : chain;
1380
+ }
1381
+ function itemAtCaret(state) {
1382
+ const { $from } = state.selection;
1383
+ if ($from.depth < 2) return null;
1384
+ const item = $from.node(-1);
1385
+ if (item.type.name !== "listItem" || $from.index(-1) !== 0) return null;
1386
+ return { node: item, pos: $from.before(-1) };
1387
+ }
1388
+ var sameAttrs = (a, b) => Object.keys({ ...a.attrs, ...b.attrs }).every((key) => a.attrs[key] === b.attrs[key]);
1389
+ function listItemView(editor, node, view, getPos) {
1390
+ const toDOM = node.type.spec.toDOM;
1391
+ const { dom, contentDOM } = toDOM ? DOMSerializer.renderSpec(document, toDOM(node)) : { dom: document.createElement("li"), contentDOM: void 0 };
1392
+ const checked = node.attrs.checked;
1393
+ if (typeof checked !== "boolean") return { dom, contentDOM: contentDOM ?? dom, update: (next) => next.type === node.type && sameAttrs(next, node) };
1394
+ const li = dom;
1395
+ const box = document.createElement("span");
1396
+ box.contentEditable = "false";
1397
+ box.setAttribute("data-checklist-box", "");
1398
+ const body = document.createElement("div");
1399
+ li.replaceChildren(box, body);
1400
+ const tick = (next) => {
1401
+ const pos = getPos();
1402
+ if (pos === void 0 || !view.editable) return;
1403
+ view.dispatch(view.state.tr.setNodeAttribute(pos, "checked", next).setMeta(TICK, true));
1404
+ };
1405
+ let words = node.firstChild?.textContent.trim() || "Item";
1406
+ const props = () => ({ checked, onChange: view.editable ? tick : void 0, "aria-label": words });
1407
+ const renderer = new ReactRenderer2(Checkbox, { editor, props: props(), as: "span" });
1408
+ box.appendChild(renderer.element);
1409
+ let drawn = "";
1410
+ const sync = () => {
1411
+ const now = `${view.editable}:${words}`;
1412
+ if (now === drawn) return;
1413
+ drawn = now;
1414
+ renderer.updateProps(props());
1415
+ for (const [name, value] of Object.entries({ role: "checkbox", "aria-checked": String(checked), "aria-readonly": "true", "aria-label": words })) {
1416
+ if (view.editable) box.removeAttribute(name);
1417
+ else box.setAttribute(name, value);
1418
+ }
1419
+ };
1420
+ sync();
1421
+ editor.on("update", sync);
1422
+ return {
1423
+ dom: li,
1424
+ contentDOM: body,
1425
+ update: (next) => {
1426
+ if (next.type !== node.type || !sameAttrs(next, node)) return false;
1427
+ words = next.firstChild?.textContent.trim() || "Item";
1428
+ sync();
1429
+ return true;
1430
+ },
1431
+ stopEvent: (event) => box.contains(event.target),
1432
+ ignoreMutation: (mutation) => box.contains(mutation.target),
1433
+ destroy: () => {
1434
+ editor.off("update", sync);
1435
+ renderer.destroy();
1436
+ }
1437
+ };
1438
+ }
1439
+ var Checklist = Extension4.create({
1440
+ name: "checklist",
1441
+ // Before the list item's own Enter and Backspace.
1442
+ priority: 101,
1443
+ addGlobalAttributes() {
1444
+ return [
1445
+ {
1446
+ types: ["listItem"],
1447
+ attributes: {
1448
+ checked: {
1449
+ default: null,
1450
+ keepOnSplit: false,
1451
+ parseHTML: (element) => {
1452
+ const value = element.getAttribute("data-checked");
1453
+ return value === "true" ? true : value === "false" ? false : null;
1454
+ },
1455
+ renderHTML: (attributes) => typeof attributes.checked === "boolean" ? { "data-checked": String(attributes.checked) } : {}
1456
+ }
1457
+ }
1458
+ }
1459
+ ];
1460
+ },
1461
+ addInputRules() {
1462
+ return [
1463
+ new InputRule({
1464
+ find: /^\[([ xX]?)\]\s$/,
1465
+ handler: ({ state, range, match, chain }) => {
1466
+ const checked = match[1].toLowerCase() === "x";
1467
+ const inList = itemAtCaret(state) !== null;
1468
+ if (!inList && !chain().toggleBulletList) return null;
1469
+ const deleted = chain().deleteRange(range);
1470
+ const listed2 = inList ? deleted : named(deleted, "toggleBulletList");
1471
+ listed2.updateAttributes("listItem", { checked }).run();
1472
+ }
1473
+ })
1474
+ ];
1475
+ },
1476
+ addKeyboardShortcuts() {
1477
+ return {
1478
+ Enter: () => {
1479
+ const { state } = this.editor;
1480
+ const item = itemAtCaret(state);
1481
+ const { $from, empty } = state.selection;
1482
+ if (!item || typeof item.node.attrs.checked !== "boolean") return false;
1483
+ if (!empty) return this.editor.chain().deleteSelection().splitListItem("listItem", { checked: false }).run();
1484
+ if ($from.parent.content.size === 0) return false;
1485
+ if ($from.parentOffset > 0) return this.editor.commands.splitListItem("listItem", { checked: false });
1486
+ return this.editor.chain().splitListItem("listItem", { checked: item.node.attrs.checked }).command(({ tr }) => {
1487
+ tr.setNodeAttribute(tr.mapping.map(item.pos), "checked", false);
1488
+ return true;
1489
+ }).run();
1490
+ },
1491
+ Backspace: () => {
1492
+ const { state } = this.editor;
1493
+ const item = itemAtCaret(state);
1494
+ const { $from, empty } = state.selection;
1495
+ if (!item || !empty || $from.parentOffset > 0 || typeof item.node.attrs.checked !== "boolean") return false;
1496
+ this.editor.view.dispatch(state.tr.setNodeAttribute(item.pos, "checked", null));
1497
+ return true;
1498
+ }
1499
+ };
1500
+ },
1501
+ addProseMirrorPlugins() {
1502
+ const editor = this.editor;
1503
+ return [
1504
+ new Plugin3({
1505
+ key: new PluginKey4("checklist"),
1506
+ props: { nodeViews: { listItem: (node, view, getPos) => listItemView(editor, node, view, getPos) } }
1507
+ })
1508
+ ];
1509
+ }
1510
+ });
1511
+ var Callout = Extension4.create({
1512
+ name: "callout",
1513
+ addGlobalAttributes() {
1514
+ return [
1515
+ {
1516
+ types: ["blockquote"],
1517
+ attributes: {
1518
+ tone: {
1519
+ default: null,
1520
+ parseHTML: (element) => element.getAttribute("data-tone"),
1521
+ // As `RichText` draws it: the tone as written, and the look it is drawn in.
1522
+ renderHTML: (attributes) => typeof attributes.tone === "string" ? { "data-tone": attributes.tone, "data-callout": calloutLook(attributes.tone) } : {}
1523
+ }
1524
+ }
1525
+ }
1526
+ ];
1190
1527
  }
1191
1528
  });
1192
1529
  export {
1193
1530
  AttachmentNode,
1194
1531
  BlockId,
1532
+ Callout,
1195
1533
  CaptionedReference,
1534
+ Checklist,
1196
1535
  CodeBlockColours,
1197
1536
  EditorBlockHandle,
1537
+ EditorTableControls,
1198
1538
  FORMATS,
1199
1539
  KeptSelection,
1200
1540
  PastedImages,
@@ -1205,14 +1545,19 @@ export {
1205
1545
  SlashCommands,
1206
1546
  UnknownBlock,
1207
1547
  UrgentPersonMention,
1548
+ appendTableColumn,
1549
+ appendTableRow,
1208
1550
  applyFormat,
1209
1551
  blockDropCursor,
1210
1552
  deleteBlock,
1553
+ deleteTableColumn,
1554
+ deleteTableRow,
1211
1555
  duplicateBlock,
1212
1556
  filterPeople,
1213
1557
  filterReferences,
1214
1558
  filterSlashSections,
1215
1559
  formatSection,
1560
+ isChecklistTick,
1216
1561
  isSuggestionActive,
1217
1562
  isSuggestionOpen,
1218
1563
  moveBlock,