@monolith-forensics/monolith-ui 1.9.1-dev.9 → 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 (91) 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 +3 -3
  9. package/dist/RichTextEditor/Components/BubbleMenu.js +190 -51
  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 -0
  69. package/dist/RichTextEditor/Extensions/getTiptapExtensions.js +157 -24
  70. package/dist/RichTextEditor/Plugins/ImageActionsPlugin.js +4 -7
  71. package/dist/RichTextEditor/RichTextEditor.d.ts +3 -1
  72. package/dist/RichTextEditor/RichTextEditor.js +352 -14
  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
@@ -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[];