@monolith-forensics/monolith-ui 1.9.1-dev.0 → 1.9.1-dev.10
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/DropDownMenu/components/MenuItemList.js +32 -12
- package/dist/DropDownMenu/components/StyledInnerItemContainer.js +1 -0
- package/dist/RichTextEditor/Components/BubbleMenu.d.ts +8 -8
- package/dist/RichTextEditor/Components/BubbleMenu.js +198 -93
- package/dist/RichTextEditor/Components/CodeBlockCopyButton.d.ts +9 -0
- package/dist/RichTextEditor/Components/CodeBlockCopyButton.js +45 -0
- package/dist/RichTextEditor/Components/CodeBlockFormatButton.d.ts +10 -0
- package/dist/RichTextEditor/Components/CodeBlockFormatButton.js +60 -0
- package/dist/RichTextEditor/Components/CodeBlockLanguageSelect.d.ts +6 -0
- package/dist/RichTextEditor/Components/CodeBlockLanguageSelect.js +21 -0
- package/dist/RichTextEditor/Components/CodeBlockNodeView.d.ts +3 -0
- package/dist/RichTextEditor/Components/CodeBlockNodeView.js +27 -0
- package/dist/RichTextEditor/Components/CodeBlockWrapButton.d.ts +10 -0
- package/dist/RichTextEditor/Components/CodeBlockWrapButton.js +17 -0
- package/dist/RichTextEditor/Components/LinkEditor.d.ts +8 -0
- package/dist/RichTextEditor/Components/LinkEditor.js +94 -0
- package/dist/RichTextEditor/Enums/Controls.d.ts +5 -1
- package/dist/RichTextEditor/Enums/Controls.js +4 -0
- package/dist/RichTextEditor/Enums/Extensions.d.ts +4 -0
- package/dist/RichTextEditor/Enums/Extensions.js +4 -0
- package/dist/RichTextEditor/Enums/HighlightColors.d.ts +9 -0
- package/dist/RichTextEditor/Enums/HighlightColors.js +10 -0
- package/dist/RichTextEditor/Enums/SlashCommands.d.ts +1 -0
- package/dist/RichTextEditor/Enums/SlashCommands.js +1 -0
- package/dist/RichTextEditor/Extensions/getSlashCommand.js +16 -1
- package/dist/RichTextEditor/Extensions/getTiptapExtensions.d.ts +10 -2
- package/dist/RichTextEditor/Extensions/getTiptapExtensions.js +158 -31
- package/dist/RichTextEditor/Plugins/ImageActionsPlugin.js +3 -73
- package/dist/RichTextEditor/Plugins/UploadImagesPlugin.js +1 -0
- package/dist/RichTextEditor/RichTextEditor.d.ts +5 -2
- package/dist/RichTextEditor/RichTextEditor.js +186 -13
- package/dist/RichTextEditor/Toolbar/Control.d.ts +6 -2
- package/dist/RichTextEditor/Toolbar/Control.js +13 -6
- package/dist/RichTextEditor/Toolbar/Controls.d.ts +2 -0
- package/dist/RichTextEditor/Toolbar/Controls.js +14 -0
- package/dist/RichTextEditor/Toolbar/ControlsGroup.js +1 -0
- package/dist/RichTextEditor/Toolbar/Toolbar.js +62 -9
- package/dist/RichTextEditor/Utils/codeBlockUtils.d.ts +20 -0
- package/dist/RichTextEditor/Utils/codeBlockUtils.js +137 -0
- package/dist/RichTextEditor/Utils/codeUtils.d.ts +3 -0
- package/dist/RichTextEditor/Utils/codeUtils.js +12 -0
- package/dist/RichTextEditor/Utils/linkUtils.d.ts +19 -0
- package/dist/RichTextEditor/Utils/linkUtils.js +57 -0
- package/package.json +8 -1
- package/dist/RichTextEditor/Extensions/BubbleMenuExtension.d.ts +0 -7
- package/dist/RichTextEditor/Extensions/BubbleMenuExtension.js +0 -157
|
@@ -56,11 +56,18 @@ const buildGroupedDisplayList = (items) => {
|
|
|
56
56
|
const ListViewPort = styled.div.attrs({ className: "ListViewPort" }) `
|
|
57
57
|
display: flex;
|
|
58
58
|
flex-direction: column;
|
|
59
|
+
flex: 1 1 auto;
|
|
60
|
+
min-height: 0;
|
|
59
61
|
width: 100%;
|
|
60
62
|
`;
|
|
63
|
+
const MenuItemListRoot = styled.div `
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
height: 100%;
|
|
67
|
+
min-height: 0;
|
|
68
|
+
`;
|
|
61
69
|
const ListScroller = styled.div `
|
|
62
70
|
scrollbar-color: ${({ theme }) => `${theme.scrollbar.thumb} ${theme.scrollbar.track}`};
|
|
63
|
-
scrollbar-gutter: stable;
|
|
64
71
|
scrollbar-width: thin;
|
|
65
72
|
|
|
66
73
|
&::-webkit-scrollbar {
|
|
@@ -234,16 +241,6 @@ export const MenuItemList = ({ menuItems, searchable, onSearch, manualSearch, dy
|
|
|
234
241
|
list.addEventListener("scroll", handleOnScroll);
|
|
235
242
|
return () => list.removeEventListener("scroll", handleOnScroll);
|
|
236
243
|
}, [dynamicOptionHeight, handleOnScroll, isLoading, renderOption]);
|
|
237
|
-
useLayoutEffect(() => {
|
|
238
|
-
var _a;
|
|
239
|
-
if (targetElm.current) {
|
|
240
|
-
const viewPortDimensions = (_a = targetElm.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
241
|
-
setViewPortDimensions({
|
|
242
|
-
width: viewPortDimensions.width,
|
|
243
|
-
height: viewPortDimensions.height,
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
}, [targetElm.current, isLoading]);
|
|
247
244
|
const overscanCount = 10;
|
|
248
245
|
const sizeTokens = getControlSizeTokens(size || "sm");
|
|
249
246
|
const itemHeight = sizeTokens.menuRowHeight;
|
|
@@ -267,6 +264,29 @@ export const MenuItemList = ({ menuItems, searchable, onSearch, manualSearch, dy
|
|
|
267
264
|
? estimatedHeight
|
|
268
265
|
: 200;
|
|
269
266
|
const width = "100%";
|
|
267
|
+
useLayoutEffect(() => {
|
|
268
|
+
const target = targetElm.current;
|
|
269
|
+
if (!target)
|
|
270
|
+
return;
|
|
271
|
+
const updateDimensions = () => {
|
|
272
|
+
const nextDimensions = target.getBoundingClientRect();
|
|
273
|
+
setViewPortDimensions((currentDimensions) => {
|
|
274
|
+
const width = Math.ceil(nextDimensions.width);
|
|
275
|
+
const height = Math.floor(nextDimensions.height);
|
|
276
|
+
if (currentDimensions.width === width &&
|
|
277
|
+
currentDimensions.height === height) {
|
|
278
|
+
return currentDimensions;
|
|
279
|
+
}
|
|
280
|
+
return { width, height };
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
updateDimensions();
|
|
284
|
+
if (typeof ResizeObserver === "undefined")
|
|
285
|
+
return;
|
|
286
|
+
const observer = new ResizeObserver(updateDimensions);
|
|
287
|
+
observer.observe(target);
|
|
288
|
+
return () => observer.disconnect();
|
|
289
|
+
}, [displayItemsKey, isLoading]);
|
|
270
290
|
const setItemSize = useCallback((index, measuredSize) => {
|
|
271
291
|
var _a;
|
|
272
292
|
if (itemSizeMap.current[index] === measuredSize)
|
|
@@ -335,7 +355,7 @@ export const MenuItemList = ({ menuItems, searchable, onSearch, manualSearch, dy
|
|
|
335
355
|
(item === null || item === void 0 ? void 0 : item.label) ||
|
|
336
356
|
(typeof item === "string" || typeof item === "number" ? item : null) }, index));
|
|
337
357
|
};
|
|
338
|
-
return (_jsxs(
|
|
358
|
+
return (_jsxs(MenuItemListRoot, { children: [searchable && (_jsx(SearchInput, { variant: "outlined", size: size, placeholder: "Search", defaultValue: searchValue, onChange: (e) => {
|
|
339
359
|
if (!manualSearch) {
|
|
340
360
|
handleSearch(e);
|
|
341
361
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { Extensions } from "../Enums";
|
|
2
2
|
import { DropDownItem, DropDownMenuProps } from "../../DropDownMenu";
|
|
3
|
-
import { ReactElement } from "react";
|
|
3
|
+
import { ReactElement, ReactNode } from "react";
|
|
4
4
|
import { ButtonProps } from "../../Button";
|
|
5
5
|
import { Editor } from "@tiptap/react";
|
|
6
|
-
export interface
|
|
6
|
+
export interface BubbleMenuContentProps {
|
|
7
7
|
className?: string;
|
|
8
8
|
editor: Editor;
|
|
9
|
-
rect: DOMRect;
|
|
10
|
-
open: boolean;
|
|
11
|
-
onOpen?: (element: HTMLElement) => void;
|
|
12
9
|
customMenuItems?: BubbleItem[];
|
|
13
10
|
}
|
|
14
11
|
interface BubbleMenuDropDownItem extends DropDownItem {
|
|
@@ -20,7 +17,7 @@ export type BubbleItem = {
|
|
|
20
17
|
name: Extensions | string;
|
|
21
18
|
icon?: React.FC<any>;
|
|
22
19
|
type: "menu";
|
|
23
|
-
label?:
|
|
20
|
+
label?: ReactNode;
|
|
24
21
|
items: BubbleMenuDropDownItem[];
|
|
25
22
|
arrow?: boolean;
|
|
26
23
|
isActive?: (editor: Editor) => boolean;
|
|
@@ -32,12 +29,15 @@ export type BubbleItem = {
|
|
|
32
29
|
name: Extensions | string;
|
|
33
30
|
icon?: React.FC<any>;
|
|
34
31
|
type: "button";
|
|
35
|
-
label?:
|
|
32
|
+
label?: ReactNode;
|
|
36
33
|
arrow?: boolean;
|
|
37
34
|
isActive?: (editor: Editor) => boolean;
|
|
38
35
|
buttonRender?: (props: any) => ReactElement;
|
|
39
36
|
buttonProps?: Partial<ButtonProps>;
|
|
40
37
|
onClick?: (editor: Editor) => void;
|
|
41
38
|
};
|
|
42
|
-
|
|
39
|
+
export type BubbleMenuOptions = {
|
|
40
|
+
customMenuItems?: BubbleItem[];
|
|
41
|
+
};
|
|
42
|
+
declare const BubbleMenu: React.FC<BubbleMenuContentProps>;
|
|
43
43
|
export default BubbleMenu;
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import styled, { useTheme } from "styled-components";
|
|
3
3
|
import { Extensions } from "../Enums";
|
|
4
|
-
import { BoldIcon, ItalicIcon, UnderlineIcon, CaseSensitiveIcon, ListIcon, ListOrderedIcon, StrikethroughIcon, Heading1Icon, Heading2Icon, Heading3Icon, Heading4Icon, RemoveFormattingIcon, SquircleIcon, } from "lucide-react";
|
|
4
|
+
import { BoldIcon, CodeIcon, ItalicIcon, UnderlineIcon, CaseSensitiveIcon, ListIcon, ListOrderedIcon, SquareCodeIcon, StrikethroughIcon, Heading1Icon, Heading2Icon, Heading3Icon, Heading4Icon, HighlighterIcon, LinkIcon, PaletteIcon, RemoveFormattingIcon, SquircleIcon, } from "lucide-react";
|
|
5
5
|
import { DropDownMenu, } from "../../DropDownMenu";
|
|
6
|
-
import {
|
|
7
|
-
import { useEffect, useRef } from "react";
|
|
6
|
+
import { useEffect, useState } from "react";
|
|
8
7
|
import { Button } from "../../Button";
|
|
9
8
|
import TextColors from "../Enums/TextColors";
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import HighlightColors from "../Enums/HighlightColors";
|
|
10
|
+
import LinkEditor from "./LinkEditor";
|
|
11
|
+
import { hasInlineCode, toggleInlineCode } from "../Utils/codeUtils";
|
|
12
|
+
import CodeBlockLanguageSelect from "./CodeBlockLanguageSelect";
|
|
13
|
+
import CodeBlockCopyButton from "./CodeBlockCopyButton";
|
|
14
|
+
import CodeBlockWrapButton from "./CodeBlockWrapButton";
|
|
15
|
+
import CodeBlockFormatButton from "./CodeBlockFormatButton";
|
|
16
|
+
import { hasSyntaxHighlightedCodeBlock, toggleCodeBlock, } from "../Utils/codeBlockUtils";
|
|
17
|
+
const getMenuItems = (editor, customMenuItems, theme, openLinkEditor) => {
|
|
18
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
12
19
|
const node = (_c = (_b = (_a = editor === null || editor === void 0 ? void 0 : editor.state) === null || _a === void 0 ? void 0 : _a.selection) === null || _b === void 0 ? void 0 : _b.$from) === null || _c === void 0 ? void 0 : _c.parent;
|
|
13
20
|
const pos = (_e = (_d = editor === null || editor === void 0 ? void 0 : editor.state) === null || _d === void 0 ? void 0 : _d.selection) === null || _e === void 0 ? void 0 : _e.$from;
|
|
14
21
|
let withinUnorderedList = false;
|
|
@@ -24,6 +31,11 @@ const getMenuItems = (editor, customMenuItems, theme) => {
|
|
|
24
31
|
}
|
|
25
32
|
});
|
|
26
33
|
const attrs = node === null || node === void 0 ? void 0 : node.attrs;
|
|
34
|
+
const supportsSyntaxCodeBlock = hasSyntaxHighlightedCodeBlock(editor);
|
|
35
|
+
const supportsInlineCode = hasInlineCode(editor);
|
|
36
|
+
const supportsColor = Boolean(editor.extensionManager.extensions.find((extension) => extension.name === "color"));
|
|
37
|
+
const supportsHighlight = Boolean(editor.extensionManager.extensions.find((extension) => extension.name === "highlight"));
|
|
38
|
+
const supportsLink = Boolean(editor.extensionManager.extensions.find((extension) => extension.name === "link"));
|
|
27
39
|
let nodeTypeLabel = "Select Type";
|
|
28
40
|
let nodeTypeIcon = null;
|
|
29
41
|
if (withinOrderedList) {
|
|
@@ -38,7 +50,11 @@ const getMenuItems = (editor, customMenuItems, theme) => {
|
|
|
38
50
|
nodeTypeLabel = "Text";
|
|
39
51
|
nodeTypeIcon = _jsx(CaseSensitiveIcon, { size: 16 });
|
|
40
52
|
}
|
|
41
|
-
else if (((_g = node === null || node === void 0 ? void 0 : node.type) === null || _g === void 0 ? void 0 : _g.name) === "
|
|
53
|
+
else if (((_g = node === null || node === void 0 ? void 0 : node.type) === null || _g === void 0 ? void 0 : _g.name) === "codeBlock") {
|
|
54
|
+
nodeTypeLabel = "Code Block";
|
|
55
|
+
nodeTypeIcon = _jsx(SquareCodeIcon, { size: 16 });
|
|
56
|
+
}
|
|
57
|
+
else if (((_h = node === null || node === void 0 ? void 0 : node.type) === null || _h === void 0 ? void 0 : _h.name) === "heading") {
|
|
42
58
|
const level = attrs === null || attrs === void 0 ? void 0 : attrs.level;
|
|
43
59
|
nodeTypeLabel = `Heading ${level}`;
|
|
44
60
|
nodeTypeIcon =
|
|
@@ -50,7 +66,6 @@ const getMenuItems = (editor, customMenuItems, theme) => {
|
|
|
50
66
|
name: "node_type",
|
|
51
67
|
label: nodeTypeLabel,
|
|
52
68
|
type: "menu",
|
|
53
|
-
arrow: true,
|
|
54
69
|
buttonProps: {
|
|
55
70
|
leftSection: nodeTypeIcon,
|
|
56
71
|
style: { fontSize: 11, padding: "4px" },
|
|
@@ -62,13 +77,16 @@ const getMenuItems = (editor, customMenuItems, theme) => {
|
|
|
62
77
|
data: {
|
|
63
78
|
Icon: CaseSensitiveIcon,
|
|
64
79
|
command: (editor) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
if (editor.isActive("bulletList") || editor.isActive("orderedList")) {
|
|
81
|
+
editor
|
|
82
|
+
.chain()
|
|
83
|
+
.focus()
|
|
84
|
+
.liftListItem("listItem")
|
|
85
|
+
.setParagraph()
|
|
86
|
+
.run();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
editor.chain().focus().setParagraph().run();
|
|
72
90
|
},
|
|
73
91
|
},
|
|
74
92
|
},
|
|
@@ -132,50 +150,120 @@ const getMenuItems = (editor, customMenuItems, theme) => {
|
|
|
132
150
|
},
|
|
133
151
|
},
|
|
134
152
|
},
|
|
153
|
+
...(supportsSyntaxCodeBlock
|
|
154
|
+
? [
|
|
155
|
+
{
|
|
156
|
+
label: "Code Block",
|
|
157
|
+
value: "code_block",
|
|
158
|
+
data: {
|
|
159
|
+
Icon: SquareCodeIcon,
|
|
160
|
+
command: (editor) => {
|
|
161
|
+
toggleCodeBlock(editor);
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
]
|
|
166
|
+
: []),
|
|
135
167
|
],
|
|
136
168
|
dropDownProps: {
|
|
137
169
|
renderOption: (item) => (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [_jsx(item.data.Icon, { size: 16 }), item.label] })),
|
|
138
170
|
},
|
|
139
171
|
},
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
label: "Color",
|
|
143
|
-
type: "menu",
|
|
144
|
-
arrow: true,
|
|
145
|
-
buttonProps: {
|
|
146
|
-
// leftSection: nodeTypeIcon,
|
|
147
|
-
style: { fontSize: 11, padding: "4px" },
|
|
148
|
-
},
|
|
149
|
-
items: [
|
|
172
|
+
...(supportsColor
|
|
173
|
+
? [
|
|
150
174
|
{
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
175
|
+
name: "color",
|
|
176
|
+
label: _jsx(PaletteIcon, { size: 14 }),
|
|
177
|
+
type: "menu",
|
|
178
|
+
buttonProps: {
|
|
179
|
+
style: { fontSize: 11, padding: "4px" },
|
|
180
|
+
},
|
|
181
|
+
items: [
|
|
182
|
+
{
|
|
183
|
+
label: "Default",
|
|
184
|
+
value: "default",
|
|
185
|
+
onClick: () => {
|
|
186
|
+
editor === null || editor === void 0 ? void 0 : editor.chain().focus().unsetColor().run();
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
...Object.keys(TextColors).map((color) => {
|
|
190
|
+
const colorKey = color;
|
|
191
|
+
return {
|
|
192
|
+
label: color,
|
|
193
|
+
value: TextColors[colorKey],
|
|
194
|
+
onClick: () => {
|
|
195
|
+
editor === null || editor === void 0 ? void 0 : editor.chain().focus().setColor(TextColors[colorKey]).run();
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}),
|
|
199
|
+
],
|
|
200
|
+
dropDownProps: {
|
|
201
|
+
renderOption: (item) => (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [_jsx(SquircleIcon, { size: 12, color: item.value === "default"
|
|
202
|
+
? theme.palette.text.primary
|
|
203
|
+
: item.value, style: {
|
|
204
|
+
backgroundColor: item.value === "default"
|
|
205
|
+
? theme.palette.text.primary
|
|
206
|
+
: item.value,
|
|
207
|
+
borderRadius: "3px",
|
|
208
|
+
} }), item.label] })),
|
|
155
209
|
},
|
|
156
210
|
},
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
211
|
+
]
|
|
212
|
+
: []),
|
|
213
|
+
...(supportsHighlight
|
|
214
|
+
? [
|
|
215
|
+
{
|
|
216
|
+
name: Extensions.Highlight,
|
|
217
|
+
label: _jsx(HighlighterIcon, { size: 14 }),
|
|
218
|
+
type: "menu",
|
|
219
|
+
buttonProps: {
|
|
220
|
+
style: { fontSize: 11, padding: "4px" },
|
|
221
|
+
},
|
|
222
|
+
items: [
|
|
223
|
+
{
|
|
224
|
+
label: "Default",
|
|
225
|
+
value: "default",
|
|
226
|
+
onClick: () => {
|
|
227
|
+
editor === null || editor === void 0 ? void 0 : editor.chain().focus().unsetHighlight().run();
|
|
228
|
+
},
|
|
164
229
|
},
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
230
|
+
...Object.keys(HighlightColors).map((color) => {
|
|
231
|
+
const colorKey = color;
|
|
232
|
+
return {
|
|
233
|
+
label: color,
|
|
234
|
+
value: HighlightColors[colorKey],
|
|
235
|
+
onClick: () => {
|
|
236
|
+
editor === null || editor === void 0 ? void 0 : editor.chain().focus().setHighlight({ color: HighlightColors[colorKey] }).run();
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}),
|
|
240
|
+
],
|
|
241
|
+
dropDownProps: {
|
|
242
|
+
renderOption: (item) => (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [_jsx(SquircleIcon, { size: 12, color: item.value === "default"
|
|
243
|
+
? theme.palette.text.primary
|
|
244
|
+
: item.value, style: {
|
|
245
|
+
backgroundColor: item.value === "default"
|
|
246
|
+
? "transparent"
|
|
247
|
+
: item.value,
|
|
248
|
+
borderRadius: "3px",
|
|
249
|
+
} }), item.label] })),
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
]
|
|
253
|
+
: []),
|
|
254
|
+
...(supportsLink
|
|
255
|
+
? [
|
|
256
|
+
{
|
|
257
|
+
name: Extensions.Link,
|
|
258
|
+
icon: LinkIcon,
|
|
259
|
+
type: "button",
|
|
260
|
+
isActive: (editor) => editor.isActive("link"),
|
|
261
|
+
onClick: () => {
|
|
262
|
+
openLinkEditor();
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
]
|
|
266
|
+
: []),
|
|
179
267
|
{
|
|
180
268
|
name: Extensions.Bold,
|
|
181
269
|
icon: BoldIcon,
|
|
@@ -212,6 +300,32 @@ const getMenuItems = (editor, customMenuItems, theme) => {
|
|
|
212
300
|
editor.chain().focus().toggleStrike().run();
|
|
213
301
|
},
|
|
214
302
|
},
|
|
303
|
+
...(supportsInlineCode
|
|
304
|
+
? [
|
|
305
|
+
{
|
|
306
|
+
name: Extensions.Code,
|
|
307
|
+
icon: CodeIcon,
|
|
308
|
+
type: "button",
|
|
309
|
+
isActive: (editor) => editor.isActive("code"),
|
|
310
|
+
onClick: (editor) => {
|
|
311
|
+
toggleInlineCode(editor);
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
]
|
|
315
|
+
: []),
|
|
316
|
+
...(supportsSyntaxCodeBlock
|
|
317
|
+
? [
|
|
318
|
+
{
|
|
319
|
+
name: Extensions.CodeBlock,
|
|
320
|
+
icon: SquareCodeIcon,
|
|
321
|
+
type: "button",
|
|
322
|
+
isActive: (editor) => editor.isActive("codeBlock"),
|
|
323
|
+
onClick: (editor) => {
|
|
324
|
+
toggleCodeBlock(editor);
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
]
|
|
328
|
+
: []),
|
|
215
329
|
{
|
|
216
330
|
name: Extensions.ClearFormatting,
|
|
217
331
|
icon: RemoveFormattingIcon,
|
|
@@ -224,12 +338,11 @@ const getMenuItems = (editor, customMenuItems, theme) => {
|
|
|
224
338
|
];
|
|
225
339
|
};
|
|
226
340
|
const BubbleMenuContent = styled.div `
|
|
227
|
-
position: fixed;
|
|
228
341
|
display: flex;
|
|
229
342
|
justify-content: space-between;
|
|
230
343
|
align-items: center;
|
|
231
344
|
padding: 2px;
|
|
232
|
-
gap:
|
|
345
|
+
gap: 4px;
|
|
233
346
|
|
|
234
347
|
color: ${({ theme }) => theme.palette.text.primary};
|
|
235
348
|
background-color: ${({ theme }) => theme.palette.input.background};
|
|
@@ -272,51 +385,43 @@ const BubbleItemButton = styled(Button) `
|
|
|
272
385
|
background-color: ${({ theme }) => theme.palette.action.hover};
|
|
273
386
|
}
|
|
274
387
|
`;
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
388
|
+
const CodeBlockBubbleTools = ({ editor, theme, }) => {
|
|
389
|
+
const nodeTypeMenu = getMenuItems(editor, [], theme, () => undefined).find((item) => item.name === "node_type");
|
|
390
|
+
return (_jsxs(_Fragment, { children: [(nodeTypeMenu === null || nodeTypeMenu === void 0 ? void 0 : nodeTypeMenu.type) === "menu" && (_jsx(DropDownMenu, Object.assign({ data: nodeTypeMenu.items, size: "xs", arrow: nodeTypeMenu.arrow, buttonProps: nodeTypeMenu.buttonProps, variant: "subtle", buttonRender: nodeTypeMenu.buttonRender, onItemSelect: (item) => { var _a, _b; return (_b = (_a = item === null || item === void 0 ? void 0 : item.data) === null || _a === void 0 ? void 0 : _a.command) === null || _b === void 0 ? void 0 : _b.call(_a, editor, ""); }, dropDownProps: {
|
|
391
|
+
style: { width: 135 },
|
|
392
|
+
} }, nodeTypeMenu.dropDownProps, { children: nodeTypeMenu.icon
|
|
393
|
+
? (_jsx(nodeTypeMenu.icon, { size: 14 }))
|
|
394
|
+
: (nodeTypeMenu.label || nodeTypeMenu.name) }))), _jsx(CodeBlockLanguageSelect, { editor: editor }), _jsx(CodeBlockWrapButton, { editor: editor }), _jsx(CodeBlockFormatButton, { editor: editor }), _jsx(CodeBlockCopyButton, { editor: editor })] }));
|
|
395
|
+
};
|
|
396
|
+
const BubbleMenu = ({ className, editor, customMenuItems = [], }) => {
|
|
279
397
|
const theme = useTheme();
|
|
280
|
-
|
|
281
|
-
if (open && onOpen) {
|
|
282
|
-
onOpen(elements.floating);
|
|
283
|
-
}
|
|
284
|
-
}, [open, onOpen, elements.floating]);
|
|
285
|
-
const elementWidth = ((_a = elements.floating) === null || _a === void 0 ? void 0 : _a.offsetWidth) || 0;
|
|
398
|
+
const [linkEditorOpen, setLinkEditorOpen] = useState(false);
|
|
286
399
|
const { from, to } = editor.state.selection;
|
|
287
400
|
const selectedText = editor.state.doc.textBetween(from, to, "\n", "\n");
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (
|
|
294
|
-
|
|
401
|
+
const isLinkSelection = editor.isActive("link");
|
|
402
|
+
const isCodeBlockSelection = editor.isActive("codeBlock") && hasSyntaxHighlightedCodeBlock(editor);
|
|
403
|
+
useEffect(() => {
|
|
404
|
+
setLinkEditorOpen(false);
|
|
405
|
+
}, [from, to]);
|
|
406
|
+
if (isLinkSelection || linkEditorOpen) {
|
|
407
|
+
return (_jsx(BubbleMenuContent, { className: className, children: _jsx(LinkEditor, { editor: editor, autoFocus: true, onClose: () => setLinkEditorOpen(false) }) }));
|
|
295
408
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (rightOverflow > -10) {
|
|
299
|
-
left -= rightOverflow + 10; // add some padding
|
|
409
|
+
if (isCodeBlockSelection) {
|
|
410
|
+
return (_jsx(BubbleMenuContent, { className: className, children: _jsx(CodeBlockBubbleTools, { editor: editor, theme: theme }) }));
|
|
300
411
|
}
|
|
301
|
-
return (_jsx(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
var _a;
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
style: { width: 135 },
|
|
316
|
-
} }, item.dropDownProps, { children: item.icon
|
|
317
|
-
? (_jsx(item.icon, { size: 14 }))
|
|
318
|
-
: (item.label || item.name) }), item.name));
|
|
319
|
-
}
|
|
320
|
-
}) })) }));
|
|
412
|
+
return (_jsx(BubbleMenuContent, { className: className, children: getMenuItems(editor, customMenuItems, theme, () => setLinkEditorOpen(true)).map((item) => {
|
|
413
|
+
var _a;
|
|
414
|
+
if (item.type === "button") {
|
|
415
|
+
const isActive = (_a = item.isActive) === null || _a === void 0 ? void 0 : _a.call(item, editor);
|
|
416
|
+
return (_jsx(BubbleItemButton, { variant: "subtle", onClick: () => { var _a; return (_a = item === null || item === void 0 ? void 0 : item.onClick) === null || _a === void 0 ? void 0 : _a.call(item, editor); }, color: isActive ? "primary" : undefined, selected: isActive, children: item.icon && _jsx(item.icon, { size: 14 }) }, item.name));
|
|
417
|
+
}
|
|
418
|
+
if (item.type === "menu") {
|
|
419
|
+
return (_jsx(DropDownMenu, Object.assign({ data: item.items, size: "xs", arrow: item.arrow, buttonProps: item.buttonProps, variant: "subtle", buttonRender: item.buttonRender, onItemSelect: (item) => { var _a, _b; return (_b = (_a = item === null || item === void 0 ? void 0 : item.data) === null || _a === void 0 ? void 0 : _a.command) === null || _b === void 0 ? void 0 : _b.call(_a, editor, selectedText); }, dropDownProps: {
|
|
420
|
+
style: { width: 135 },
|
|
421
|
+
} }, item.dropDownProps, { children: item.icon
|
|
422
|
+
? (_jsx(item.icon, { size: 14 }))
|
|
423
|
+
: (item.label || item.name) }), item.name));
|
|
424
|
+
}
|
|
425
|
+
}) }));
|
|
321
426
|
};
|
|
322
427
|
export default BubbleMenu;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Editor } from "@tiptap/react";
|
|
2
|
+
type CodeBlockCopyButtonProps = {
|
|
3
|
+
className?: string;
|
|
4
|
+
editor?: Editor | null;
|
|
5
|
+
text?: string;
|
|
6
|
+
size?: "xs" | "sm";
|
|
7
|
+
};
|
|
8
|
+
declare const CodeBlockCopyButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<CodeBlockCopyButtonProps, never>> & string & Omit<({ className, editor, text, size }: CodeBlockCopyButtonProps) => import("react/jsx-runtime").JSX.Element, keyof import("react").Component<any, {}, any>>;
|
|
9
|
+
export default CodeBlockCopyButton;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { useEffect, useState } from "react";
|
|
12
|
+
import styled from "styled-components";
|
|
13
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
14
|
+
import { Button } from "../../Button";
|
|
15
|
+
import { copyCodeBlockText, getActiveCodeBlockText, } from "../Utils/codeBlockUtils";
|
|
16
|
+
const CodeBlockCopyButton = styled(({ className, editor, text, size = "xs" }) => {
|
|
17
|
+
const [copied, setCopied] = useState(false);
|
|
18
|
+
const [copyFailed, setCopyFailed] = useState(false);
|
|
19
|
+
const code = text !== null && text !== void 0 ? text : getActiveCodeBlockText(editor || null);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!copied && !copyFailed)
|
|
22
|
+
return;
|
|
23
|
+
const timeout = window.setTimeout(() => {
|
|
24
|
+
setCopied(false);
|
|
25
|
+
setCopyFailed(false);
|
|
26
|
+
}, 1400);
|
|
27
|
+
return () => window.clearTimeout(timeout);
|
|
28
|
+
}, [copied, copyFailed]);
|
|
29
|
+
return (_jsx(Button, { className: className, size: size, variant: "outlined", title: copyFailed ? "Unable to copy" : copied ? "Copied" : "Copy code", "aria-label": copyFailed ? "Unable to copy code" : copied ? "Copied" : "Copy code", disabled: !code, onClick: (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
30
|
+
event.preventDefault();
|
|
31
|
+
event.stopPropagation();
|
|
32
|
+
try {
|
|
33
|
+
yield copyCodeBlockText(code);
|
|
34
|
+
setCopied(true);
|
|
35
|
+
setCopyFailed(false);
|
|
36
|
+
}
|
|
37
|
+
catch (_a) {
|
|
38
|
+
setCopied(false);
|
|
39
|
+
setCopyFailed(true);
|
|
40
|
+
}
|
|
41
|
+
}), children: copied ? _jsx(CheckIcon, { size: 14 }) : _jsx(CopyIcon, { size: 14 }) }));
|
|
42
|
+
}) `
|
|
43
|
+
padding: 3px;
|
|
44
|
+
`;
|
|
45
|
+
export default CodeBlockCopyButton;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Editor } from "@tiptap/react";
|
|
2
|
+
type CodeBlockFormatButtonProps = {
|
|
3
|
+
className?: string;
|
|
4
|
+
editor?: Editor | null;
|
|
5
|
+
language?: string;
|
|
6
|
+
onFormat?: () => Promise<void>;
|
|
7
|
+
size?: "xs" | "sm";
|
|
8
|
+
};
|
|
9
|
+
declare const CodeBlockFormatButton: ({ className, editor, language, onFormat, size, }: CodeBlockFormatButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export default CodeBlockFormatButton;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { useEffect, useState } from "react";
|
|
12
|
+
import { CheckIcon, TriangleAlertIcon, WandSparklesIcon } from "lucide-react";
|
|
13
|
+
import { Button } from "../../Button";
|
|
14
|
+
import { canFormatCodeBlockLanguage, formatActiveCodeBlock, getCodeBlockLanguage, } from "../Utils/codeBlockUtils";
|
|
15
|
+
const CodeBlockFormatButton = ({ className, editor, language, onFormat, size = "xs", }) => {
|
|
16
|
+
const [formatted, setFormatted] = useState(false);
|
|
17
|
+
const [formatFailed, setFormatFailed] = useState(false);
|
|
18
|
+
const codeLanguage = language !== null && language !== void 0 ? language : getCodeBlockLanguage(editor || null);
|
|
19
|
+
const canFormat = canFormatCodeBlockLanguage(codeLanguage);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!formatted && !formatFailed)
|
|
22
|
+
return;
|
|
23
|
+
const timeout = window.setTimeout(() => {
|
|
24
|
+
setFormatted(false);
|
|
25
|
+
setFormatFailed(false);
|
|
26
|
+
}, 1400);
|
|
27
|
+
return () => window.clearTimeout(timeout);
|
|
28
|
+
}, [formatted, formatFailed]);
|
|
29
|
+
return (_jsx(Button, { className: className, size: size, variant: "outlined", title: !canFormat
|
|
30
|
+
? "Formatting is not supported for this language"
|
|
31
|
+
: formatFailed
|
|
32
|
+
? "Unable to format code"
|
|
33
|
+
: formatted
|
|
34
|
+
? "Formatted"
|
|
35
|
+
: "Format code", "aria-label": !canFormat
|
|
36
|
+
? "Formatting is not supported for this language"
|
|
37
|
+
: formatFailed
|
|
38
|
+
? "Unable to format code"
|
|
39
|
+
: formatted
|
|
40
|
+
? "Formatted"
|
|
41
|
+
: "Format code", disabled: !canFormat || (!onFormat && !(editor === null || editor === void 0 ? void 0 : editor.isActive("codeBlock"))), onClick: (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
event.stopPropagation();
|
|
44
|
+
try {
|
|
45
|
+
if (onFormat) {
|
|
46
|
+
yield onFormat();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
yield formatActiveCodeBlock(editor || null);
|
|
50
|
+
}
|
|
51
|
+
setFormatted(true);
|
|
52
|
+
setFormatFailed(false);
|
|
53
|
+
}
|
|
54
|
+
catch (_a) {
|
|
55
|
+
setFormatted(false);
|
|
56
|
+
setFormatFailed(true);
|
|
57
|
+
}
|
|
58
|
+
}), children: formatted ? (_jsx(CheckIcon, { size: 14 })) : formatFailed ? (_jsx(TriangleAlertIcon, { size: 14 })) : (_jsx(WandSparklesIcon, { size: 14 })) }));
|
|
59
|
+
};
|
|
60
|
+
export default CodeBlockFormatButton;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Editor } from "@tiptap/react";
|
|
2
|
+
type CodeBlockLanguageSelectProps = {
|
|
3
|
+
editor: Editor | null;
|
|
4
|
+
};
|
|
5
|
+
declare const CodeBlockLanguageSelect: ({ editor }: CodeBlockLanguageSelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default CodeBlockLanguageSelect;
|