@firecms/core 3.1.0 → 3.2.0-canary.4c3b8f2
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/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
- package/dist/components/ErrorBoundary.d.ts +3 -1
- package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
- package/dist/components/LanguageToggle.d.ts +1 -0
- package/dist/components/UnsavedChangesDialog.d.ts +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/core/DrawerNavigationGroup.d.ts +2 -2
- package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
- package/dist/editor/components/editor-bubble-item.d.ts +8 -0
- package/dist/editor/components/editor-bubble.d.ts +8 -0
- package/dist/editor/components/image-bubble.d.ts +5 -0
- package/dist/editor/components/index.d.ts +16 -0
- package/dist/editor/components/table-bubble.d.ts +5 -0
- package/dist/editor/editor.d.ts +30 -0
- package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
- package/dist/editor/extensions/Image/index.d.ts +6 -0
- package/dist/editor/extensions/Image.d.ts +6 -0
- package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
- package/dist/editor/extensions/clipboard.d.ts +7 -0
- package/dist/editor/extensions/custom-keymap.d.ts +1 -0
- package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
- package/dist/editor/hooks/useProseMirror.d.ts +13 -0
- package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/markdown.d.ts +5 -0
- package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
- package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
- package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
- package/dist/editor/nodeViews/index.d.ts +6 -0
- package/dist/editor/plugins/index.d.ts +2 -0
- package/dist/editor/plugins/inputrules.d.ts +6 -0
- package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
- package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
- package/dist/editor/schema.d.ts +2 -0
- package/dist/editor/selectors/ai-selector.d.ts +0 -0
- package/dist/editor/selectors/color-selector.d.ts +10 -0
- package/dist/editor/selectors/link-selector.d.ts +8 -0
- package/dist/editor/selectors/node-selector.d.ts +15 -0
- package/dist/editor/selectors/text-buttons.d.ts +1 -0
- package/dist/editor/types.d.ts +5 -0
- package/dist/editor/useProseMirror.d.ts +16 -0
- package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
- package/dist/editor/utils/remove_classes.d.ts +1 -0
- package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useBuildNavigationController.d.ts +0 -1
- package/dist/hooks/useCollapsedGroups.d.ts +3 -3
- package/dist/hooks/useTranslation.d.ts +17 -0
- package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.es.js +12898 -2265
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +12877 -2264
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/en.d.ts +10 -0
- package/dist/locales/es.d.ts +10 -0
- package/dist/locales/fr.d.ts +2 -0
- package/dist/locales/hi.d.ts +2 -0
- package/dist/locales/it.d.ts +2 -0
- package/dist/locales/pt.d.ts +7 -0
- package/dist/types/customization_controller.d.ts +2 -1
- package/dist/types/firecms.d.ts +2 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/navigation.d.ts +2 -2
- package/dist/types/plugins.d.ts +7 -0
- package/dist/types/storage.d.ts +1 -0
- package/dist/types/translations.d.ts +646 -0
- package/dist/util/useStorageUploadController.d.ts +10 -1
- package/package.json +45 -9
- package/src/app/Scaffold.tsx +7 -5
- package/src/components/AIIcon.tsx +3 -1
- package/src/components/ArrayContainer.tsx +6 -4
- package/src/components/ClearFilterSortButton.tsx +6 -3
- package/src/components/ConfirmationDialog.tsx +4 -2
- package/src/components/DeleteEntityDialog.tsx +10 -7
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
- package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
- package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +16 -43
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -18
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
- package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
- package/src/components/EntityCollectionView/ViewModeToggle.tsx +11 -8
- package/src/components/EntityView.tsx +3 -2
- package/src/components/ErrorBoundary.tsx +27 -15
- package/src/components/HomePage/DefaultHomePage.tsx +19 -13
- package/src/components/HomePage/HomePageDnD.tsx +3 -1
- package/src/components/HomePage/NavigationGroup.tsx +3 -1
- package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
- package/src/components/LanguageToggle.tsx +66 -0
- package/src/components/NotFoundPage.tsx +5 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
- package/src/components/ReferenceWidget.tsx +3 -2
- package/src/components/SearchIconsView.tsx +3 -1
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
- package/src/components/UnsavedChangesDialog.tsx +6 -4
- package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
- package/src/components/VirtualTable/VirtualTableHeader.tsx +12 -10
- package/src/components/common/default_entity_actions.tsx +4 -0
- package/src/components/common/useDataSourceTableController.tsx +12 -4
- package/src/components/index.tsx +1 -0
- package/src/core/DefaultAppBar.tsx +14 -10
- package/src/core/DefaultDrawer.tsx +8 -2
- package/src/core/DrawerNavigationGroup.tsx +5 -3
- package/src/core/EntityEditView.tsx +4 -3
- package/src/core/EntityEditViewFormActions.tsx +24 -17
- package/src/core/EntitySidePanel.tsx +6 -5
- package/src/core/FireCMS.tsx +33 -6
- package/src/editor/components/SlashCommandMenu.tsx +516 -0
- package/src/editor/components/editor-bubble-item.tsx +32 -0
- package/src/editor/components/editor-bubble.tsx +118 -0
- package/src/editor/components/image-bubble.tsx +156 -0
- package/src/editor/components/index.ts +14 -0
- package/src/editor/components/table-bubble.tsx +165 -0
- package/src/editor/editor.tsx +455 -0
- package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
- package/src/editor/extensions/Image/index.ts +133 -0
- package/src/editor/extensions/Image.ts +159 -0
- package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
- package/src/editor/extensions/clipboard.ts +72 -0
- package/src/editor/extensions/custom-keymap.ts +24 -0
- package/src/editor/extensions/drag-and-drop.tsx +480 -0
- package/src/editor/hooks/useProseMirror.ts +124 -0
- package/src/editor/hooks/useProseMirrorContext.ts +15 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/markdown.ts +172 -0
- package/src/editor/nodeViews/ImageComponent.tsx +20 -0
- package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
- package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
- package/src/editor/nodeViews/index.ts +35 -0
- package/src/editor/plugins/index.ts +58 -0
- package/src/editor/plugins/inputrules.ts +82 -0
- package/src/editor/plugins/placeholderPlugin.ts +55 -0
- package/src/editor/plugins/slashCommandPlugin.ts +61 -0
- package/src/editor/schema.ts +240 -0
- package/src/editor/selectors/ai-selector.tsx +111 -0
- package/src/editor/selectors/color-selector.tsx +200 -0
- package/src/editor/selectors/link-selector.tsx +118 -0
- package/src/editor/selectors/node-selector.tsx +157 -0
- package/src/editor/selectors/text-buttons.tsx +86 -0
- package/src/editor/types.ts +6 -0
- package/src/editor/useProseMirror.ts +126 -0
- package/src/editor/utils/prosemirror-utils.ts +108 -0
- package/src/editor/utils/remove_classes.ts +17 -0
- package/src/editor/utils/useDebouncedCallback.ts +25 -0
- package/src/form/EntityForm.tsx +16 -3
- package/src/form/EntityFormActions.tsx +19 -12
- package/src/form/PropertyFieldBinding.tsx +3 -2
- package/src/form/components/LocalChangesMenu.tsx +13 -13
- package/src/form/components/StorageItemPreview.tsx +3 -2
- package/src/form/components/StorageUploadProgress.tsx +18 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
- package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
- package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +33 -19
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -3
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useBuildNavigationController.tsx +45 -18
- package/src/hooks/useCollapsedGroups.ts +7 -6
- package/src/hooks/useTranslation.ts +31 -0
- package/src/i18n/FireCMSi18nProvider.tsx +160 -0
- package/src/index.ts +4 -0
- package/src/internal/useBuildSideEntityController.tsx +22 -20
- package/src/locales/de.ts +691 -0
- package/src/locales/en.ts +703 -0
- package/src/locales/es.ts +703 -0
- package/src/locales/fr.ts +691 -0
- package/src/locales/hi.ts +691 -0
- package/src/locales/it.ts +691 -0
- package/src/locales/pt.ts +700 -0
- package/src/preview/components/UrlComponentPreview.tsx +4 -2
- package/src/preview/components/UserPreview.tsx +3 -1
- package/src/types/customization_controller.tsx +2 -1
- package/src/types/firecms.tsx +2 -1
- package/src/types/index.ts +1 -0
- package/src/types/navigation.ts +2 -2
- package/src/types/plugins.tsx +8 -0
- package/src/types/properties.ts +1 -0
- package/src/types/storage.ts +2 -1
- package/src/types/translations.ts +725 -0
- package/src/util/useStorageUploadController.tsx +23 -29
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { forwardRef, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useProseMirrorContext } from "../hooks/useProseMirrorContext";
|
|
3
|
+
import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
|
|
4
|
+
import { NodeSelection } from "prosemirror-state";
|
|
5
|
+
import { TextField, defaultBorderMixin, Typography, cls } from "@firecms/ui";
|
|
6
|
+
|
|
7
|
+
export interface ImageBubbleProps {
|
|
8
|
+
options?: any;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const ImageBubble = forwardRef<HTMLDivElement, ImageBubbleProps>(
|
|
13
|
+
({ options, className }, ref) => {
|
|
14
|
+
const { view, state } = useProseMirrorContext();
|
|
15
|
+
const menuRef = useRef<HTMLDivElement>(null);
|
|
16
|
+
const [show, setShow] = useState(false);
|
|
17
|
+
const [alt, setAlt] = useState("");
|
|
18
|
+
const [title, setTitle] = useState("");
|
|
19
|
+
const [imagePos, setImagePos] = useState<number | null>(null);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!view) return;
|
|
23
|
+
const handleContextMenu = (e: MouseEvent) => {
|
|
24
|
+
const posInfo = view.posAtCoords({ left: e.clientX, top: e.clientY });
|
|
25
|
+
if (posInfo && posInfo.inside >= 0) {
|
|
26
|
+
const node = view.state.doc.nodeAt(posInfo.inside);
|
|
27
|
+
if (node && node.type.name === "image") {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
const tr = view.state.tr.setSelection(NodeSelection.create(view.state.doc, posInfo.inside));
|
|
30
|
+
view.dispatch(tr);
|
|
31
|
+
setShow(true);
|
|
32
|
+
setAlt(node.attrs.alt || "");
|
|
33
|
+
setTitle(node.attrs.title || "");
|
|
34
|
+
setImagePos(posInfo.inside);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
view.dom.addEventListener("contextmenu", handleContextMenu);
|
|
39
|
+
|
|
40
|
+
const handleMousedown = (e: MouseEvent) => {
|
|
41
|
+
if (menuRef.current && !menuRef.current.contains(e.target as Node) && e.button !== 2) {
|
|
42
|
+
setShow(false);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
document.addEventListener("mousedown", handleMousedown);
|
|
46
|
+
|
|
47
|
+
return () => {
|
|
48
|
+
view.dom.removeEventListener("contextmenu", handleContextMenu);
|
|
49
|
+
document.removeEventListener("mousedown", handleMousedown);
|
|
50
|
+
};
|
|
51
|
+
}, [view]);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!show || !view || !state || !menuRef.current) return;
|
|
55
|
+
|
|
56
|
+
const { from, to } = state.selection;
|
|
57
|
+
let start = view.coordsAtPos(from);
|
|
58
|
+
let end = view.coordsAtPos(to);
|
|
59
|
+
|
|
60
|
+
const virtualEl = {
|
|
61
|
+
getBoundingClientRect() {
|
|
62
|
+
const top = Math.min(start.top, end.top);
|
|
63
|
+
const bottom = Math.max(start.bottom, end.bottom);
|
|
64
|
+
const left = Math.min(start.left, end.left);
|
|
65
|
+
const right = Math.max(start.right, end.right);
|
|
66
|
+
return {
|
|
67
|
+
width: right - left,
|
|
68
|
+
height: bottom - top,
|
|
69
|
+
x: left,
|
|
70
|
+
y: top,
|
|
71
|
+
top,
|
|
72
|
+
left,
|
|
73
|
+
right,
|
|
74
|
+
bottom,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const cleanup = autoUpdate(virtualEl as any, menuRef.current, () => {
|
|
80
|
+
if (!menuRef.current) return;
|
|
81
|
+
try {
|
|
82
|
+
start = view.coordsAtPos(state.selection.from);
|
|
83
|
+
end = view.coordsAtPos(state.selection.to);
|
|
84
|
+
} catch (e) {}
|
|
85
|
+
|
|
86
|
+
computePosition(virtualEl as any, menuRef.current, {
|
|
87
|
+
placement: options?.placement || "bottom",
|
|
88
|
+
middleware: [offset(options?.offset || 8), flip(), shift()],
|
|
89
|
+
strategy: "fixed"
|
|
90
|
+
}).then(({ x, y }) => {
|
|
91
|
+
if (menuRef.current) {
|
|
92
|
+
Object.assign(menuRef.current.style, {
|
|
93
|
+
left: `${x}px`,
|
|
94
|
+
top: `${y}px`,
|
|
95
|
+
visibility: "visible",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
return () => cleanup();
|
|
101
|
+
}, [show, view, state, options]);
|
|
102
|
+
|
|
103
|
+
const handleSave = (newAlt: string, newTitle: string) => {
|
|
104
|
+
if (imagePos !== null && view) {
|
|
105
|
+
const node = view.state.doc.nodeAt(imagePos);
|
|
106
|
+
if (node && node.type.name === "image") {
|
|
107
|
+
const tr = view.state.tr.setNodeMarkup(imagePos, undefined, {
|
|
108
|
+
...node.attrs,
|
|
109
|
+
alt: newAlt,
|
|
110
|
+
title: newTitle
|
|
111
|
+
});
|
|
112
|
+
// Preserve the node selection so the bubble stays open!
|
|
113
|
+
tr.setSelection(NodeSelection.create(tr.doc, imagePos));
|
|
114
|
+
view.dispatch(tr);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
if (!show) return null;
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<div
|
|
123
|
+
ref={menuRef}
|
|
124
|
+
style={{ visibility: "hidden", position: "fixed", zIndex: 50 }}
|
|
125
|
+
className={cls("flex flex-col gap-1.5 p-2 w-56 max-w-[90vw] rounded-lg border bg-white dark:bg-surface-900 shadow-lg", defaultBorderMixin, className)}
|
|
126
|
+
onMouseDown={(e) => {
|
|
127
|
+
// Prevent mousedown from stealing focus from inputs
|
|
128
|
+
// but we don't want to prevent typing
|
|
129
|
+
// Only prevent if clicking on the container background
|
|
130
|
+
if (e.target === e.currentTarget) {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
}
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<TextField
|
|
136
|
+
size={"small"}
|
|
137
|
+
placeholder="Alt text"
|
|
138
|
+
value={alt}
|
|
139
|
+
onChange={(e: any) => {
|
|
140
|
+
setAlt(e.target.value);
|
|
141
|
+
handleSave(e.target.value, title);
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
<TextField
|
|
145
|
+
size={"small"}
|
|
146
|
+
placeholder="Title"
|
|
147
|
+
value={title}
|
|
148
|
+
onChange={(e: any) => {
|
|
149
|
+
setTitle(e.target.value);
|
|
150
|
+
handleSave(alt, e.target.value);
|
|
151
|
+
}}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { EditorBubble } from "./editor-bubble";
|
|
2
|
+
export { EditorBubbleItem } from "./editor-bubble-item";
|
|
3
|
+
export { SlashCommandMenu } from "./SlashCommandMenu";
|
|
4
|
+
export { ImageBubble } from "./image-bubble";
|
|
5
|
+
export { TableBubble } from "./table-bubble";
|
|
6
|
+
|
|
7
|
+
export type JSONContent = {
|
|
8
|
+
type?: string;
|
|
9
|
+
attrs?: Record<string, any>;
|
|
10
|
+
content?: JSONContent[];
|
|
11
|
+
marks?: { type: string; attrs?: Record<string, any> }[];
|
|
12
|
+
text?: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { forwardRef, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useProseMirrorContext } from "../hooks/useProseMirrorContext";
|
|
3
|
+
import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
|
|
4
|
+
import { IconButton, Tooltip, defaultBorderMixin, cls } from "@firecms/ui";
|
|
5
|
+
import {
|
|
6
|
+
addColumnBefore,
|
|
7
|
+
addColumnAfter,
|
|
8
|
+
deleteColumn,
|
|
9
|
+
addRowBefore,
|
|
10
|
+
addRowAfter,
|
|
11
|
+
deleteRow,
|
|
12
|
+
deleteTable,
|
|
13
|
+
} from "prosemirror-tables";
|
|
14
|
+
import { EditorState } from "prosemirror-state";
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export interface TableBubbleProps {
|
|
18
|
+
options?: any;
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const isSelectionInTable = (state: EditorState) => {
|
|
23
|
+
const { $from } = state.selection;
|
|
24
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
25
|
+
if ($from.node(d).type.name === "table") {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// We create separate icon components if they don't exist in @firecms/ui
|
|
33
|
+
// using basic SVGs for now, or we can use existing ones if we know them.
|
|
34
|
+
// Wait, I will use generic SVGs for these since I don't know if @firecms/ui has them.
|
|
35
|
+
|
|
36
|
+
export const TableBubble = forwardRef<HTMLDivElement, TableBubbleProps>(
|
|
37
|
+
({ options, className }, ref) => {
|
|
38
|
+
const { view, state } = useProseMirrorContext();
|
|
39
|
+
const menuRef = useRef<HTMLDivElement>(null);
|
|
40
|
+
const [show, setShow] = useState(false);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!state) return;
|
|
44
|
+
setShow(isSelectionInTable(state));
|
|
45
|
+
}, [state?.selection]);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!show || !view || !state || !menuRef.current) return;
|
|
49
|
+
|
|
50
|
+
const { from, to } = state.selection;
|
|
51
|
+
|
|
52
|
+
// Safety measure: if view.docView is destroyed, coordsAtPos might crash
|
|
53
|
+
if (view.isDestroyed) return;
|
|
54
|
+
|
|
55
|
+
let start = view.coordsAtPos(from);
|
|
56
|
+
let end = view.coordsAtPos(to);
|
|
57
|
+
|
|
58
|
+
const virtualEl = {
|
|
59
|
+
getBoundingClientRect() {
|
|
60
|
+
const top = Math.min(start.top, end.top);
|
|
61
|
+
const bottom = Math.max(start.bottom, end.bottom);
|
|
62
|
+
const left = Math.min(start.left, end.left);
|
|
63
|
+
const right = Math.max(start.right, end.right);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
width: right - left,
|
|
67
|
+
height: bottom - top,
|
|
68
|
+
x: left,
|
|
69
|
+
y: top,
|
|
70
|
+
top,
|
|
71
|
+
bottom,
|
|
72
|
+
left,
|
|
73
|
+
right,
|
|
74
|
+
} as DOMRect;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const cleanup = autoUpdate(virtualEl as any, menuRef.current, () => {
|
|
79
|
+
if (!menuRef.current || view.isDestroyed) return;
|
|
80
|
+
try {
|
|
81
|
+
start = view.coordsAtPos(state.selection.from);
|
|
82
|
+
end = view.coordsAtPos(state.selection.to);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
// Ignore errors during fast remounts/updates
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
computePosition(virtualEl as any, menuRef.current, {
|
|
88
|
+
placement: options?.placement || "top",
|
|
89
|
+
middleware: [offset(options?.offset || 8), flip(), shift()],
|
|
90
|
+
strategy: "fixed"
|
|
91
|
+
}).then(({ x, y }) => {
|
|
92
|
+
if (menuRef.current) {
|
|
93
|
+
Object.assign(menuRef.current.style, {
|
|
94
|
+
left: `${x}px`,
|
|
95
|
+
top: `${y}px`,
|
|
96
|
+
visibility: "visible",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
return () => cleanup();
|
|
102
|
+
}, [show, view, state, options]);
|
|
103
|
+
|
|
104
|
+
if (!show || !view || !state) return null;
|
|
105
|
+
|
|
106
|
+
const executeCommand = (cmd: any) => {
|
|
107
|
+
cmd(state, view.dispatch);
|
|
108
|
+
view.focus();
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div
|
|
113
|
+
ref={menuRef}
|
|
114
|
+
style={{ visibility: "hidden", position: "fixed", zIndex: 50 }}
|
|
115
|
+
className={cls("flex flex-row gap-1 p-1 rounded-lg border bg-white dark:bg-surface-900 shadow-lg", defaultBorderMixin, className)}
|
|
116
|
+
onMouseDown={(e) => {
|
|
117
|
+
// Prevent mousedown from stealing focus from the editor
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
<div className="flex gap-1 border-r pr-1 mr-1 dark:border-gray-700">
|
|
122
|
+
<Tooltip title="Add row before">
|
|
123
|
+
<IconButton size="small" onClick={() => executeCommand(addRowBefore)}>
|
|
124
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line><line x1="3" y1="9" x2="21" y2="9"></line></svg>
|
|
125
|
+
</IconButton>
|
|
126
|
+
</Tooltip>
|
|
127
|
+
<Tooltip title="Add row after">
|
|
128
|
+
<IconButton size="small" onClick={() => executeCommand(addRowAfter)}>
|
|
129
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="12" y1="12" x2="12" y2="20"></line><line x1="8" y1="16" x2="16" y2="16"></line><line x1="3" y1="15" x2="21" y2="15"></line></svg>
|
|
130
|
+
</IconButton>
|
|
131
|
+
</Tooltip>
|
|
132
|
+
<Tooltip title="Delete row">
|
|
133
|
+
<IconButton size="small" onClick={() => executeCommand(deleteRow)}>
|
|
134
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="12" x2="21" y2="12"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg>
|
|
135
|
+
</IconButton>
|
|
136
|
+
</Tooltip>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div className="flex gap-1 border-r pr-1 mr-1 dark:border-gray-700">
|
|
140
|
+
<Tooltip title="Add column before">
|
|
141
|
+
<IconButton size="small" onClick={() => executeCommand(addColumnBefore)}>
|
|
142
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="8" y1="12" x2="16" y2="12"></line><line x1="12" y1="8" x2="12" y2="16"></line><line x1="9" y1="3" x2="9" y2="21"></line></svg>
|
|
143
|
+
</IconButton>
|
|
144
|
+
</Tooltip>
|
|
145
|
+
<Tooltip title="Add column after">
|
|
146
|
+
<IconButton size="small" onClick={() => executeCommand(addColumnAfter)}>
|
|
147
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="12" y1="12" x2="20" y2="12"></line><line x1="16" y1="8" x2="16" y2="16"></line><line x1="15" y1="3" x2="15" y2="21"></line></svg>
|
|
148
|
+
</IconButton>
|
|
149
|
+
</Tooltip>
|
|
150
|
+
<Tooltip title="Delete column">
|
|
151
|
+
<IconButton size="small" onClick={() => executeCommand(deleteColumn)}>
|
|
152
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="12" y1="3" x2="12" y2="21"></line><line x1="12" y1="8" x2="12" y2="16"></line></svg>
|
|
153
|
+
</IconButton>
|
|
154
|
+
</Tooltip>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<Tooltip title="Delete table">
|
|
158
|
+
<IconButton size="small" onClick={() => executeCommand(deleteTable)}>
|
|
159
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
|
|
160
|
+
</IconButton>
|
|
161
|
+
</Tooltip>
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
);
|