@monolith-forensics/monolith-ui 1.9.1-dev.8 → 1.9.3-dev.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 (93) hide show
  1. package/dist/DropDownMenu/components/MenuItemList.js +32 -12
  2. package/dist/DropDownMenu/components/StyledContent.js +1 -1
  3. package/dist/DropDownMenu/components/StyledInnerItemContainer.js +1 -0
  4. package/dist/FileViewer/FileViewer.js +32 -8
  5. package/dist/FileViewer/viewers/ImageViewer.d.ts +1 -0
  6. package/dist/FileViewer/viewers/ImageViewer.js +36 -15
  7. package/dist/MonolithUIProvider/MonolithUIProvider.d.ts +23 -0
  8. package/dist/RichTextEditor/Components/BubbleMenu.d.ts +8 -8
  9. package/dist/RichTextEditor/Components/BubbleMenu.js +202 -94
  10. package/dist/RichTextEditor/Components/CodeBlockBaseButton.d.ts +18 -0
  11. package/dist/RichTextEditor/Components/CodeBlockBaseButton.js +6 -0
  12. package/dist/RichTextEditor/Components/CodeBlockCopyButton.d.ts +9 -0
  13. package/dist/RichTextEditor/Components/CodeBlockCopyButton.js +42 -0
  14. package/dist/RichTextEditor/Components/CodeBlockFormatButton.d.ts +10 -0
  15. package/dist/RichTextEditor/Components/CodeBlockFormatButton.js +60 -0
  16. package/dist/RichTextEditor/Components/CodeBlockLanguageSelect.d.ts +9 -0
  17. package/dist/RichTextEditor/Components/CodeBlockLanguageSelect.js +30 -0
  18. package/dist/RichTextEditor/Components/CodeBlockNodeView.d.ts +3 -0
  19. package/dist/RichTextEditor/Components/CodeBlockNodeView.js +28 -0
  20. package/dist/RichTextEditor/Components/CodeBlockWrapButton.d.ts +10 -0
  21. package/dist/RichTextEditor/Components/CodeBlockWrapButton.js +17 -0
  22. package/dist/RichTextEditor/Components/LinkEditor.d.ts +8 -0
  23. package/dist/RichTextEditor/Components/LinkEditor.js +94 -0
  24. package/dist/RichTextEditor/Components/TableCornerMenu.d.ts +16 -0
  25. package/dist/RichTextEditor/Components/TableCornerMenu.js +202 -0
  26. package/dist/RichTextEditor/Components/TableTools/TableCornerMenu.d.ts +2 -0
  27. package/dist/RichTextEditor/Components/TableTools/TableCornerMenu.js +19 -0
  28. package/dist/RichTextEditor/Components/TableTools/TableInsertControls.d.ts +2 -0
  29. package/dist/RichTextEditor/Components/TableTools/TableInsertControls.js +24 -0
  30. package/dist/RichTextEditor/Components/TableTools/TableRails.d.ts +2 -0
  31. package/dist/RichTextEditor/Components/TableTools/TableRails.js +180 -0
  32. package/dist/RichTextEditor/Components/TableTools/TableToolMenu.d.ts +5 -0
  33. package/dist/RichTextEditor/Components/TableTools/TableToolMenu.js +6 -0
  34. package/dist/RichTextEditor/Components/TableTools/TableTools.actions.d.ts +5 -0
  35. package/dist/RichTextEditor/Components/TableTools/TableTools.actions.js +183 -0
  36. package/dist/RichTextEditor/Components/TableTools/TableTools.commands.d.ts +16 -0
  37. package/dist/RichTextEditor/Components/TableTools/TableTools.commands.js +217 -0
  38. package/dist/RichTextEditor/Components/TableTools/TableTools.constants.d.ts +8 -0
  39. package/dist/RichTextEditor/Components/TableTools/TableTools.constants.js +11 -0
  40. package/dist/RichTextEditor/Components/TableTools/TableTools.d.ts +3 -0
  41. package/dist/RichTextEditor/Components/TableTools/TableTools.geometry.d.ts +23 -0
  42. package/dist/RichTextEditor/Components/TableTools/TableTools.geometry.js +75 -0
  43. package/dist/RichTextEditor/Components/TableTools/TableTools.js +3 -0
  44. package/dist/RichTextEditor/Components/TableTools/TableTools.selectors.d.ts +16 -0
  45. package/dist/RichTextEditor/Components/TableTools/TableTools.selectors.js +53 -0
  46. package/dist/RichTextEditor/Components/TableTools/TableTools.styled.d.ts +40 -0
  47. package/dist/RichTextEditor/Components/TableTools/TableTools.styled.js +167 -0
  48. package/dist/RichTextEditor/Components/TableTools/TableTools.types.d.ts +76 -0
  49. package/dist/RichTextEditor/Components/TableTools/TableTools.types.js +1 -0
  50. package/dist/RichTextEditor/Components/TableTools/TableTools.utils.d.ts +4 -0
  51. package/dist/RichTextEditor/Components/TableTools/TableTools.utils.js +4 -0
  52. package/dist/RichTextEditor/Components/TableTools/TableToolsPopover.d.ts +2 -0
  53. package/dist/RichTextEditor/Components/TableTools/TableToolsPopover.js +12 -0
  54. package/dist/RichTextEditor/Components/TableTools/index.d.ts +3 -0
  55. package/dist/RichTextEditor/Components/TableTools/index.js +2 -0
  56. package/dist/RichTextEditor/Components/TableTools.d.ts +44 -0
  57. package/dist/RichTextEditor/Components/TableTools.js +790 -0
  58. package/dist/RichTextEditor/Enums/Controls.d.ts +7 -1
  59. package/dist/RichTextEditor/Enums/Controls.js +6 -0
  60. package/dist/RichTextEditor/Enums/Extensions.d.ts +4 -0
  61. package/dist/RichTextEditor/Enums/Extensions.js +4 -0
  62. package/dist/RichTextEditor/Enums/HighlightColors.d.ts +9 -0
  63. package/dist/RichTextEditor/Enums/HighlightColors.js +10 -0
  64. package/dist/RichTextEditor/Enums/SlashCommands.d.ts +4 -1
  65. package/dist/RichTextEditor/Enums/SlashCommands.js +3 -0
  66. package/dist/RichTextEditor/Extensions/SlashCommandList.js +0 -1
  67. package/dist/RichTextEditor/Extensions/getSlashCommand.js +39 -1
  68. package/dist/RichTextEditor/Extensions/getTiptapExtensions.d.ts +10 -2
  69. package/dist/RichTextEditor/Extensions/getTiptapExtensions.js +157 -30
  70. package/dist/RichTextEditor/Plugins/ImageActionsPlugin.js +4 -7
  71. package/dist/RichTextEditor/RichTextEditor.d.ts +4 -2
  72. package/dist/RichTextEditor/RichTextEditor.js +395 -15
  73. package/dist/RichTextEditor/Toolbar/Control.d.ts +6 -2
  74. package/dist/RichTextEditor/Toolbar/Control.js +13 -6
  75. package/dist/RichTextEditor/Toolbar/Controls.d.ts +6 -0
  76. package/dist/RichTextEditor/Toolbar/Controls.js +118 -1
  77. package/dist/RichTextEditor/Toolbar/ControlsGroup.js +17 -6
  78. package/dist/RichTextEditor/Toolbar/Labels.d.ts +1 -0
  79. package/dist/RichTextEditor/Toolbar/Labels.js +1 -0
  80. package/dist/RichTextEditor/Toolbar/Toolbar.d.ts +1 -2
  81. package/dist/RichTextEditor/Toolbar/Toolbar.js +32 -67
  82. package/dist/RichTextEditor/Utils/codeBlockUtils.d.ts +20 -0
  83. package/dist/RichTextEditor/Utils/codeBlockUtils.js +137 -0
  84. package/dist/RichTextEditor/Utils/codeUtils.d.ts +3 -0
  85. package/dist/RichTextEditor/Utils/codeUtils.js +12 -0
  86. package/dist/RichTextEditor/Utils/linkUtils.d.ts +19 -0
  87. package/dist/RichTextEditor/Utils/linkUtils.js +57 -0
  88. package/dist/RichTextEditor/Utils/tableUtils.d.ts +1 -0
  89. package/dist/RichTextEditor/Utils/tableUtils.js +1 -0
  90. package/dist/theme/variants.js +46 -0
  91. package/package.json +8 -1
  92. package/dist/RichTextEditor/Extensions/BubbleMenuExtension.d.ts +0 -7
  93. package/dist/RichTextEditor/Extensions/BubbleMenuExtension.js +0 -157
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { WrapTextIcon } from "lucide-react";
3
+ import { getCodeBlockWrap, toggleCodeBlockWrap } from "../Utils/codeBlockUtils";
4
+ import { CodeBlockBaseButton } from "./CodeBlockBaseButton";
5
+ const CodeBlockWrapButton = ({ className, editor, active, onToggle, size = "xs", }) => {
6
+ const isWrapped = active !== null && active !== void 0 ? active : getCodeBlockWrap(editor || null);
7
+ return (_jsx(CodeBlockBaseButton, { className: className, size: size, variant: "outlined", title: isWrapped ? "Disable code wrapping" : "Enable code wrapping", "aria-label": isWrapped ? "Disable code wrapping" : "Enable code wrapping", selected: isWrapped, disabled: !onToggle && !(editor === null || editor === void 0 ? void 0 : editor.isActive("codeBlock")), onClick: (event) => {
8
+ event.preventDefault();
9
+ event.stopPropagation();
10
+ if (onToggle) {
11
+ onToggle();
12
+ return;
13
+ }
14
+ toggleCodeBlockWrap(editor || null);
15
+ }, children: _jsx(WrapTextIcon, { size: 14 }) }));
16
+ };
17
+ export default CodeBlockWrapButton;
@@ -0,0 +1,8 @@
1
+ import { Editor } from "@tiptap/react";
2
+ type LinkEditorProps = {
3
+ editor: Editor;
4
+ onClose?: () => void;
5
+ autoFocus?: boolean;
6
+ };
7
+ export declare const LinkEditor: React.FC<LinkEditorProps>;
8
+ export default LinkEditor;
@@ -0,0 +1,94 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState, } from "react";
3
+ import styled from "styled-components";
4
+ import { ExternalLinkIcon, SaveIcon, UnlinkIcon } from "lucide-react";
5
+ import Input from "../../Input";
6
+ import { Button } from "../../Button";
7
+ import { getLinkAttributes, normalizeLinkUrl, openLink, } from "../Utils/linkUtils";
8
+ const LinkEditorContent = styled.form `
9
+ display: flex;
10
+ align-items: center;
11
+ gap: 6px;
12
+ min-width: 280px;
13
+ `;
14
+ const LinkInput = styled(Input) `
15
+ min-width: 190px;
16
+ `;
17
+ const LinkActions = styled.div `
18
+ display: flex;
19
+ align-items: center;
20
+ gap: 4px;
21
+ `;
22
+ const LinkEditorButton = styled(Button) `
23
+ padding: 0 6px;
24
+ `;
25
+ const getInitialHref = (editor) => editor.getAttributes("link").href || "";
26
+ export const LinkEditor = ({ editor, onClose, autoFocus, }) => {
27
+ const inputRef = useRef(null);
28
+ const [href, setHref] = useState(() => getInitialHref(editor));
29
+ const currentHref = getInitialHref(editor);
30
+ const normalizedLink = useMemo(() => normalizeLinkUrl(href), [href]);
31
+ const hasLink = Boolean(currentHref);
32
+ useEffect(() => {
33
+ setHref(getInitialHref(editor));
34
+ }, [editor, editor.state.selection.from, editor.state.selection.to]);
35
+ useEffect(() => {
36
+ if (!autoFocus)
37
+ return;
38
+ window.requestAnimationFrame(() => {
39
+ var _a, _b;
40
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
41
+ (_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.select();
42
+ });
43
+ }, [autoFocus]);
44
+ const handleSave = () => {
45
+ if (!normalizedLink.isValid)
46
+ return;
47
+ const { from, to, empty } = editor.state.selection;
48
+ const attributes = getLinkAttributes(normalizedLink.href);
49
+ if (empty && hasLink) {
50
+ editor.chain().focus().extendMarkRange("link").setLink(attributes).run();
51
+ }
52
+ else if (empty) {
53
+ editor
54
+ .chain()
55
+ .focus()
56
+ .insertContent({
57
+ type: "text",
58
+ text: normalizedLink.href,
59
+ marks: [
60
+ {
61
+ type: "link",
62
+ attrs: attributes,
63
+ },
64
+ ],
65
+ })
66
+ .run();
67
+ }
68
+ else {
69
+ editor
70
+ .chain()
71
+ .focus()
72
+ .setTextSelection({ from, to })
73
+ .setLink(attributes)
74
+ .run();
75
+ }
76
+ onClose === null || onClose === void 0 ? void 0 : onClose();
77
+ };
78
+ const handleRemove = () => {
79
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
80
+ onClose === null || onClose === void 0 ? void 0 : onClose();
81
+ };
82
+ const handleSubmit = (event) => {
83
+ event.preventDefault();
84
+ handleSave();
85
+ };
86
+ const handleKeyDown = (event) => {
87
+ if (event.key === "Escape") {
88
+ event.preventDefault();
89
+ onClose === null || onClose === void 0 ? void 0 : onClose();
90
+ }
91
+ };
92
+ return (_jsxs(LinkEditorContent, { onSubmit: handleSubmit, children: [_jsx(LinkInput, { ref: inputRef, "aria-label": "Enter URL", placeholder: "https://example.com/", size: "xs", variant: "outlined", value: href, onChange: (event) => setHref(event.target.value), onKeyDown: handleKeyDown }), _jsxs(LinkActions, { children: [hasLink && (_jsxs(_Fragment, { children: [_jsx(LinkEditorButton, { "aria-label": "Open link", title: "Open link", size: "xs", variant: "subtle", type: "button", onClick: () => openLink(currentHref), children: _jsx(ExternalLinkIcon, { size: 14 }) }), _jsx(LinkEditorButton, { "aria-label": "Remove link", title: "Remove link", size: "xs", variant: "subtle", type: "button", onClick: handleRemove, children: _jsx(UnlinkIcon, { size: 14 }) })] })), _jsx(LinkEditorButton, { "aria-label": "Save link", title: "Save link", size: "xs", variant: "subtle", type: "submit", disabled: !normalizedLink.isValid, children: _jsx(SaveIcon, { size: 14 }) })] })] }));
93
+ };
94
+ export default LinkEditor;
@@ -0,0 +1,16 @@
1
+ import { Editor } from "@tiptap/react";
2
+ import type { MouseEvent as ReactMouseEvent } from "react";
3
+ import type { TableRailTarget } from "./TableTools";
4
+ type TableCornerMenuProps = {
5
+ editor: Editor | null;
6
+ target: TableRailTarget;
7
+ opened: boolean;
8
+ onOpen: () => void;
9
+ onClose: () => void;
10
+ onAction: () => void;
11
+ onMouseEnter: () => void;
12
+ onMouseLeave: () => void;
13
+ onMouseDown: (event: ReactMouseEvent<HTMLButtonElement>) => void;
14
+ };
15
+ export declare const TableCornerMenu: ({ editor, target, opened, onOpen, onClose, onAction, onMouseEnter, onMouseLeave, onMouseDown, }: TableCornerMenuProps) => import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,202 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TextSelection } from "@tiptap/pm/state";
3
+ import { CopyIcon, EraserIcon, TablePropertiesIcon, Trash2Icon, } from "lucide-react";
4
+ import styled from "styled-components";
5
+ import { Button } from "../../Button";
6
+ import { getControlSizeTokens } from "../../core";
7
+ import { Popover } from "../../Popover";
8
+ const CORNER_BUTTON_SIZE = 18;
9
+ const VIEWPORT_PADDING = 8;
10
+ const clearTableContents = (editor, target) => {
11
+ if (!editor)
12
+ return false;
13
+ const paragraph = editor.state.schema.nodes.paragraph.createAndFill();
14
+ if (!paragraph)
15
+ return false;
16
+ const tr = editor.state.tr;
17
+ const { tableContext } = target;
18
+ const cells = tableContext.map
19
+ .cellsInRect({
20
+ left: 0,
21
+ right: tableContext.map.width,
22
+ top: 0,
23
+ bottom: tableContext.map.height,
24
+ })
25
+ .map((pos) => ({
26
+ pos,
27
+ node: tableContext.node.nodeAt(pos),
28
+ }))
29
+ .filter((cell) => Boolean(cell.node))
30
+ .sort((a, b) => b.pos - a.pos);
31
+ cells.forEach((cell) => {
32
+ const from = tableContext.start + cell.pos + 1;
33
+ const to = tableContext.start + cell.pos + cell.node.nodeSize - 1;
34
+ tr.replaceWith(from, to, paragraph);
35
+ });
36
+ editor.view.dispatch(tr);
37
+ return true;
38
+ };
39
+ const duplicateTable = (editor, target) => {
40
+ if (!editor)
41
+ return false;
42
+ const { tableContext } = target;
43
+ const paragraph = editor.state.schema.nodes.paragraph.createAndFill();
44
+ if (!paragraph)
45
+ return false;
46
+ const insertPosition = tableContext.pos + tableContext.node.nodeSize;
47
+ const duplicatedTable = tableContext.node.copy(tableContext.node.content);
48
+ const tr = editor.state.tr.insert(insertPosition, [
49
+ paragraph,
50
+ duplicatedTable,
51
+ ]);
52
+ const firstCellPosition = tableContext.map.positionAt(0, 0, duplicatedTable);
53
+ if (firstCellPosition !== null) {
54
+ const duplicatePosition = insertPosition + paragraph.nodeSize;
55
+ const firstCellContentPosition = duplicatePosition + 1 + firstCellPosition + 1;
56
+ tr.setSelection(TextSelection.near(tr.doc.resolve(firstCellContentPosition)));
57
+ }
58
+ editor.view.dispatch(tr.scrollIntoView());
59
+ return true;
60
+ };
61
+ const deleteTable = (editor, target) => {
62
+ if (!editor)
63
+ return false;
64
+ const { tableContext } = target;
65
+ editor.view.dispatch(editor.state.tr
66
+ .delete(tableContext.pos, tableContext.pos + tableContext.node.nodeSize)
67
+ .scrollIntoView());
68
+ return true;
69
+ };
70
+ const getTableActionGroups = (editor, target) => [
71
+ {
72
+ label: "Content",
73
+ actions: [
74
+ {
75
+ label: "Clear table contents",
76
+ leftSection: _jsx(EraserIcon, { size: 14 }),
77
+ onClick: () => clearTableContents(editor, target),
78
+ },
79
+ {
80
+ label: "Duplicate table",
81
+ leftSection: _jsx(CopyIcon, { size: 14 }),
82
+ onClick: () => duplicateTable(editor, target),
83
+ },
84
+ ],
85
+ },
86
+ {
87
+ label: "Delete",
88
+ actions: [
89
+ {
90
+ label: "Delete table",
91
+ leftSection: _jsx(Trash2Icon, { size: 14 }),
92
+ danger: true,
93
+ onClick: () => deleteTable(editor, target),
94
+ },
95
+ ],
96
+ },
97
+ ];
98
+ const getCornerRect = (target) => ({
99
+ top: Math.max(VIEWPORT_PADDING, target.tableRect.top - CORNER_BUTTON_SIZE),
100
+ left: Math.max(VIEWPORT_PADDING, target.tableRect.left - CORNER_BUTTON_SIZE),
101
+ });
102
+ const CornerRoot = styled.div `
103
+ display: flex;
104
+ position: fixed;
105
+ top: ${({ $top }) => `${$top}px`};
106
+ left: ${({ $left }) => `${$left}px`};
107
+ z-index: 1500;
108
+ width: ${CORNER_BUTTON_SIZE}px;
109
+ height: ${CORNER_BUTTON_SIZE}px;
110
+
111
+ button {
112
+ width: 100%;
113
+ height: 100%;
114
+ min-height: 0;
115
+ padding: 0;
116
+ border-radius: 6px;
117
+ background-color: ${({ theme }) => theme.palette.background.paper};
118
+ color: ${({ theme }) => theme.palette.text.secondary};
119
+
120
+ &:hover {
121
+ background-color: ${({ theme }) => theme.palette.primary.main};
122
+ color: white;
123
+ }
124
+ }
125
+ `;
126
+ const TableMenuPanel = styled.div `
127
+ display: flex;
128
+ flex-direction: column;
129
+ min-width: 220px;
130
+ color: ${({ theme }) => theme.palette.text.primary};
131
+ `;
132
+ const TableMenuDropdown = styled(Popover.Dropdown) `
133
+ min-width: 180px;
134
+ border-radius: 5px;
135
+ border: 1px solid ${({ theme }) => theme.palette.divider};
136
+ background-color: ${({ theme }) => theme.palette.input.background};
137
+ box-shadow: none;
138
+ padding: 5px;
139
+ `;
140
+ const TableMenuGroup = styled.div `
141
+ display: flex;
142
+ flex-direction: column;
143
+ gap: 1px;
144
+ padding-top: ${({ $withDivider }) => ($withDivider ? "5px" : 0)};
145
+ margin-top: ${({ $withDivider }) => ($withDivider ? "5px" : 0)};
146
+ border-top: ${({ $withDivider, theme }) => $withDivider ? `1px solid ${theme.palette.divider}` : 0};
147
+ `;
148
+ const TableMenuButton = styled(Button) `
149
+ min-width: 100%;
150
+ height: ${getControlSizeTokens("sm").menuRowHeight}px;
151
+ min-height: ${getControlSizeTokens("sm").menuRowHeight}px;
152
+ padding: 0 ${getControlSizeTokens("sm").menuItemPaddingX}px;
153
+ border: none;
154
+ border-radius: 3px;
155
+ color: ${({ $danger, theme }) => $danger ? theme.palette.error.main : theme.palette.text.primary};
156
+ font-weight: normal;
157
+ justify-content: flex-start;
158
+ width: 100%;
159
+
160
+ .button-label {
161
+ display: inline-block;
162
+ min-width: 0;
163
+ width: 100%;
164
+ overflow: hidden;
165
+ text-align: left;
166
+ text-overflow: ellipsis;
167
+ white-space: nowrap;
168
+ }
169
+
170
+ [data-position="left"] {
171
+ color: currentColor;
172
+ opacity: 0.8;
173
+ }
174
+
175
+ &:hover {
176
+ background-color: ${({ theme }) => theme.palette.action.hover};
177
+ color: ${({ $danger, theme }) => $danger ? theme.palette.error.main : theme.palette.text.primary};
178
+ }
179
+
180
+ &:active {
181
+ translate: none;
182
+ }
183
+
184
+ &:focus {
185
+ text-decoration: none;
186
+ }
187
+ `;
188
+ const TableMenu = ({ groups, onAction, }) => (_jsx(TableMenuPanel, { children: groups.map((group, index) => (_jsx(TableMenuGroup, { "$withDivider": index > 0, children: group.actions.map((action) => (_jsx(TableMenuButton, { "$danger": action.danger, size: "sm", variant: "subtle", leftSection: action.leftSection, justify: "start", onClick: () => {
189
+ action.onClick();
190
+ onAction();
191
+ }, children: action.label }, action.label))) }, group.label))) }));
192
+ export const TableCornerMenu = ({ editor, target, opened, onOpen, onClose, onAction, onMouseEnter, onMouseLeave, onMouseDown, }) => {
193
+ const rect = getCornerRect(target);
194
+ const groups = getTableActionGroups(editor, target);
195
+ return (_jsx(CornerRoot, { "data-monolith-table-rail": true, "$top": rect.top, "$left": rect.left, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: _jsxs(Popover, { opened: opened, onChange: (nextOpened) => {
196
+ if (nextOpened) {
197
+ onOpen();
198
+ return;
199
+ }
200
+ onClose();
201
+ }, position: "bottom-start", width: 245, trapFocus: true, children: [_jsx(Popover.Target, { children: _jsx(Button, { type: "button", size: "xs", variant: "outlined", title: "Table options", "aria-label": "Table options", selected: opened, onMouseDown: onMouseDown, children: _jsx(TablePropertiesIcon, { size: 12 }) }) }), _jsx(TableMenuDropdown, { children: _jsx(TableMenu, { groups: groups, onAction: onAction }) })] }) }));
202
+ };
@@ -0,0 +1,2 @@
1
+ import type { TableCornerMenuProps } from "./TableTools.types";
2
+ export declare const TableCornerMenu: ({ editor, target, opened, onOpen, onClose, onAction, onMouseEnter, onMouseLeave, onMouseDown, }: TableCornerMenuProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TablePropertiesIcon } from "lucide-react";
3
+ import { Button } from "../../../Button";
4
+ import { Popover } from "../../../Popover";
5
+ import { getTableActionGroups } from "./TableTools.actions";
6
+ import { CornerRoot, MenuDropdown } from "./TableTools.styled";
7
+ import { getCornerRect } from "./TableTools.geometry";
8
+ import { TableToolMenu } from "./TableToolMenu";
9
+ export const TableCornerMenu = ({ editor, target, opened, onOpen, onClose, onAction, onMouseEnter, onMouseLeave, onMouseDown, }) => {
10
+ const rect = getCornerRect(target);
11
+ const groups = getTableActionGroups(editor, target);
12
+ return (_jsx(CornerRoot, { "data-monolith-table-rail": true, "$top": rect.top, "$left": rect.left, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: _jsxs(Popover, { opened: opened, onChange: (nextOpened) => {
13
+ if (nextOpened) {
14
+ onOpen();
15
+ return;
16
+ }
17
+ onClose();
18
+ }, position: "bottom-start", width: 245, trapFocus: true, children: [_jsx(Popover.Target, { children: _jsx(Button, { type: "button", size: "xs", variant: "outlined", title: "Table options", "aria-label": "Table options", selected: opened, onMouseDown: onMouseDown, children: _jsx(TablePropertiesIcon, { size: 12 }) }) }), _jsx(MenuDropdown, { children: _jsx(TableToolMenu, { groups: groups, onAction: onAction }) })] }) }));
19
+ };
@@ -0,0 +1,2 @@
1
+ import type { TableToolsContentProps } from "./TableTools.types";
2
+ export declare const TableToolsContent: ({ editor, showInsert, onInsert, onAction, }: TableToolsContentProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Button } from "../../../Button";
4
+ import Input from "../../../Input";
5
+ import { Switch } from "../../../Switch";
6
+ import { Field, InsertGrid, SwitchRow, TableToolsPanel, TableToolsSection, TableToolsSectionTitle, } from "./TableTools.styled";
7
+ import { DEFAULT_TABLE_OPTIONS, MAX_TABLE_SIZE, MIN_TABLE_SIZE, } from "./TableTools.constants";
8
+ import { runTableCommand } from "./TableTools.commands";
9
+ import { clampTableSize, hasTableExtension } from "./TableTools.selectors";
10
+ export const TableToolsContent = ({ editor, showInsert = false, onInsert, onAction, }) => {
11
+ const [insertOptions, setInsertOptions] = useState(DEFAULT_TABLE_OPTIONS);
12
+ const tableAvailable = hasTableExtension(editor) && (editor === null || editor === void 0 ? void 0 : editor.isEditable);
13
+ const updateInsertOption = (key, value) => {
14
+ setInsertOptions((current) => (Object.assign(Object.assign({}, current), { [key]: typeof value === "number" ? clampTableSize(value) : value })));
15
+ };
16
+ const insertTable = () => {
17
+ if (!tableAvailable)
18
+ return;
19
+ runTableCommand(editor, "insertTable", [insertOptions]);
20
+ onInsert === null || onInsert === void 0 ? void 0 : onInsert();
21
+ onAction === null || onAction === void 0 ? void 0 : onAction();
22
+ };
23
+ return (_jsx(TableToolsPanel, { children: showInsert && (_jsxs(TableToolsSection, { children: [_jsx(TableToolsSectionTitle, { description: "Create and insert a table at the current editor position.", children: "Insert Table" }), _jsxs(TableToolsSection, { style: { gap: "1rem" }, children: [_jsxs(InsertGrid, { children: [_jsxs(Field, { children: ["Rows", _jsx(Input, { type: "number", min: MIN_TABLE_SIZE, max: MAX_TABLE_SIZE, size: "xs", value: insertOptions.rows, onChange: (event) => updateInsertOption("rows", Number(event.target.value)) })] }), _jsxs(Field, { children: ["Columns", _jsx(Input, { type: "number", min: MIN_TABLE_SIZE, max: MAX_TABLE_SIZE, size: "xs", value: insertOptions.cols, onChange: (event) => updateInsertOption("cols", Number(event.target.value)) })] })] }), _jsx(SwitchRow, { children: _jsx(Switch, { size: "xs", label: "Include Header Row", description: "The first row will be formatted as a header.", value: insertOptions.withHeaderRow, onChange: (checked) => updateInsertOption("withHeaderRow", checked) }) }), _jsx(Button, { size: "xs", variant: "outlined", fullWidth: true, disabled: !tableAvailable, onClick: insertTable, children: "Insert Table" })] })] })) }));
24
+ };
@@ -0,0 +1,2 @@
1
+ import type { TableRailsProps } from "./TableTools.types";
2
+ export declare const TableRails: ({ editor }: TableRailsProps) => import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,180 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { MoreHorizontalIcon, MoreVerticalIcon } from "lucide-react";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+ import { Button } from "../../../Button";
5
+ import { Popover } from "../../../Popover";
6
+ import { TableCornerMenu } from "./TableCornerMenu";
7
+ import { getColumnActionGroups, getRowActionGroups, } from "./TableTools.actions";
8
+ import { RAIL_HIDE_DELAY } from "./TableTools.constants";
9
+ import { getRailRect, getRailTargetFromCell } from "./TableTools.geometry";
10
+ import { MenuDropdown, RailRoot } from "./TableTools.styled";
11
+ import { getCellsInTableTarget, getCellsInTarget, hasTableExtension, } from "./TableTools.selectors";
12
+ import { TableToolMenu } from "./TableToolMenu";
13
+ export const TableRails = ({ editor }) => {
14
+ const [hoverTarget, setHoverTarget] = useState(null);
15
+ const [lockedTarget, setLockedTarget] = useState(null);
16
+ const [openRail, setOpenRail] = useState(null);
17
+ const hideTimerRef = useRef(null);
18
+ const lastPointerRef = useRef(null);
19
+ const activeTarget = openRail && lockedTarget ? lockedTarget : hoverTarget;
20
+ const clearHideTimer = useCallback(() => {
21
+ if (hideTimerRef.current) {
22
+ clearTimeout(hideTimerRef.current);
23
+ hideTimerRef.current = null;
24
+ }
25
+ }, []);
26
+ const clearTarget = useCallback(() => {
27
+ var _a;
28
+ clearHideTimer();
29
+ if (!openRail) {
30
+ const pointer = lastPointerRef.current;
31
+ const element = pointer
32
+ ? document.elementFromPoint(pointer.x, pointer.y)
33
+ : null;
34
+ if ((_a = element === null || element === void 0 ? void 0 : element.closest) === null || _a === void 0 ? void 0 : _a.call(element, "[data-monolith-table-rail]")) {
35
+ return;
36
+ }
37
+ setHoverTarget(null);
38
+ }
39
+ }, [clearHideTimer, openRail]);
40
+ const scheduleClearTarget = useCallback(() => {
41
+ clearHideTimer();
42
+ hideTimerRef.current = setTimeout(clearTarget, RAIL_HIDE_DELAY);
43
+ }, [clearHideTimer, clearTarget]);
44
+ const refreshTargetFromPoint = useCallback(() => {
45
+ var _a, _b;
46
+ if (!editor || !lastPointerRef.current) {
47
+ setHoverTarget(null);
48
+ return;
49
+ }
50
+ const element = document.elementFromPoint(lastPointerRef.current.x, lastPointerRef.current.y);
51
+ if ((_a = element === null || element === void 0 ? void 0 : element.closest) === null || _a === void 0 ? void 0 : _a.call(element, "[data-monolith-table-rail]")) {
52
+ return;
53
+ }
54
+ const cell = (_b = element === null || element === void 0 ? void 0 : element.closest) === null || _b === void 0 ? void 0 : _b.call(element, "td, th");
55
+ if (cell instanceof HTMLTableCellElement &&
56
+ editor.view.dom.contains(cell)) {
57
+ const target = getRailTargetFromCell(editor, cell);
58
+ setHoverTarget(target);
59
+ return;
60
+ }
61
+ setHoverTarget(null);
62
+ }, [editor]);
63
+ const closeMenu = useCallback(() => {
64
+ setOpenRail(null);
65
+ setLockedTarget(null);
66
+ requestAnimationFrame(refreshTargetFromPoint);
67
+ }, [refreshTargetFromPoint]);
68
+ useEffect(() => {
69
+ if (!editor || !hasTableExtension(editor))
70
+ return;
71
+ const handleMouseMove = (event) => {
72
+ var _a, _b;
73
+ const target = event.target;
74
+ const cell = (_a = target === null || target === void 0 ? void 0 : target.closest) === null || _a === void 0 ? void 0 : _a.call(target, "td, th");
75
+ lastPointerRef.current = { x: event.clientX, y: event.clientY };
76
+ if ((_b = target === null || target === void 0 ? void 0 : target.closest) === null || _b === void 0 ? void 0 : _b.call(target, "[data-monolith-table-rail]")) {
77
+ clearHideTimer();
78
+ return;
79
+ }
80
+ if (openRail) {
81
+ clearHideTimer();
82
+ return;
83
+ }
84
+ if (!(cell instanceof HTMLTableCellElement) ||
85
+ !editor.view.dom.contains(cell)) {
86
+ if (!openRail) {
87
+ scheduleClearTarget();
88
+ }
89
+ return;
90
+ }
91
+ const railTarget = getRailTargetFromCell(editor, cell);
92
+ if (railTarget) {
93
+ clearHideTimer();
94
+ setHoverTarget(railTarget);
95
+ }
96
+ };
97
+ const handleMouseLeave = () => {
98
+ if (!openRail) {
99
+ scheduleClearTarget();
100
+ }
101
+ };
102
+ editor.view.dom.addEventListener("mousemove", handleMouseMove);
103
+ editor.view.dom.addEventListener("mouseleave", handleMouseLeave);
104
+ window.addEventListener("mousemove", handleMouseMove);
105
+ window.addEventListener("scroll", refreshTargetFromPoint, true);
106
+ window.addEventListener("resize", refreshTargetFromPoint);
107
+ return () => {
108
+ editor.view.dom.removeEventListener("mousemove", handleMouseMove);
109
+ editor.view.dom.removeEventListener("mouseleave", handleMouseLeave);
110
+ window.removeEventListener("mousemove", handleMouseMove);
111
+ window.removeEventListener("scroll", refreshTargetFromPoint, true);
112
+ window.removeEventListener("resize", refreshTargetFromPoint);
113
+ clearHideTimer();
114
+ };
115
+ }, [
116
+ clearHideTimer,
117
+ editor,
118
+ openRail,
119
+ refreshTargetFromPoint,
120
+ scheduleClearTarget,
121
+ ]);
122
+ useEffect(() => {
123
+ const highlighted = Array.from(document.querySelectorAll(".monolith-table-rail-target"));
124
+ highlighted.forEach((element) => element.classList.remove("monolith-table-rail-target"));
125
+ if (!activeTarget || !openRail)
126
+ return;
127
+ const cells = openRail === "table"
128
+ ? getCellsInTableTarget(activeTarget)
129
+ : getCellsInTarget(Object.assign(Object.assign({}, activeTarget), { kind: openRail }));
130
+ cells.forEach((cell) => {
131
+ const dom = editor === null || editor === void 0 ? void 0 : editor.view.nodeDOM(activeTarget.tableContext.start + cell.pos);
132
+ if (dom instanceof HTMLElement) {
133
+ dom.classList.add("monolith-table-rail-target");
134
+ }
135
+ });
136
+ return () => {
137
+ cells.forEach((cell) => {
138
+ const dom = editor === null || editor === void 0 ? void 0 : editor.view.nodeDOM(activeTarget.tableContext.start + cell.pos);
139
+ if (dom instanceof HTMLElement) {
140
+ dom.classList.remove("monolith-table-rail-target");
141
+ }
142
+ });
143
+ };
144
+ }, [activeTarget, editor, openRail]);
145
+ const keepEditorSelection = (event) => {
146
+ event.preventDefault();
147
+ };
148
+ if (!activeTarget || !(editor === null || editor === void 0 ? void 0 : editor.isEditable))
149
+ return null;
150
+ const keepTargetVisible = () => {
151
+ clearHideTimer();
152
+ };
153
+ const scheduleTargetClearIfClosed = () => {
154
+ if (!openRail) {
155
+ scheduleClearTarget();
156
+ }
157
+ };
158
+ const renderRail = (kind) => {
159
+ const railTarget = Object.assign(Object.assign({}, activeTarget), { kind });
160
+ const groups = kind === "column"
161
+ ? getColumnActionGroups(editor, railTarget)
162
+ : getRowActionGroups(editor, railTarget);
163
+ return (_jsx(RailRoot, { "data-monolith-table-rail": true, "$kind": kind, "$rect": getRailRect(activeTarget, kind), onMouseEnter: keepTargetVisible, onMouseLeave: scheduleTargetClearIfClosed, children: _jsxs(Popover, { opened: openRail === kind, onChange: (opened) => {
164
+ if (opened) {
165
+ clearHideTimer();
166
+ setLockedTarget(railTarget);
167
+ setOpenRail(kind);
168
+ return;
169
+ }
170
+ setOpenRail(null);
171
+ setLockedTarget(null);
172
+ refreshTargetFromPoint();
173
+ }, position: kind === "column" ? "bottom" : "right", width: 245, trapFocus: true, children: [_jsx(Popover.Target, { children: _jsx(Button, { type: "button", size: "xs", variant: "outlined", title: kind === "column" ? "Column options" : "Row options", "aria-label": kind === "column" ? "Column options" : "Row options", selected: openRail === kind, onMouseDown: keepEditorSelection, children: kind === "column" ? (_jsx(MoreHorizontalIcon, { size: 14 })) : (_jsx(MoreVerticalIcon, { size: 14 })) }) }), _jsx(MenuDropdown, { children: _jsx(TableToolMenu, { groups: groups, onAction: closeMenu }) })] }) }, kind));
174
+ };
175
+ return (_jsxs(_Fragment, { children: [_jsx(TableCornerMenu, { editor: editor, target: activeTarget, opened: openRail === "table", onOpen: () => {
176
+ clearHideTimer();
177
+ setLockedTarget(activeTarget);
178
+ setOpenRail("table");
179
+ }, onClose: closeMenu, onAction: closeMenu, onMouseEnter: keepTargetVisible, onMouseLeave: scheduleTargetClearIfClosed, onMouseDown: keepEditorSelection }), renderRail("column"), renderRail("row")] }));
180
+ };
@@ -0,0 +1,5 @@
1
+ import type { TableToolActionGroup } from "./TableTools.types";
2
+ export declare const TableToolMenu: ({ groups, onAction, }: {
3
+ groups: TableToolActionGroup[];
4
+ onAction: () => void;
5
+ }) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MenuButton, MenuGroup, MenuPanel } from "./TableTools.styled";
3
+ export const TableToolMenu = ({ groups, onAction, }) => (_jsx(MenuPanel, { children: groups.map((group, index) => (_jsx(MenuGroup, { "$withDivider": index > 0, children: group.actions.map((action) => (_jsx(MenuButton, { "$danger": action.danger, size: "sm", variant: "subtle", leftSection: action.leftSection, disabled: action.disabled, justify: "start", onClick: () => {
4
+ action.onClick();
5
+ onAction();
6
+ }, children: action.label }, action.label))) }, group.label))) }));
@@ -0,0 +1,5 @@
1
+ import type { Editor } from "@tiptap/react";
2
+ import type { RailTarget, TableRailTarget, TableToolActionGroup } from "./TableTools.types";
3
+ export declare const getColumnActionGroups: (editor: Editor | null, target: RailTarget) => TableToolActionGroup[];
4
+ export declare const getRowActionGroups: (editor: Editor | null, target: RailTarget) => TableToolActionGroup[];
5
+ export declare const getTableActionGroups: (editor: Editor | null, target: TableRailTarget) => TableToolActionGroup[];