@estiva-app/ui 0.70.0 → 0.71.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.
- package/dist/BlockNodes.d.ts +2 -0
- package/dist/BlockNodes.d.ts.map +1 -1
- package/dist/ComposerTriggers.d.ts +8 -0
- package/dist/ComposerTriggers.d.ts.map +1 -1
- package/dist/EditorBlockHandle.d.ts +53 -4
- package/dist/EditorBlockHandle.d.ts.map +1 -1
- package/dist/LinkChips.d.ts +2 -0
- package/dist/LinkChips.d.ts.map +1 -1
- package/dist/editor.d.ts +1 -1
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +373 -264
- package/dist/editor.js.map +4 -4
- package/package.json +1 -1
- package/registry.json +62 -5
- package/src/BlockHandle.test.tsx +1 -1
- package/src/BlockNodes.tsx +2 -0
- package/src/ComposerTriggers.tsx +3 -1
- package/src/EditorBlockHandle.mdx +14 -9
- package/src/EditorBlockHandle.stories.tsx +5 -11
- package/src/EditorBlockHandle.test.tsx +200 -0
- package/src/EditorBlockHandle.tsx +181 -10
- package/src/LinkChips.ts +2 -0
- package/src/editor.ts +15 -1
- package/src/registry/registry.test.ts +3 -3
package/dist/editor.js
CHANGED
|
@@ -516,260 +516,8 @@ function suggestionPopup(options) {
|
|
|
516
516
|
import { useMemo } from "react";
|
|
517
517
|
import { IconCopy, IconTrash } from "@tabler/icons-react";
|
|
518
518
|
import { BlockHandle, blockAtY } from "@estiva-app/ui";
|
|
519
|
+
import { Selection } from "@tiptap/pm/state";
|
|
519
520
|
import { MenuItem, MenuSub } from "@estiva-app/ui";
|
|
520
|
-
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
521
|
-
function editorBlocks(view) {
|
|
522
|
-
const out = [];
|
|
523
|
-
view.state.doc.forEach((node, pos) => {
|
|
524
|
-
const element = view.nodeDOM(pos);
|
|
525
|
-
if (element instanceof HTMLElement) out.push({ id: node.attrs.blockId || void 0, element, pos });
|
|
526
|
-
});
|
|
527
|
-
return out;
|
|
528
|
-
}
|
|
529
|
-
function duplicateBlock(editor, pos) {
|
|
530
|
-
const node = editor.state.doc.nodeAt(pos);
|
|
531
|
-
if (!node) return false;
|
|
532
|
-
editor.view.dispatch(editor.state.tr.insert(pos + node.nodeSize, node).scrollIntoView());
|
|
533
|
-
editor.commands.focus(pos + node.nodeSize + 1);
|
|
534
|
-
return true;
|
|
535
|
-
}
|
|
536
|
-
function deleteBlock(editor, pos) {
|
|
537
|
-
const { state } = editor;
|
|
538
|
-
const node = state.doc.nodeAt(pos);
|
|
539
|
-
if (!node) return false;
|
|
540
|
-
const tr = state.doc.childCount === 1 ? state.tr.replaceWith(pos, pos + node.nodeSize, state.schema.nodes.paragraph.create()) : state.tr.delete(pos, pos + node.nodeSize);
|
|
541
|
-
editor.view.dispatch(tr);
|
|
542
|
-
editor.commands.focus(Math.min(pos + 1, editor.state.doc.content.size));
|
|
543
|
-
return true;
|
|
544
|
-
}
|
|
545
|
-
function moveBlock(editor, from, to) {
|
|
546
|
-
const node = editor.state.doc.nodeAt(from);
|
|
547
|
-
if (!node || to >= from && to <= from + node.nodeSize) return false;
|
|
548
|
-
const tr = editor.state.tr.delete(from, from + node.nodeSize);
|
|
549
|
-
const at = tr.mapping.map(to);
|
|
550
|
-
editor.view.dispatch(tr.insert(at, node).scrollIntoView());
|
|
551
|
-
editor.commands.focus(at + 1);
|
|
552
|
-
return true;
|
|
553
|
-
}
|
|
554
|
-
function startBlockDrag(editor, pos, event, image) {
|
|
555
|
-
editor.commands.setNodeSelection(pos);
|
|
556
|
-
const { view } = editor;
|
|
557
|
-
const slice = view.state.selection.content();
|
|
558
|
-
const { dom, text } = view.serializeForClipboard(slice);
|
|
559
|
-
const transfer = event.dataTransfer;
|
|
560
|
-
if (transfer) {
|
|
561
|
-
transfer.clearData();
|
|
562
|
-
transfer.setData("text/html", dom.innerHTML);
|
|
563
|
-
transfer.setData("text/plain", text);
|
|
564
|
-
transfer.effectAllowed = "copyMove";
|
|
565
|
-
if (image) transfer.setDragImage(image, 0, 0);
|
|
566
|
-
}
|
|
567
|
-
view.dragging = { slice, move: true };
|
|
568
|
-
}
|
|
569
|
-
var blockDropCursor = { color: false, class: "bg-accent-primary" };
|
|
570
|
-
var NOTHING_YET = { find: () => null, rows: () => null, startDrag: () => {
|
|
571
|
-
}, drop: () => {
|
|
572
|
-
}, endDrag: () => {
|
|
573
|
-
}, onChange: () => () => {
|
|
574
|
-
} };
|
|
575
|
-
function EditorBlockHandle({ editor, turnInto = [], actions, children, className }) {
|
|
576
|
-
const editing = useMemo(() => {
|
|
577
|
-
if (!editor) return NOTHING_YET;
|
|
578
|
-
let dragged;
|
|
579
|
-
return {
|
|
580
|
-
find: (y) => blockAtY(editorBlocks(editor.view), y),
|
|
581
|
-
rows: (block) => {
|
|
582
|
-
const pos = block.pos;
|
|
583
|
-
if (!editor.isEditable) return null;
|
|
584
|
-
const node = pos === void 0 ? null : editor.state.doc.nodeAt(pos);
|
|
585
|
-
if (pos === void 0 || !node) return null;
|
|
586
|
-
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
587
|
-
node.isTextblock && turnInto.length > 0 && /* @__PURE__ */ jsx3(MenuSub, { label: "Turn into", children: turnInto.map((row) => /* @__PURE__ */ jsx3(
|
|
588
|
-
MenuItem,
|
|
589
|
-
{
|
|
590
|
-
label: row.label,
|
|
591
|
-
selected: row.active?.(node) ?? false,
|
|
592
|
-
onClick: () => {
|
|
593
|
-
editor.chain().focus().setTextSelection(pos + 1).run();
|
|
594
|
-
row.run(editor);
|
|
595
|
-
}
|
|
596
|
-
},
|
|
597
|
-
row.label
|
|
598
|
-
)) }),
|
|
599
|
-
/* @__PURE__ */ jsx3(MenuItem, { label: "Duplicate", leading: /* @__PURE__ */ jsx3(IconCopy, { size: 16, stroke: 1.5, className: "text-text-secondary" }), onClick: () => duplicateBlock(editor, pos) }),
|
|
600
|
-
/* @__PURE__ */ jsx3(MenuItem, { label: "Delete", destructive: true, leading: /* @__PURE__ */ jsx3(IconTrash, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: () => deleteBlock(editor, pos) })
|
|
601
|
-
] });
|
|
602
|
-
},
|
|
603
|
-
startDrag: (block, event) => {
|
|
604
|
-
dragged = block.pos;
|
|
605
|
-
if (block.pos !== void 0) startBlockDrag(editor, block.pos, event, block.element);
|
|
606
|
-
},
|
|
607
|
-
drop: (target, after) => {
|
|
608
|
-
const node = target.pos === void 0 ? null : editor.state.doc.nodeAt(target.pos);
|
|
609
|
-
if (dragged === void 0 || target.pos === void 0 || !node) return;
|
|
610
|
-
moveBlock(editor, dragged, after ? target.pos + node.nodeSize : target.pos);
|
|
611
|
-
},
|
|
612
|
-
// ProseMirror clears `dragging` on its own dragend, which a drag started outside its element never reaches.
|
|
613
|
-
endDrag: () => setTimeout(() => editor.view.dragging = null, 50),
|
|
614
|
-
onChange: (changed) => {
|
|
615
|
-
editor.on("update", changed);
|
|
616
|
-
return () => {
|
|
617
|
-
editor.off("update", changed);
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
|
-
}, [editor, turnInto]);
|
|
622
|
-
return /* @__PURE__ */ jsx3(BlockHandle, { editing, actions, className, children });
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// src/EditorTableControls.tsx
|
|
626
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
|
|
627
|
-
import { TextSelection } from "@tiptap/pm/state";
|
|
628
|
-
import { addColumnAfter, addColumnBefore, addRowAfter, addRowBefore, deleteColumn, deleteRow, deleteTable, toggleHeader } from "@tiptap/pm/tables";
|
|
629
|
-
import { IconColumnInsertLeft, IconColumnInsertRight, IconGripHorizontal, IconGripVertical, IconPlus, IconRowInsertBottom, IconRowInsertTop, IconTrash as IconTrash2 } from "@tabler/icons-react";
|
|
630
|
-
import { cn } from "@estiva-app/ui";
|
|
631
|
-
import { IconButton } from "@estiva-app/ui";
|
|
632
|
-
|
|
633
|
-
// src/looks.ts
|
|
634
|
-
var FLOATING_SURFACE_CLASSES = "rounded-lg border border-border-default bg-bg-elevated shadow-lg";
|
|
635
|
-
|
|
636
|
-
// src/EditorTableControls.tsx
|
|
637
|
-
import { Menu, MenuItem as MenuItem2, MenuSeparator } from "@estiva-app/ui";
|
|
638
|
-
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
639
|
-
function inCell(editor, cell, command) {
|
|
640
|
-
const { view } = editor;
|
|
641
|
-
if (!view.dom.contains(cell)) return false;
|
|
642
|
-
const at = view.posAtDOM(cell, 0);
|
|
643
|
-
view.dispatch(view.state.tr.setSelection(TextSelection.near(view.state.doc.resolve(at))));
|
|
644
|
-
const done = command(view.state, view.dispatch, view);
|
|
645
|
-
editor.commands.focus();
|
|
646
|
-
return done;
|
|
647
|
-
}
|
|
648
|
-
function isHeaderRow(row) {
|
|
649
|
-
return row.cells.length > 0 && Array.from(row.cells).every((c) => c.tagName === "TH");
|
|
650
|
-
}
|
|
651
|
-
function deleteTableRow(editor, cell) {
|
|
652
|
-
const row = cell.closest("tr");
|
|
653
|
-
const table = cell.closest("table");
|
|
654
|
-
if (!row || !table || table.rows.length <= 1) return inCell(editor, cell, deleteTable);
|
|
655
|
-
const header = isHeaderRow(row);
|
|
656
|
-
return inCell(editor, cell, header ? (state, dispatch, view) => deleteRow(state, dispatch) && (!view || toggleHeader("row")(view.state, view.dispatch)) : deleteRow);
|
|
657
|
-
}
|
|
658
|
-
function deleteTableColumn(editor, cell) {
|
|
659
|
-
const row = cell.closest("tr");
|
|
660
|
-
return inCell(editor, cell, row && row.cells.length > 1 ? deleteColumn : deleteTable);
|
|
661
|
-
}
|
|
662
|
-
function appendTableRow(editor, table) {
|
|
663
|
-
const last = table.rows[table.rows.length - 1]?.cells[0];
|
|
664
|
-
return !!last && inCell(editor, last, addRowAfter);
|
|
665
|
-
}
|
|
666
|
-
function appendTableColumn(editor, table) {
|
|
667
|
-
const first = table.rows[0];
|
|
668
|
-
const last = first?.cells[first.cells.length - 1];
|
|
669
|
-
return !!last && inCell(editor, last, addColumnAfter);
|
|
670
|
-
}
|
|
671
|
-
function place(frame, { table, row, cell }) {
|
|
672
|
-
const at = frame.getBoundingClientRect();
|
|
673
|
-
const t = table.getBoundingClientRect();
|
|
674
|
-
const r = row.getBoundingClientRect();
|
|
675
|
-
const c = cell.getBoundingClientRect();
|
|
676
|
-
return {
|
|
677
|
-
tableBox: { top: t.top - at.top, left: t.left - at.left, width: t.width, height: t.height },
|
|
678
|
-
rowBox: { top: r.top - at.top, height: r.height },
|
|
679
|
-
columnBox: { left: c.left - at.left, width: c.width }
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
var ICON = { size: 16, stroke: 1.5, className: "text-text-secondary" };
|
|
683
|
-
var keepFocus = (e) => e.preventDefault();
|
|
684
|
-
var HANDLE_BOX = cn(FLOATING_SURFACE_CLASSES, "shadow-none");
|
|
685
|
-
function EditorTableControls({ editor, children, className }) {
|
|
686
|
-
const frame = useRef3(null);
|
|
687
|
-
const [hovered, setHovered] = useState2(null);
|
|
688
|
-
const [menu, setMenu] = useState2(null);
|
|
689
|
-
const editable = !!editor && editor.isEditable;
|
|
690
|
-
const follow = useCallback2(
|
|
691
|
-
(target) => {
|
|
692
|
-
if (!editor || !frame.current || !(target instanceof Element)) return;
|
|
693
|
-
if (target.closest("[data-table-control]")) return;
|
|
694
|
-
const cell = target.closest("td, th");
|
|
695
|
-
const row = cell?.closest("tr");
|
|
696
|
-
const table = row?.closest("table");
|
|
697
|
-
if (!cell || !row || !table || !editor.view.dom.contains(table)) {
|
|
698
|
-
setHovered(null);
|
|
699
|
-
return;
|
|
700
|
-
}
|
|
701
|
-
const next = { table, row, cell, ...place(frame.current, { table, row, cell }) };
|
|
702
|
-
setHovered((was) => was && was.cell === cell && JSON.stringify([was.tableBox, was.rowBox, was.columnBox]) === JSON.stringify([next.tableBox, next.rowBox, next.columnBox]) ? was : next);
|
|
703
|
-
},
|
|
704
|
-
[editor]
|
|
705
|
-
);
|
|
706
|
-
useEffect2(() => {
|
|
707
|
-
if (!editor) return;
|
|
708
|
-
const changed = ({ transaction }) => {
|
|
709
|
-
if (transaction.docChanged && !menu) setHovered(null);
|
|
710
|
-
};
|
|
711
|
-
editor.on("transaction", changed);
|
|
712
|
-
return () => {
|
|
713
|
-
editor.off("transaction", changed);
|
|
714
|
-
};
|
|
715
|
-
}, [editor, menu]);
|
|
716
|
-
const shown = editable ? hovered : null;
|
|
717
|
-
const run = (act) => () => {
|
|
718
|
-
if (editor) act();
|
|
719
|
-
setMenu(null);
|
|
720
|
-
setHovered(null);
|
|
721
|
-
};
|
|
722
|
-
return /* @__PURE__ */ jsxs3(
|
|
723
|
-
"div",
|
|
724
|
-
{
|
|
725
|
-
ref: frame,
|
|
726
|
-
className: cn("relative", className),
|
|
727
|
-
onMouseMove: (e) => {
|
|
728
|
-
if (!menu) follow(e.target);
|
|
729
|
-
},
|
|
730
|
-
onMouseLeave: () => {
|
|
731
|
-
if (!menu) setHovered(null);
|
|
732
|
-
},
|
|
733
|
-
children: [
|
|
734
|
-
children,
|
|
735
|
-
shown && editor && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
736
|
-
/* @__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(
|
|
737
|
-
Menu,
|
|
738
|
-
{
|
|
739
|
-
open: menu === "row",
|
|
740
|
-
onOpenChange: (open) => setMenu(open ? "row" : null),
|
|
741
|
-
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 }) }),
|
|
742
|
-
children: [
|
|
743
|
-
!isHeaderRow(shown.row) && /* @__PURE__ */ jsx4(MenuItem2, { label: "Insert above", leading: /* @__PURE__ */ jsx4(IconRowInsertTop, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addRowBefore)) }),
|
|
744
|
-
/* @__PURE__ */ jsx4(MenuItem2, { label: "Insert below", leading: /* @__PURE__ */ jsx4(IconRowInsertBottom, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addRowAfter)) }),
|
|
745
|
-
/* @__PURE__ */ jsx4(MenuSeparator, {}),
|
|
746
|
-
/* @__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)) }),
|
|
747
|
-
/* @__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)) })
|
|
748
|
-
]
|
|
749
|
-
}
|
|
750
|
-
) }),
|
|
751
|
-
/* @__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(
|
|
752
|
-
Menu,
|
|
753
|
-
{
|
|
754
|
-
open: menu === "column",
|
|
755
|
-
onOpenChange: (open) => setMenu(open ? "column" : null),
|
|
756
|
-
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 }) }),
|
|
757
|
-
children: [
|
|
758
|
-
/* @__PURE__ */ jsx4(MenuItem2, { label: "Insert left", leading: /* @__PURE__ */ jsx4(IconColumnInsertLeft, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addColumnBefore)) }),
|
|
759
|
-
/* @__PURE__ */ jsx4(MenuItem2, { label: "Insert right", leading: /* @__PURE__ */ jsx4(IconColumnInsertRight, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addColumnAfter)) }),
|
|
760
|
-
/* @__PURE__ */ jsx4(MenuSeparator, {}),
|
|
761
|
-
/* @__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)) }),
|
|
762
|
-
/* @__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)) })
|
|
763
|
-
]
|
|
764
|
-
}
|
|
765
|
-
) }),
|
|
766
|
-
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 }) }) }),
|
|
767
|
-
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 }) }) })
|
|
768
|
-
] })
|
|
769
|
-
]
|
|
770
|
-
}
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
521
|
|
|
774
522
|
// src/ComposerTriggers.tsx
|
|
775
523
|
import { Extension as Extension4 } from "@tiptap/core";
|
|
@@ -780,7 +528,7 @@ import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
|
|
|
780
528
|
import { Avatar } from "@estiva-app/ui";
|
|
781
529
|
import { EnterHint } from "@estiva-app/ui";
|
|
782
530
|
import { inlineChipClassName } from "@estiva-app/ui";
|
|
783
|
-
import { jsx as
|
|
531
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
784
532
|
var PEOPLE_LIMIT = 6;
|
|
785
533
|
function filterPeople(people, query) {
|
|
786
534
|
const q = query.toLowerCase();
|
|
@@ -798,8 +546,8 @@ function peopleMenu(urgent) {
|
|
|
798
546
|
row: (person) => ({
|
|
799
547
|
label: person.name,
|
|
800
548
|
description: person.description,
|
|
801
|
-
leading: person.avatar ?? /* @__PURE__ */
|
|
802
|
-
hint: /* @__PURE__ */
|
|
549
|
+
leading: person.avatar ?? /* @__PURE__ */ jsx3(Avatar, { size: 32, alt: person.name }),
|
|
550
|
+
hint: /* @__PURE__ */ jsx3(EnterHint, {}),
|
|
803
551
|
size: "tall"
|
|
804
552
|
})
|
|
805
553
|
};
|
|
@@ -926,9 +674,9 @@ var CaptionedReference = Mention.extend({
|
|
|
926
674
|
addNodeView() {
|
|
927
675
|
const { icon, iconFor } = this.options;
|
|
928
676
|
return ReactNodeViewRenderer(
|
|
929
|
-
({ node }) => /* @__PURE__ */
|
|
930
|
-
/* @__PURE__ */
|
|
931
|
-
/* @__PURE__ */
|
|
677
|
+
({ node }) => /* @__PURE__ */ jsxs2(NodeViewWrapper, { as: "span", className: inlineChipClassName("neutral", "max-w-[24ch] cursor-default"), children: [
|
|
678
|
+
/* @__PURE__ */ jsx3("span", { className: "flex items-center justify-center w-4 h-4 shrink-0 text-text-secondary", children: iconFor?.(node.attrs.uri ?? null) ?? icon }),
|
|
679
|
+
/* @__PURE__ */ jsx3("span", { className: "truncate", children: captionOf(node.attrs.label, node.attrs.snippet) })
|
|
932
680
|
] }),
|
|
933
681
|
{ as: "span" }
|
|
934
682
|
);
|
|
@@ -950,7 +698,7 @@ function referenceMenu({ ariaLabel, sectionLabel, icon, iconFor }) {
|
|
|
950
698
|
return groups;
|
|
951
699
|
},
|
|
952
700
|
itemKey: (item) => `reference-${item.id}`,
|
|
953
|
-
row: (item) => ({ leading: item.icon ?? iconFor?.(item.uri) ?? icon, label: captionOf(item.label, item.snippet), description: item.description, hint: /* @__PURE__ */
|
|
701
|
+
row: (item) => ({ leading: item.icon ?? iconFor?.(item.uri) ?? icon, label: captionOf(item.label, item.snippet), description: item.description, hint: /* @__PURE__ */ jsx3(EnterHint, {}) })
|
|
954
702
|
};
|
|
955
703
|
}
|
|
956
704
|
var listed = (options, query) => options.sections ? options.sections(query).flatMap((section) => section.items.map((item) => ({ ...item, section: section.label }))) : filterReferences(options.items(), query);
|
|
@@ -1103,6 +851,364 @@ var SlashCommands = Extension4.create({
|
|
|
1103
851
|
}
|
|
1104
852
|
});
|
|
1105
853
|
|
|
854
|
+
// src/EditorBlockHandle.tsx
|
|
855
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
856
|
+
function editorBlocks(view) {
|
|
857
|
+
const out = [];
|
|
858
|
+
view.state.doc.forEach((node, pos) => {
|
|
859
|
+
const element = view.nodeDOM(pos);
|
|
860
|
+
if (element instanceof HTMLElement) out.push({ id: node.attrs.blockId || void 0, element, pos });
|
|
861
|
+
});
|
|
862
|
+
return out;
|
|
863
|
+
}
|
|
864
|
+
function duplicateBlock(editor, pos) {
|
|
865
|
+
const node = editor.state.doc.nodeAt(pos);
|
|
866
|
+
if (!node) return false;
|
|
867
|
+
editor.view.dispatch(editor.state.tr.insert(pos + node.nodeSize, node).scrollIntoView());
|
|
868
|
+
editor.commands.focus(pos + node.nodeSize + 1);
|
|
869
|
+
return true;
|
|
870
|
+
}
|
|
871
|
+
function deleteBlock(editor, pos) {
|
|
872
|
+
const { state } = editor;
|
|
873
|
+
const node = state.doc.nodeAt(pos);
|
|
874
|
+
if (!node) return false;
|
|
875
|
+
const tr = state.doc.childCount === 1 ? state.tr.replaceWith(pos, pos + node.nodeSize, state.schema.nodes.paragraph.create()) : state.tr.delete(pos, pos + node.nodeSize);
|
|
876
|
+
editor.view.dispatch(tr);
|
|
877
|
+
editor.commands.focus(Math.min(pos + 1, editor.state.doc.content.size));
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
880
|
+
function moveBlock(editor, from, to) {
|
|
881
|
+
const node = editor.state.doc.nodeAt(from);
|
|
882
|
+
if (!node || to >= from && to <= from + node.nodeSize) return false;
|
|
883
|
+
const tr = editor.state.tr.delete(from, from + node.nodeSize);
|
|
884
|
+
const at = tr.mapping.map(to);
|
|
885
|
+
editor.view.dispatch(tr.insert(at, node).scrollIntoView());
|
|
886
|
+
editor.commands.focus(at + 1);
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
function startBlockDrag(editor, pos, event, image) {
|
|
890
|
+
editor.commands.setNodeSelection(pos);
|
|
891
|
+
const { view } = editor;
|
|
892
|
+
const slice = view.state.selection.content();
|
|
893
|
+
const { dom, text } = view.serializeForClipboard(slice);
|
|
894
|
+
const transfer = event.dataTransfer;
|
|
895
|
+
if (transfer) {
|
|
896
|
+
transfer.clearData();
|
|
897
|
+
transfer.setData("text/html", dom.innerHTML);
|
|
898
|
+
transfer.setData("text/plain", text);
|
|
899
|
+
transfer.effectAllowed = "copyMove";
|
|
900
|
+
if (image) transfer.setDragImage(image, 0, 0);
|
|
901
|
+
}
|
|
902
|
+
view.dragging = { slice, move: true };
|
|
903
|
+
}
|
|
904
|
+
var blockDropCursor = { color: false, class: "bg-accent-primary" };
|
|
905
|
+
var NOTHING_YET = { find: () => null, rows: () => null, startDrag: () => {
|
|
906
|
+
}, drop: () => {
|
|
907
|
+
}, endDrag: () => {
|
|
908
|
+
}, onChange: () => () => {
|
|
909
|
+
} };
|
|
910
|
+
function EditorBlockHandle({ editor, turnInto = turnIntoRows, actions, children, className }) {
|
|
911
|
+
const editing = useMemo(() => {
|
|
912
|
+
if (!editor) return NOTHING_YET;
|
|
913
|
+
let dragged;
|
|
914
|
+
return {
|
|
915
|
+
find: (y) => blockAtY(editorBlocks(editor.view), y),
|
|
916
|
+
rows: (block) => {
|
|
917
|
+
const pos = block.pos;
|
|
918
|
+
if (!editor.isEditable) return null;
|
|
919
|
+
const node = pos === void 0 ? null : editor.state.doc.nodeAt(pos);
|
|
920
|
+
if (pos === void 0 || !node) return null;
|
|
921
|
+
const turnRows = typeof turnInto === "function" ? turnInto(node, editor) : node.isTextblock ? turnInto : [];
|
|
922
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
923
|
+
turnRows.length > 0 && /* @__PURE__ */ jsx4(MenuSub, { label: "Turn into", children: turnRows.map((row) => /* @__PURE__ */ jsx4(
|
|
924
|
+
MenuItem,
|
|
925
|
+
{
|
|
926
|
+
label: row.label,
|
|
927
|
+
selected: row.active?.(node) ?? false,
|
|
928
|
+
onClick: () => {
|
|
929
|
+
if (node.isTextblock) editor.chain().focus().setTextSelection(pos + 1).run();
|
|
930
|
+
row.run(editor, pos);
|
|
931
|
+
}
|
|
932
|
+
},
|
|
933
|
+
row.label
|
|
934
|
+
)) }),
|
|
935
|
+
/* @__PURE__ */ jsx4(MenuItem, { label: "Duplicate", leading: /* @__PURE__ */ jsx4(IconCopy, { size: 16, stroke: 1.5, className: "text-text-secondary" }), onClick: () => duplicateBlock(editor, pos) }),
|
|
936
|
+
/* @__PURE__ */ jsx4(MenuItem, { label: "Delete", destructive: true, leading: /* @__PURE__ */ jsx4(IconTrash, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: () => deleteBlock(editor, pos) })
|
|
937
|
+
] });
|
|
938
|
+
},
|
|
939
|
+
startDrag: (block, event) => {
|
|
940
|
+
dragged = block.pos;
|
|
941
|
+
if (block.pos !== void 0) startBlockDrag(editor, block.pos, event, block.element);
|
|
942
|
+
},
|
|
943
|
+
drop: (target, after) => {
|
|
944
|
+
const node = target.pos === void 0 ? null : editor.state.doc.nodeAt(target.pos);
|
|
945
|
+
if (dragged === void 0 || target.pos === void 0 || !node) return;
|
|
946
|
+
moveBlock(editor, dragged, after ? target.pos + node.nodeSize : target.pos);
|
|
947
|
+
},
|
|
948
|
+
// ProseMirror clears `dragging` on its own dragend, which a drag started outside its element never reaches.
|
|
949
|
+
endDrag: () => setTimeout(() => editor.view.dragging = null, 50),
|
|
950
|
+
onChange: (changed) => {
|
|
951
|
+
editor.on("update", changed);
|
|
952
|
+
return () => {
|
|
953
|
+
editor.off("update", changed);
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
}, [editor, turnInto]);
|
|
958
|
+
return /* @__PURE__ */ jsx4(BlockHandle, { editing, actions, className, children });
|
|
959
|
+
}
|
|
960
|
+
var LISTS = ["bulletList", "orderedList"];
|
|
961
|
+
var isList = (node) => LISTS.includes(node.type.name);
|
|
962
|
+
function listItems(list, at) {
|
|
963
|
+
const out = [];
|
|
964
|
+
list.descendants((node, pos) => {
|
|
965
|
+
if (node.type.name === "listItem") out.push({ node, pos: at + 1 + pos });
|
|
966
|
+
});
|
|
967
|
+
return out;
|
|
968
|
+
}
|
|
969
|
+
var isChecklistItem = ({ node }) => typeof node.attrs.checked === "boolean";
|
|
970
|
+
var isChecklist = (list) => listItems(list, 0).every(isChecklistItem);
|
|
971
|
+
var isPlainList = (list, type) => list.type.name === type && !listItems(list, 0).some(isChecklistItem);
|
|
972
|
+
var hasExtension = (editor, name) => editor.extensionManager.extensions.some((e) => e.name === name);
|
|
973
|
+
function caretInto(editor, pos) {
|
|
974
|
+
const { state } = editor;
|
|
975
|
+
editor.view.dispatch(state.tr.setSelection(Selection.near(state.doc.resolve(Math.min(pos + 1, state.doc.content.size)))));
|
|
976
|
+
editor.commands.focus();
|
|
977
|
+
}
|
|
978
|
+
function turnListInto(editor, pos, kind) {
|
|
979
|
+
const { state } = editor;
|
|
980
|
+
const list = state.doc.nodeAt(pos);
|
|
981
|
+
if (!list || !isList(list)) return false;
|
|
982
|
+
const tr = state.tr;
|
|
983
|
+
if (kind === "text") {
|
|
984
|
+
const blocks = [];
|
|
985
|
+
for (const { node: item } of listItems(list, pos)) {
|
|
986
|
+
item.forEach((child, _offset, index) => {
|
|
987
|
+
if (isList(child)) return;
|
|
988
|
+
blocks.push(child.type.create({ ...child.attrs, blockId: index === 0 ? item.attrs.blockId : null }, child.content, child.marks));
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
tr.replaceWith(pos, pos + list.nodeSize, blocks);
|
|
992
|
+
} else {
|
|
993
|
+
const type = kind === "checklist" ? list.type : state.schema.nodes[kind];
|
|
994
|
+
if (list.type !== type) tr.setNodeMarkup(pos, type, list.attrs);
|
|
995
|
+
list.descendants((node, at) => {
|
|
996
|
+
if (isList(node) && node.type !== type) tr.setNodeMarkup(pos + 1 + at, type, node.attrs);
|
|
997
|
+
});
|
|
998
|
+
for (const { node, pos: at } of listItems(list, pos)) {
|
|
999
|
+
const checked = kind === "checklist" ? typeof node.attrs.checked === "boolean" ? node.attrs.checked : false : null;
|
|
1000
|
+
if (node.attrs.checked !== checked) tr.setNodeAttribute(at, "checked", checked);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
if (tr.docChanged) editor.view.dispatch(tr.scrollIntoView());
|
|
1004
|
+
caretInto(editor, pos);
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
function turnQuoteInto(editor, pos, kind) {
|
|
1008
|
+
const { state } = editor;
|
|
1009
|
+
const quote = state.doc.nodeAt(pos);
|
|
1010
|
+
if (!quote || quote.type.name !== "blockquote") return false;
|
|
1011
|
+
const tr = state.tr;
|
|
1012
|
+
if (kind === "text") {
|
|
1013
|
+
const blocks = [];
|
|
1014
|
+
quote.forEach((child) => blocks.push(blocks.length === 0 ? child.type.create({ ...child.attrs, blockId: quote.attrs.blockId }, child.content, child.marks) : child));
|
|
1015
|
+
tr.replaceWith(pos, pos + quote.nodeSize, blocks);
|
|
1016
|
+
} else {
|
|
1017
|
+
const tone = kind === "quote" ? null : kind;
|
|
1018
|
+
if ((quote.attrs.tone ?? null) !== tone) tr.setNodeAttribute(pos, "tone", tone);
|
|
1019
|
+
}
|
|
1020
|
+
if (tr.docChanged) editor.view.dispatch(tr.scrollIntoView());
|
|
1021
|
+
caretInto(editor, pos);
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
function isFormat(node, id) {
|
|
1025
|
+
if (id === "paragraph") return node.type.name === "paragraph";
|
|
1026
|
+
if (id === "heading") return node.type.name === "heading" && node.attrs.level === 1;
|
|
1027
|
+
if (id === "subheading") return node.type.name === "heading" && node.attrs.level === 2;
|
|
1028
|
+
if (id === "code") return node.type.name === "codeBlock";
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
1031
|
+
function turnIntoRows(node, editor) {
|
|
1032
|
+
const checklist = hasExtension(editor, "checklist");
|
|
1033
|
+
const callout = hasExtension(editor, "callout");
|
|
1034
|
+
if (node.isTextblock) {
|
|
1035
|
+
const document2 = DOCUMENT_FORMATS.filter((f) => f.id === "checklist" ? checklist : callout);
|
|
1036
|
+
return [...FORMATS, ...document2].map((format) => ({
|
|
1037
|
+
label: format.label,
|
|
1038
|
+
run: (e) => void applyFormat(e, format.id, "block"),
|
|
1039
|
+
active: (n) => isFormat(n, format.id)
|
|
1040
|
+
}));
|
|
1041
|
+
}
|
|
1042
|
+
if (isList(node)) {
|
|
1043
|
+
const rows = [
|
|
1044
|
+
{ label: "Bulleted list", run: (e, pos) => void turnListInto(e, pos, "bulletList"), active: (n) => isPlainList(n, "bulletList") },
|
|
1045
|
+
{ label: "Numbered list", run: (e, pos) => void turnListInto(e, pos, "orderedList"), active: (n) => isPlainList(n, "orderedList") }
|
|
1046
|
+
];
|
|
1047
|
+
if (checklist) rows.push({ label: "Checklist", run: (e, pos) => void turnListInto(e, pos, "checklist"), active: isChecklist });
|
|
1048
|
+
rows.push({ label: "Text", run: (e, pos) => void turnListInto(e, pos, "text") });
|
|
1049
|
+
return rows;
|
|
1050
|
+
}
|
|
1051
|
+
if (node.type.name === "blockquote") {
|
|
1052
|
+
const rows = [{ label: "Quote", run: (e, pos) => void turnQuoteInto(e, pos, "quote"), active: (n) => !n.attrs.tone }];
|
|
1053
|
+
if (callout) {
|
|
1054
|
+
rows.push({ label: "Note", run: (e, pos) => void turnQuoteInto(e, pos, "note"), active: (n) => n.attrs.tone === "note" });
|
|
1055
|
+
rows.push({ label: "Warning", run: (e, pos) => void turnQuoteInto(e, pos, "warning"), active: (n) => n.attrs.tone === "warning" });
|
|
1056
|
+
}
|
|
1057
|
+
rows.push({ label: "Text", run: (e, pos) => void turnQuoteInto(e, pos, "text") });
|
|
1058
|
+
return rows;
|
|
1059
|
+
}
|
|
1060
|
+
return [];
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/EditorTableControls.tsx
|
|
1064
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
|
|
1065
|
+
import { TextSelection } from "@tiptap/pm/state";
|
|
1066
|
+
import { addColumnAfter, addColumnBefore, addRowAfter, addRowBefore, deleteColumn, deleteRow, deleteTable, toggleHeader } from "@tiptap/pm/tables";
|
|
1067
|
+
import { IconColumnInsertLeft, IconColumnInsertRight, IconGripHorizontal, IconGripVertical, IconPlus, IconRowInsertBottom, IconRowInsertTop, IconTrash as IconTrash2 } from "@tabler/icons-react";
|
|
1068
|
+
import { cn } from "@estiva-app/ui";
|
|
1069
|
+
import { IconButton } from "@estiva-app/ui";
|
|
1070
|
+
|
|
1071
|
+
// src/looks.ts
|
|
1072
|
+
var FLOATING_SURFACE_CLASSES = "rounded-lg border border-border-default bg-bg-elevated shadow-lg";
|
|
1073
|
+
|
|
1074
|
+
// src/EditorTableControls.tsx
|
|
1075
|
+
import { Menu, MenuItem as MenuItem2, MenuSeparator } from "@estiva-app/ui";
|
|
1076
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1077
|
+
function inCell(editor, cell, command) {
|
|
1078
|
+
const { view } = editor;
|
|
1079
|
+
if (!view.dom.contains(cell)) return false;
|
|
1080
|
+
const at = view.posAtDOM(cell, 0);
|
|
1081
|
+
view.dispatch(view.state.tr.setSelection(TextSelection.near(view.state.doc.resolve(at))));
|
|
1082
|
+
const done = command(view.state, view.dispatch, view);
|
|
1083
|
+
editor.commands.focus();
|
|
1084
|
+
return done;
|
|
1085
|
+
}
|
|
1086
|
+
function isHeaderRow(row) {
|
|
1087
|
+
return row.cells.length > 0 && Array.from(row.cells).every((c) => c.tagName === "TH");
|
|
1088
|
+
}
|
|
1089
|
+
function deleteTableRow(editor, cell) {
|
|
1090
|
+
const row = cell.closest("tr");
|
|
1091
|
+
const table = cell.closest("table");
|
|
1092
|
+
if (!row || !table || table.rows.length <= 1) return inCell(editor, cell, deleteTable);
|
|
1093
|
+
const header = isHeaderRow(row);
|
|
1094
|
+
return inCell(editor, cell, header ? (state, dispatch, view) => deleteRow(state, dispatch) && (!view || toggleHeader("row")(view.state, view.dispatch)) : deleteRow);
|
|
1095
|
+
}
|
|
1096
|
+
function deleteTableColumn(editor, cell) {
|
|
1097
|
+
const row = cell.closest("tr");
|
|
1098
|
+
return inCell(editor, cell, row && row.cells.length > 1 ? deleteColumn : deleteTable);
|
|
1099
|
+
}
|
|
1100
|
+
function appendTableRow(editor, table) {
|
|
1101
|
+
const last = table.rows[table.rows.length - 1]?.cells[0];
|
|
1102
|
+
return !!last && inCell(editor, last, addRowAfter);
|
|
1103
|
+
}
|
|
1104
|
+
function appendTableColumn(editor, table) {
|
|
1105
|
+
const first = table.rows[0];
|
|
1106
|
+
const last = first?.cells[first.cells.length - 1];
|
|
1107
|
+
return !!last && inCell(editor, last, addColumnAfter);
|
|
1108
|
+
}
|
|
1109
|
+
function place(frame, { table, row, cell }) {
|
|
1110
|
+
const at = frame.getBoundingClientRect();
|
|
1111
|
+
const t = table.getBoundingClientRect();
|
|
1112
|
+
const r = row.getBoundingClientRect();
|
|
1113
|
+
const c = cell.getBoundingClientRect();
|
|
1114
|
+
return {
|
|
1115
|
+
tableBox: { top: t.top - at.top, left: t.left - at.left, width: t.width, height: t.height },
|
|
1116
|
+
rowBox: { top: r.top - at.top, height: r.height },
|
|
1117
|
+
columnBox: { left: c.left - at.left, width: c.width }
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
var ICON = { size: 16, stroke: 1.5, className: "text-text-secondary" };
|
|
1121
|
+
var keepFocus = (e) => e.preventDefault();
|
|
1122
|
+
var HANDLE_BOX = cn(FLOATING_SURFACE_CLASSES, "shadow-none");
|
|
1123
|
+
function EditorTableControls({ editor, children, className }) {
|
|
1124
|
+
const frame = useRef3(null);
|
|
1125
|
+
const [hovered, setHovered] = useState2(null);
|
|
1126
|
+
const [menu, setMenu] = useState2(null);
|
|
1127
|
+
const editable = !!editor && editor.isEditable;
|
|
1128
|
+
const follow = useCallback2(
|
|
1129
|
+
(target) => {
|
|
1130
|
+
if (!editor || !frame.current || !(target instanceof Element)) return;
|
|
1131
|
+
if (target.closest("[data-table-control]")) return;
|
|
1132
|
+
const cell = target.closest("td, th");
|
|
1133
|
+
const row = cell?.closest("tr");
|
|
1134
|
+
const table = row?.closest("table");
|
|
1135
|
+
if (!cell || !row || !table || !editor.view.dom.contains(table)) {
|
|
1136
|
+
setHovered(null);
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
const next = { table, row, cell, ...place(frame.current, { table, row, cell }) };
|
|
1140
|
+
setHovered((was) => was && was.cell === cell && JSON.stringify([was.tableBox, was.rowBox, was.columnBox]) === JSON.stringify([next.tableBox, next.rowBox, next.columnBox]) ? was : next);
|
|
1141
|
+
},
|
|
1142
|
+
[editor]
|
|
1143
|
+
);
|
|
1144
|
+
useEffect2(() => {
|
|
1145
|
+
if (!editor) return;
|
|
1146
|
+
const changed = ({ transaction }) => {
|
|
1147
|
+
if (transaction.docChanged && !menu) setHovered(null);
|
|
1148
|
+
};
|
|
1149
|
+
editor.on("transaction", changed);
|
|
1150
|
+
return () => {
|
|
1151
|
+
editor.off("transaction", changed);
|
|
1152
|
+
};
|
|
1153
|
+
}, [editor, menu]);
|
|
1154
|
+
const shown = editable ? hovered : null;
|
|
1155
|
+
const run = (act) => () => {
|
|
1156
|
+
if (editor) act();
|
|
1157
|
+
setMenu(null);
|
|
1158
|
+
setHovered(null);
|
|
1159
|
+
};
|
|
1160
|
+
return /* @__PURE__ */ jsxs4(
|
|
1161
|
+
"div",
|
|
1162
|
+
{
|
|
1163
|
+
ref: frame,
|
|
1164
|
+
className: cn("relative", className),
|
|
1165
|
+
onMouseMove: (e) => {
|
|
1166
|
+
if (!menu) follow(e.target);
|
|
1167
|
+
},
|
|
1168
|
+
onMouseLeave: () => {
|
|
1169
|
+
if (!menu) setHovered(null);
|
|
1170
|
+
},
|
|
1171
|
+
children: [
|
|
1172
|
+
children,
|
|
1173
|
+
shown && editor && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1174
|
+
/* @__PURE__ */ jsx5("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__ */ jsxs4(
|
|
1175
|
+
Menu,
|
|
1176
|
+
{
|
|
1177
|
+
open: menu === "row",
|
|
1178
|
+
onOpenChange: (open) => setMenu(open ? "row" : null),
|
|
1179
|
+
trigger: /* @__PURE__ */ jsx5(IconButton, { "aria-label": "Row menu", tooltip: "Row", className: "h-6 w-4 p-0", children: /* @__PURE__ */ jsx5(IconGripVertical, { size: 16, stroke: 1.5 }) }),
|
|
1180
|
+
children: [
|
|
1181
|
+
!isHeaderRow(shown.row) && /* @__PURE__ */ jsx5(MenuItem2, { label: "Insert above", leading: /* @__PURE__ */ jsx5(IconRowInsertTop, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addRowBefore)) }),
|
|
1182
|
+
/* @__PURE__ */ jsx5(MenuItem2, { label: "Insert below", leading: /* @__PURE__ */ jsx5(IconRowInsertBottom, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addRowAfter)) }),
|
|
1183
|
+
/* @__PURE__ */ jsx5(MenuSeparator, {}),
|
|
1184
|
+
/* @__PURE__ */ jsx5(MenuItem2, { label: "Delete", destructive: true, leading: /* @__PURE__ */ jsx5(IconTrash2, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: run(() => deleteTableRow(editor, shown.cell)) }),
|
|
1185
|
+
/* @__PURE__ */ jsx5(MenuItem2, { label: "Delete table", destructive: true, leading: /* @__PURE__ */ jsx5(IconTrash2, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: run(() => inCell(editor, shown.cell, deleteTable)) })
|
|
1186
|
+
]
|
|
1187
|
+
}
|
|
1188
|
+
) }),
|
|
1189
|
+
/* @__PURE__ */ jsx5("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__ */ jsxs4(
|
|
1190
|
+
Menu,
|
|
1191
|
+
{
|
|
1192
|
+
open: menu === "column",
|
|
1193
|
+
onOpenChange: (open) => setMenu(open ? "column" : null),
|
|
1194
|
+
trigger: /* @__PURE__ */ jsx5(IconButton, { "aria-label": "Column menu", tooltip: "Column", className: "h-4 w-6 p-0", children: /* @__PURE__ */ jsx5(IconGripHorizontal, { size: 16, stroke: 1.5 }) }),
|
|
1195
|
+
children: [
|
|
1196
|
+
/* @__PURE__ */ jsx5(MenuItem2, { label: "Insert left", leading: /* @__PURE__ */ jsx5(IconColumnInsertLeft, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addColumnBefore)) }),
|
|
1197
|
+
/* @__PURE__ */ jsx5(MenuItem2, { label: "Insert right", leading: /* @__PURE__ */ jsx5(IconColumnInsertRight, { ...ICON }), onClick: run(() => inCell(editor, shown.cell, addColumnAfter)) }),
|
|
1198
|
+
/* @__PURE__ */ jsx5(MenuSeparator, {}),
|
|
1199
|
+
/* @__PURE__ */ jsx5(MenuItem2, { label: "Delete", destructive: true, leading: /* @__PURE__ */ jsx5(IconTrash2, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: run(() => deleteTableColumn(editor, shown.cell)) }),
|
|
1200
|
+
/* @__PURE__ */ jsx5(MenuItem2, { label: "Delete table", destructive: true, leading: /* @__PURE__ */ jsx5(IconTrash2, { size: 16, stroke: 1.5, className: "text-error-default" }), onClick: run(() => inCell(editor, shown.cell, deleteTable)) })
|
|
1201
|
+
]
|
|
1202
|
+
}
|
|
1203
|
+
) }),
|
|
1204
|
+
shown.row === shown.table.rows[shown.table.rows.length - 1] && /* @__PURE__ */ jsx5("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__ */ jsx5(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__ */ jsx5(IconPlus, { size: 12, stroke: 1.5 }) }) }),
|
|
1205
|
+
shown.cell === shown.row.cells[shown.row.cells.length - 1] && /* @__PURE__ */ jsx5("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__ */ jsx5(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__ */ jsx5(IconPlus, { size: 12, stroke: 1.5 }) }) })
|
|
1206
|
+
] })
|
|
1207
|
+
]
|
|
1208
|
+
}
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1106
1212
|
// src/BlockNodes.tsx
|
|
1107
1213
|
import { Extension as Extension8, InputRule, Node as Node2 } from "@tiptap/core";
|
|
1108
1214
|
import { DOMSerializer } from "@tiptap/pm/model";
|
|
@@ -1683,7 +1789,7 @@ function claimsIn(doc) {
|
|
|
1683
1789
|
doc.descendants((node, pos, parent) => {
|
|
1684
1790
|
const fixed = node.type.name === "unknownBlock";
|
|
1685
1791
|
if (!fixed && !BLOCK_TYPE_SET.has(node.type.name)) return;
|
|
1686
|
-
if (parent?.type.name === "listItem" &&
|
|
1792
|
+
if (parent?.type.name === "listItem" && LISTS2.has(node.type.name)) return;
|
|
1687
1793
|
const id = node.attrs.blockId;
|
|
1688
1794
|
if (typeof id !== "string" || id === "") return;
|
|
1689
1795
|
const copies = claims.get(id);
|
|
@@ -1730,11 +1836,11 @@ function repeatedBlockIds(doc, before, mapping) {
|
|
|
1730
1836
|
}
|
|
1731
1837
|
var BLOCK_CONTAINERS = /* @__PURE__ */ new Set(["bulletList", "orderedList", "table", "tableRow", "tableHeader", "tableCell"]);
|
|
1732
1838
|
var FLATTENED = /* @__PURE__ */ new Set(["listItem", "blockquote"]);
|
|
1733
|
-
var
|
|
1839
|
+
var LISTS2 = /* @__PURE__ */ new Set(["bulletList", "orderedList"]);
|
|
1734
1840
|
function innerLists(item, pos) {
|
|
1735
1841
|
const out = [];
|
|
1736
1842
|
item.forEach((child, offset) => {
|
|
1737
|
-
if (
|
|
1843
|
+
if (LISTS2.has(child.type.name)) out.push({ pos: pos + 1 + offset, node: child });
|
|
1738
1844
|
});
|
|
1739
1845
|
return out;
|
|
1740
1846
|
}
|
|
@@ -1763,7 +1869,7 @@ function hiddenIds(doc) {
|
|
|
1763
1869
|
doc.descendants((node, pos) => {
|
|
1764
1870
|
if (!FLATTENED.has(node.type.name)) return;
|
|
1765
1871
|
node.forEach((child, offset) => {
|
|
1766
|
-
const inner = child.isTextblock || node.type.name === "listItem" &&
|
|
1872
|
+
const inner = child.isTextblock || node.type.name === "listItem" && LISTS2.has(child.type.name);
|
|
1767
1873
|
if (inner && idOf(child) !== void 0) out.push(pos + 1 + offset);
|
|
1768
1874
|
});
|
|
1769
1875
|
});
|
|
@@ -2186,6 +2292,9 @@ export {
|
|
|
2186
2292
|
setReferenceCardAt,
|
|
2187
2293
|
startBlockDrag,
|
|
2188
2294
|
suggestionPopup,
|
|
2295
|
+
turnIntoRows,
|
|
2296
|
+
turnListInto,
|
|
2297
|
+
turnQuoteInto,
|
|
2189
2298
|
typeTrigger,
|
|
2190
2299
|
urgentPubkeys,
|
|
2191
2300
|
useReferenceCards
|