@monolith-forensics/monolith-ui 1.9.1-dev.1 → 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.
Files changed (46) hide show
  1. package/dist/DropDownMenu/components/MenuItemList.js +32 -12
  2. package/dist/DropDownMenu/components/StyledInnerItemContainer.js +1 -0
  3. package/dist/RichTextEditor/Components/BubbleMenu.d.ts +8 -8
  4. package/dist/RichTextEditor/Components/BubbleMenu.js +198 -93
  5. package/dist/RichTextEditor/Components/CodeBlockCopyButton.d.ts +9 -0
  6. package/dist/RichTextEditor/Components/CodeBlockCopyButton.js +45 -0
  7. package/dist/RichTextEditor/Components/CodeBlockFormatButton.d.ts +10 -0
  8. package/dist/RichTextEditor/Components/CodeBlockFormatButton.js +60 -0
  9. package/dist/RichTextEditor/Components/CodeBlockLanguageSelect.d.ts +6 -0
  10. package/dist/RichTextEditor/Components/CodeBlockLanguageSelect.js +21 -0
  11. package/dist/RichTextEditor/Components/CodeBlockNodeView.d.ts +3 -0
  12. package/dist/RichTextEditor/Components/CodeBlockNodeView.js +27 -0
  13. package/dist/RichTextEditor/Components/CodeBlockWrapButton.d.ts +10 -0
  14. package/dist/RichTextEditor/Components/CodeBlockWrapButton.js +17 -0
  15. package/dist/RichTextEditor/Components/LinkEditor.d.ts +8 -0
  16. package/dist/RichTextEditor/Components/LinkEditor.js +94 -0
  17. package/dist/RichTextEditor/Enums/Controls.d.ts +5 -1
  18. package/dist/RichTextEditor/Enums/Controls.js +4 -0
  19. package/dist/RichTextEditor/Enums/Extensions.d.ts +4 -0
  20. package/dist/RichTextEditor/Enums/Extensions.js +4 -0
  21. package/dist/RichTextEditor/Enums/HighlightColors.d.ts +9 -0
  22. package/dist/RichTextEditor/Enums/HighlightColors.js +10 -0
  23. package/dist/RichTextEditor/Enums/SlashCommands.d.ts +1 -0
  24. package/dist/RichTextEditor/Enums/SlashCommands.js +1 -0
  25. package/dist/RichTextEditor/Extensions/getSlashCommand.js +16 -1
  26. package/dist/RichTextEditor/Extensions/getTiptapExtensions.d.ts +10 -2
  27. package/dist/RichTextEditor/Extensions/getTiptapExtensions.js +158 -31
  28. package/dist/RichTextEditor/Plugins/ImageActionsPlugin.js +6 -109
  29. package/dist/RichTextEditor/Plugins/UploadImagesPlugin.js +1 -0
  30. package/dist/RichTextEditor/RichTextEditor.d.ts +5 -2
  31. package/dist/RichTextEditor/RichTextEditor.js +186 -13
  32. package/dist/RichTextEditor/Toolbar/Control.d.ts +6 -2
  33. package/dist/RichTextEditor/Toolbar/Control.js +13 -6
  34. package/dist/RichTextEditor/Toolbar/Controls.d.ts +2 -0
  35. package/dist/RichTextEditor/Toolbar/Controls.js +14 -0
  36. package/dist/RichTextEditor/Toolbar/ControlsGroup.js +1 -0
  37. package/dist/RichTextEditor/Toolbar/Toolbar.js +62 -9
  38. package/dist/RichTextEditor/Utils/codeBlockUtils.d.ts +20 -0
  39. package/dist/RichTextEditor/Utils/codeBlockUtils.js +137 -0
  40. package/dist/RichTextEditor/Utils/codeUtils.d.ts +3 -0
  41. package/dist/RichTextEditor/Utils/codeUtils.js +12 -0
  42. package/dist/RichTextEditor/Utils/linkUtils.d.ts +19 -0
  43. package/dist/RichTextEditor/Utils/linkUtils.js +57 -0
  44. package/package.json +8 -1
  45. package/dist/RichTextEditor/Extensions/BubbleMenuExtension.d.ts +0 -7
  46. package/dist/RichTextEditor/Extensions/BubbleMenuExtension.js +0 -157
@@ -1,33 +1,42 @@
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 ControlsGroup from "./ControlsGroup";
4
- import { UndoControl, RedoControl, BoldControl, ItalicControl, UnderlineControl, StrikeThroughControl, Heading1Control, Heading2Control, Heading3Control, Heading4Control, BulletListControl, OrderedListControl, AlignLeftControl, AlignCenterControl, AlignRightControl, AlignJustifiedControl, } from "./Controls";
4
+ import { UndoControl, RedoControl, BoldControl, ItalicControl, UnderlineControl, StrikeThroughControl, CodeControl, CodeBlockControl, Heading1Control, Heading2Control, Heading3Control, Heading4Control, BulletListControl, OrderedListControl, AlignLeftControl, AlignCenterControl, AlignRightControl, AlignJustifiedControl, } from "./Controls";
5
5
  import { Controls } from "../Enums";
6
6
  import { DropDownMenu } from "../../DropDownMenu";
7
7
  import Fonts from "../Enums/Fonts";
8
- import { useContext } from "react";
8
+ import { useContext, useState } from "react";
9
9
  import RichTextEditorContext from "../Contexts/RichTextEditorContext";
10
10
  import { Button } from "../../Button";
11
+ import { Popover } from "../../Popover";
11
12
  import TextColors from "../Enums/TextColors";
12
- import { SquircleIcon } from "lucide-react";
13
+ import HighlightColors from "../Enums/HighlightColors";
14
+ import LinkEditor from "../Components/LinkEditor";
15
+ import CodeBlockLanguageSelect from "../Components/CodeBlockLanguageSelect";
16
+ import CodeBlockWrapButton from "../Components/CodeBlockWrapButton";
17
+ import CodeBlockFormatButton from "../Components/CodeBlockFormatButton";
18
+ import { hasSyntaxHighlightedCodeBlock } from "../Utils/codeBlockUtils";
19
+ import { HighlighterIcon, LinkIcon, PaletteIcon, SquircleIcon, TypeIcon, } from "lucide-react";
13
20
  export const Toolbar = styled(({ className, editor, toolbarOptions }) => {
14
21
  var _a;
15
22
  const theme = useTheme();
16
23
  const { controls } = toolbarOptions || {};
24
+ const [linkPopoverOpen, setLinkPopoverOpen] = useState(false);
17
25
  const customItems = controls === null || controls === void 0 ? void 0 : controls.filter((control) => typeof control !== "string" &&
18
26
  (control.type === "menu" || control.type === "button"));
19
27
  const { font, setFont } = useContext(RichTextEditorContext);
28
+ const showCodeBlockLanguageSelect = (editor === null || editor === void 0 ? void 0 : editor.isActive("codeBlock")) && hasSyntaxHighlightedCodeBlock(editor);
20
29
  return (_jsxs("div", { className: className, children: [(_a = customItems === null || customItems === void 0 ? void 0 : customItems.map) === null || _a === void 0 ? void 0 : _a.call(customItems, (item, index) => {
21
30
  var _a, _b;
22
31
  if (item.type === "button") {
23
- return (_jsx(Button, Object.assign({}, item.options, { children: (_a = item === null || item === void 0 ? void 0 : item.options) === null || _a === void 0 ? void 0 : _a.label }), index));
32
+ return (_jsx(Button, Object.assign({ size: "xs" }, item.options, { children: (_a = item === null || item === void 0 ? void 0 : item.options) === null || _a === void 0 ? void 0 : _a.label }), index));
24
33
  }
25
34
  else if (item.type === "menu") {
26
35
  return (_jsx(DropDownMenu, Object.assign({ dropDownProps: {
27
36
  style: {
28
37
  width: 135,
29
38
  },
30
- } }, item.options, { children: (_b = item === null || item === void 0 ? void 0 : item.options) === null || _b === void 0 ? void 0 : _b.label }), index));
39
+ } }, item.options, { size: "xs", children: (_b = item === null || item === void 0 ? void 0 : item.options) === null || _b === void 0 ? void 0 : _b.label }), index));
31
40
  }
32
41
  }), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.FONT)) && (_jsx(DropDownMenu, { data: Object.values(Fonts).map((font) => ({
33
42
  label: font,
@@ -35,12 +44,13 @@ export const Toolbar = styled(({ className, editor, toolbarOptions }) => {
35
44
  onClick: () => {
36
45
  setFont(font);
37
46
  },
38
- })), size: "xxs", variant: "outlined", arrow: true, dropDownProps: {
47
+ })), size: "xs", variant: "outlined", dropDownProps: {
39
48
  style: {
40
49
  width: 135,
41
50
  },
42
51
  }, buttonProps: {
43
52
  title: "Select Font",
53
+ leftSection: _jsx(TypeIcon, { size: 12 }),
44
54
  }, children: (font || Fonts.DEFAULT) })), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.UNDO)) && _jsx(UndoControl, { editor: editor }), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.REDO)) && _jsx(RedoControl, { editor: editor })] }), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.COLOR)) && (_jsx(DropDownMenu, { data: [
45
55
  {
46
56
  label: "Default",
@@ -66,20 +76,63 @@ export const Toolbar = styled(({ className, editor, toolbarOptions }) => {
66
76
  ? theme.palette.text.primary
67
77
  : item.value,
68
78
  borderRadius: "3px",
69
- } }), item.label] })), size: "xxs", variant: "outlined", arrow: true, buttonProps: {
79
+ } }), item.label] })), size: "xs", variant: "outlined", buttonProps: {
70
80
  title: "Select Color",
81
+ style: { padding: "1px 6px" },
82
+ }, dropDownProps: {
83
+ style: {
84
+ width: 120,
85
+ },
86
+ }, children: _jsx(PaletteIcon, { size: 14 }) })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HIGHLIGHT)) && (_jsx(DropDownMenu, { data: [
87
+ {
88
+ label: "Default",
89
+ value: "default",
90
+ onClick: () => {
91
+ editor === null || editor === void 0 ? void 0 : editor.chain().focus().unsetHighlight().run();
92
+ },
93
+ },
94
+ ...Object.keys(HighlightColors).map((color) => {
95
+ const colorKey = color;
96
+ return {
97
+ label: color,
98
+ value: HighlightColors[colorKey],
99
+ onClick: () => {
100
+ editor === null || editor === void 0 ? void 0 : editor.chain().focus().setHighlight({ color: HighlightColors[colorKey] }).run();
101
+ },
102
+ };
103
+ }),
104
+ ], renderOption: (item) => (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [_jsx(SquircleIcon, { size: 12, color: item.value === "default"
105
+ ? theme.palette.text.primary
106
+ : item.value, style: {
107
+ backgroundColor: item.value === "default"
108
+ ? "transparent"
109
+ : item.value,
110
+ borderRadius: "3px",
111
+ } }), item.label] })), size: "xs", variant: "outlined", buttonProps: {
112
+ title: "Select Highlight",
113
+ style: { padding: "1px 6px" },
71
114
  }, dropDownProps: {
72
115
  style: {
73
116
  width: 100,
117
+ height: 210,
74
118
  },
75
- }, children: "Color" })), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.BOLD)) && _jsx(BoldControl, { editor: editor }), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.ITALIC)) && (_jsx(ItalicControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.UNDERLINE)) && (_jsx(UnderlineControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.STRIKE)) && (_jsx(StrikeThroughControl, { editor: editor }))] }), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HEADING_1)) && (_jsx(Heading1Control, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HEADING_2)) && (_jsx(Heading2Control, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HEADING_3)) && (_jsx(Heading3Control, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HEADING_4)) && (_jsx(Heading4Control, { editor: editor }))] }), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.BULLET_LIST)) && (_jsx(BulletListControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.ORDERED_LIST)) && (_jsx(OrderedListControl, { editor: editor }))] }), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.TEXT_ALIGN_LEFT)) && (_jsx(AlignLeftControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.TEXT_ALIGN_CENTER)) && (_jsx(AlignCenterControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.TEXT_ALIGN_RIGHT)) && (_jsx(AlignRightControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.TEXT_ALIGN_JUSTIFIED)) && (_jsx(AlignJustifiedControl, { editor: editor }))] })] }));
119
+ }, children: _jsx(HighlighterIcon, { size: 14 }) })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.LINK)) && (_jsxs(Popover, { opened: linkPopoverOpen, onChange: setLinkPopoverOpen, position: "bottom", width: 330, trapFocus: true, children: [_jsx(Popover.Target, { children: _jsx(Button, { size: "xs", variant: "outlined", title: "Link", "aria-label": "Link", selected: linkPopoverOpen || Boolean(editor === null || editor === void 0 ? void 0 : editor.isActive("link")), disabled: !editor, style: { padding: "1px 6px" }, children: _jsx(LinkIcon, { size: 14 }) }) }), _jsx(Popover.Dropdown, { children: editor && (_jsx(LinkEditor, { editor: editor, autoFocus: linkPopoverOpen, onClose: () => setLinkPopoverOpen(false) })) })] })), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.BOLD)) && _jsx(BoldControl, { editor: editor }), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.ITALIC)) && (_jsx(ItalicControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.UNDERLINE)) && (_jsx(UnderlineControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.STRIKE)) && (_jsx(StrikeThroughControl, { editor: editor }))] }), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.CODE)) && _jsx(CodeControl, { editor: editor }), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.CODE_BLOCK)) && (_jsx(CodeBlockControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.CODE_BLOCK)) &&
120
+ showCodeBlockLanguageSelect && (_jsxs(_Fragment, { children: [_jsx(CodeBlockLanguageSelect, { editor: editor }), _jsx(CodeBlockWrapButton, { editor: editor }), _jsx(CodeBlockFormatButton, { editor: editor })] }))] }), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HEADING_1)) && (_jsx(Heading1Control, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HEADING_2)) && (_jsx(Heading2Control, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HEADING_3)) && (_jsx(Heading3Control, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.HEADING_4)) && (_jsx(Heading4Control, { editor: editor }))] }), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.BULLET_LIST)) && (_jsx(BulletListControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.ORDERED_LIST)) && (_jsx(OrderedListControl, { editor: editor }))] }), _jsxs(ControlsGroup, { children: [(controls === null || controls === void 0 ? void 0 : controls.includes(Controls.TEXT_ALIGN_LEFT)) && (_jsx(AlignLeftControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.TEXT_ALIGN_CENTER)) && (_jsx(AlignCenterControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.TEXT_ALIGN_RIGHT)) && (_jsx(AlignRightControl, { editor: editor })), (controls === null || controls === void 0 ? void 0 : controls.includes(Controls.TEXT_ALIGN_JUSTIFIED)) && (_jsx(AlignJustifiedControl, { editor: editor }))] })] }));
76
121
  }) `
77
122
  display: flex;
78
123
  flex-direction: row;
124
+ flex-wrap: wrap;
79
125
  justify-content: center;
80
126
  align-items: center;
81
127
  gap: 5px;
128
+ row-gap: 6px;
129
+ width: 100%;
82
130
  margin-bottom: 10px;
83
131
  border-radius: 5px 5px 0 0;
84
132
  border: 1px solid transparent;
133
+
134
+ > button,
135
+ > .mfui-DropDownMenu {
136
+ flex: 0 0 auto;
137
+ }
85
138
  `;
@@ -0,0 +1,20 @@
1
+ import { Editor } from "@tiptap/react";
2
+ export type CodeBlockLanguage = {
3
+ label: string;
4
+ value: string;
5
+ };
6
+ export declare const DEFAULT_CODE_BLOCK_LANGUAGE = "plaintext";
7
+ export declare const CODE_BLOCK_LANGUAGES: CodeBlockLanguage[];
8
+ export declare const getCodeBlockLanguage: (editor: Editor | null) => string;
9
+ export declare const getCodeBlockLanguageOption: (language?: string) => CodeBlockLanguage;
10
+ export declare const canFormatCodeBlockLanguage: (language?: string) => boolean;
11
+ export declare const formatCodeBlockText: (text: string, language?: string) => Promise<string>;
12
+ export declare const hasSyntaxHighlightedCodeBlock: (editor: Editor | null) => boolean;
13
+ export declare const setCodeBlockLanguage: (editor: Editor | null, language: string) => void;
14
+ export declare const getCodeBlockWrap: (editor: Editor | null) => boolean;
15
+ export declare const toggleCodeBlockWrap: (editor: Editor | null) => void;
16
+ export declare const toggleCodeBlock: (editor: Editor | null) => void;
17
+ export declare const getActiveCodeBlockText: (editor: Editor | null) => string;
18
+ export declare const replaceCodeBlockContent: (editor: Editor, from: number, to: number, text: string) => boolean;
19
+ export declare const formatActiveCodeBlock: (editor: Editor | null) => Promise<void>;
20
+ export declare const copyCodeBlockText: (text: string) => Promise<void>;
@@ -0,0 +1,137 @@
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 prettier from "prettier/standalone";
11
+ import * as prettierBabelPlugin from "prettier/plugins/babel";
12
+ import * as prettierEstreePlugin from "prettier/plugins/estree";
13
+ import * as prettierHtmlPlugin from "prettier/plugins/html";
14
+ import * as prettierMarkdownPlugin from "prettier/plugins/markdown";
15
+ import * as prettierPostcssPlugin from "prettier/plugins/postcss";
16
+ import * as prettierTypescriptPlugin from "prettier/plugins/typescript";
17
+ import * as prettierYamlPlugin from "prettier/plugins/yaml";
18
+ export const DEFAULT_CODE_BLOCK_LANGUAGE = "plaintext";
19
+ export const CODE_BLOCK_LANGUAGES = [
20
+ { label: "Plain Text", value: DEFAULT_CODE_BLOCK_LANGUAGE },
21
+ { label: "JavaScript", value: "javascript" },
22
+ { label: "TypeScript", value: "typescript" },
23
+ { label: "JSX", value: "jsx" },
24
+ { label: "TSX", value: "tsx" },
25
+ { label: "HTML", value: "html" },
26
+ { label: "CSS", value: "css" },
27
+ { label: "JSON", value: "json" },
28
+ { label: "Markdown", value: "markdown" },
29
+ { label: "Bash", value: "bash" },
30
+ { label: "Python", value: "python" },
31
+ { label: "SQL", value: "sql" },
32
+ { label: "YAML", value: "yaml" },
33
+ { label: "XML", value: "xml" },
34
+ ];
35
+ const PRETTIER_PARSERS = {
36
+ javascript: "babel",
37
+ jsx: "babel",
38
+ typescript: "typescript",
39
+ tsx: "typescript",
40
+ html: "html",
41
+ css: "css",
42
+ json: "json",
43
+ markdown: "markdown",
44
+ yaml: "yaml",
45
+ };
46
+ const PRETTIER_PLUGINS = [
47
+ prettierBabelPlugin,
48
+ prettierEstreePlugin,
49
+ prettierHtmlPlugin,
50
+ prettierMarkdownPlugin,
51
+ prettierPostcssPlugin,
52
+ prettierTypescriptPlugin,
53
+ prettierYamlPlugin,
54
+ ];
55
+ export const getCodeBlockLanguage = (editor) => {
56
+ var _a;
57
+ return (((_a = editor === null || editor === void 0 ? void 0 : editor.getAttributes("codeBlock")) === null || _a === void 0 ? void 0 : _a.language) ||
58
+ DEFAULT_CODE_BLOCK_LANGUAGE);
59
+ };
60
+ export const getCodeBlockLanguageOption = (language) => {
61
+ return (CODE_BLOCK_LANGUAGES.find((item) => item.value === language) ||
62
+ CODE_BLOCK_LANGUAGES[0]);
63
+ };
64
+ export const canFormatCodeBlockLanguage = (language) => {
65
+ return Boolean(language && PRETTIER_PARSERS[language]);
66
+ };
67
+ export const formatCodeBlockText = (text, language) => __awaiter(void 0, void 0, void 0, function* () {
68
+ const parser = language ? PRETTIER_PARSERS[language] : undefined;
69
+ if (!parser) {
70
+ throw new Error("Code formatting is not supported for this language.");
71
+ }
72
+ const formatted = yield prettier.format(text, {
73
+ parser,
74
+ plugins: PRETTIER_PLUGINS,
75
+ });
76
+ return formatted.replace(/\n$/, "");
77
+ });
78
+ export const hasSyntaxHighlightedCodeBlock = (editor) => {
79
+ return Boolean(editor === null || editor === void 0 ? void 0 : editor.extensionManager.extensions.find((extension) => { var _a; return extension.name === "codeBlock" && ((_a = extension.options) === null || _a === void 0 ? void 0 : _a.lowlight); }));
80
+ };
81
+ export const setCodeBlockLanguage = (editor, language) => {
82
+ if (!(editor === null || editor === void 0 ? void 0 : editor.isActive("codeBlock")))
83
+ return;
84
+ editor.chain().focus().updateAttributes("codeBlock", { language }).run();
85
+ };
86
+ export const getCodeBlockWrap = (editor) => {
87
+ var _a;
88
+ return Boolean((_a = editor === null || editor === void 0 ? void 0 : editor.getAttributes("codeBlock")) === null || _a === void 0 ? void 0 : _a.wrap);
89
+ };
90
+ export const toggleCodeBlockWrap = (editor) => {
91
+ if (!(editor === null || editor === void 0 ? void 0 : editor.isActive("codeBlock")))
92
+ return;
93
+ editor
94
+ .chain()
95
+ .focus()
96
+ .updateAttributes("codeBlock", { wrap: !getCodeBlockWrap(editor) })
97
+ .run();
98
+ };
99
+ export const toggleCodeBlock = (editor) => {
100
+ if (!editor)
101
+ return;
102
+ editor
103
+ .chain()
104
+ .focus()
105
+ .toggleCodeBlock({ language: DEFAULT_CODE_BLOCK_LANGUAGE })
106
+ .run();
107
+ };
108
+ export const getActiveCodeBlockText = (editor) => {
109
+ if (!(editor === null || editor === void 0 ? void 0 : editor.isActive("codeBlock")))
110
+ return "";
111
+ return editor.state.selection.$from.parent.textContent;
112
+ };
113
+ export const replaceCodeBlockContent = (editor, from, to, text) => {
114
+ return editor.commands.command(({ state, tr }) => {
115
+ if (text.length) {
116
+ tr.replaceWith(from, to, state.schema.text(text));
117
+ }
118
+ else {
119
+ tr.delete(from, to);
120
+ }
121
+ return true;
122
+ });
123
+ };
124
+ export const formatActiveCodeBlock = (editor) => __awaiter(void 0, void 0, void 0, function* () {
125
+ if (!(editor === null || editor === void 0 ? void 0 : editor.isActive("codeBlock")))
126
+ return;
127
+ const language = getCodeBlockLanguage(editor);
128
+ const formatted = yield formatCodeBlockText(getActiveCodeBlockText(editor), language);
129
+ replaceCodeBlockContent(editor, editor.state.selection.$from.start(), editor.state.selection.$from.end(), formatted);
130
+ });
131
+ export const copyCodeBlockText = (text) => __awaiter(void 0, void 0, void 0, function* () {
132
+ var _a;
133
+ if (!((_a = navigator.clipboard) === null || _a === void 0 ? void 0 : _a.writeText)) {
134
+ throw new Error("Clipboard copying is not supported by this browser.");
135
+ }
136
+ yield navigator.clipboard.writeText(text);
137
+ });
@@ -0,0 +1,3 @@
1
+ import { Editor } from "@tiptap/react";
2
+ export declare const hasInlineCode: (editor: Editor | null) => boolean;
3
+ export declare const toggleInlineCode: (editor: Editor | null) => void;
@@ -0,0 +1,12 @@
1
+ export const hasInlineCode = (editor) => {
2
+ return Boolean(editor === null || editor === void 0 ? void 0 : editor.extensionManager.extensions.find((extension) => extension.name === "code"));
3
+ };
4
+ export const toggleInlineCode = (editor) => {
5
+ if (!editor || !hasInlineCode(editor))
6
+ return;
7
+ if (editor.isActive("code")) {
8
+ editor.chain().focus().unsetCode().run();
9
+ return;
10
+ }
11
+ editor.chain().focus().unsetAllMarks().setCode().run();
12
+ };
@@ -0,0 +1,19 @@
1
+ import { EditorState } from "@tiptap/pm/state";
2
+ export declare const LINK_TARGET = "_blank";
3
+ export declare const LINK_REL = "noopener noreferrer nofollow";
4
+ export type NormalizedLink = {
5
+ href: string;
6
+ isValid: boolean;
7
+ };
8
+ export declare const normalizeLinkUrl: (value?: string | null) => NormalizedLink;
9
+ export declare const getLinkAttributes: (href: string) => {
10
+ href: string;
11
+ target: string;
12
+ rel: string;
13
+ class: string;
14
+ };
15
+ export declare const openLink: (href?: string | null) => void;
16
+ export declare const getLinkRangeAtPosition: (state: EditorState, pos: number) => void | import("@tiptap/core").Range;
17
+ export declare const getLinkAttributesAtPosition: (state: EditorState, pos: number) => {
18
+ href?: string;
19
+ } | undefined;
@@ -0,0 +1,57 @@
1
+ import { getMarkRange } from "@tiptap/core";
2
+ export const LINK_TARGET = "_blank";
3
+ export const LINK_REL = "noopener noreferrer nofollow";
4
+ export const normalizeLinkUrl = (value) => {
5
+ const trimmedValue = (value === null || value === void 0 ? void 0 : value.trim()) || "";
6
+ if (!trimmedValue) {
7
+ return { href: "", isValid: false };
8
+ }
9
+ const href = /^[a-z][a-z0-9+.-]*:/i.test(trimmedValue)
10
+ ? trimmedValue
11
+ : `https://${trimmedValue}`;
12
+ try {
13
+ const url = new URL(href);
14
+ const isValid = url.protocol === "http:" || url.protocol === "https:";
15
+ return {
16
+ href,
17
+ isValid,
18
+ };
19
+ }
20
+ catch (_a) {
21
+ return {
22
+ href,
23
+ isValid: false,
24
+ };
25
+ }
26
+ };
27
+ export const getLinkAttributes = (href) => ({
28
+ href,
29
+ target: LINK_TARGET,
30
+ rel: LINK_REL,
31
+ class: "editor-link",
32
+ });
33
+ export const openLink = (href) => {
34
+ if (!href)
35
+ return;
36
+ const normalizedLink = normalizeLinkUrl(href);
37
+ if (!normalizedLink.isValid)
38
+ return;
39
+ window.open(normalizedLink.href, "_blank", "noopener,noreferrer");
40
+ };
41
+ export const getLinkRangeAtPosition = (state, pos) => {
42
+ const linkMark = state.schema.marks.link;
43
+ if (!linkMark)
44
+ return;
45
+ return getMarkRange(state.doc.resolve(pos), linkMark);
46
+ };
47
+ export const getLinkAttributesAtPosition = (state, pos) => {
48
+ const linkMark = state.schema.marks.link;
49
+ if (!linkMark)
50
+ return;
51
+ const $pos = state.doc.resolve(pos);
52
+ const nextNode = $pos.parent.childAfter($pos.parentOffset).node;
53
+ const previousNode = $pos.parent.childBefore($pos.parentOffset).node;
54
+ const mark = (nextNode === null || nextNode === void 0 ? void 0 : nextNode.marks.find((mark) => mark.type === linkMark)) ||
55
+ (previousNode === null || previousNode === void 0 ? void 0 : previousNode.marks.find((mark) => mark.type === linkMark));
56
+ return mark === null || mark === void 0 ? void 0 : mark.attrs;
57
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monolith-forensics/monolith-ui",
3
- "version": "1.9.1-dev.1",
3
+ "version": "1.9.1-dev.10",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "author": "Matt Danner (Monolith Forensics LLC)",
@@ -41,9 +41,13 @@
41
41
  "@radix-ui/react-tooltip": "^1.0.7",
42
42
  "@tabler/icons-react": "^3.11.0",
43
43
  "@tiptap/core": "3.22.4",
44
+ "@tiptap/extension-code-block": "3.22.4",
45
+ "@tiptap/extension-code-block-lowlight": "3.22.4",
44
46
  "@tiptap/extension-color": "3.22.4",
47
+ "@tiptap/extension-highlight": "3.22.4",
45
48
  "@tiptap/extension-horizontal-rule": "3.22.4",
46
49
  "@tiptap/extension-image": "3.22.4",
50
+ "@tiptap/extension-link": "3.22.4",
47
51
  "@tiptap/extension-table": "3.22.4",
48
52
  "@tiptap/extension-text-align": "3.22.4",
49
53
  "@tiptap/extension-text-style": "3.22.4",
@@ -58,10 +62,13 @@
58
62
  "d3-shape": "^3.2.0",
59
63
  "deepmerge": "^4.3.1",
60
64
  "exceljs": "^4.4.0",
65
+ "highlight.js": "^11.11.1",
66
+ "lowlight": "^3.3.0",
61
67
  "lucide-react": "^0.469.0",
62
68
  "moment": "^2.29.1",
63
69
  "overlayscrollbars": "^2.6.0",
64
70
  "overlayscrollbars-react": "^0.5.6",
71
+ "prettier": "^3.8.3",
65
72
  "react-dropzone": "^14.2.3",
66
73
  "react-icons": "^5.2.1",
67
74
  "react-pdf": "^9.1.1",
@@ -1,7 +0,0 @@
1
- import { Extension } from "@tiptap/core";
2
- import { BubbleItem } from "../Components/BubbleMenu";
3
- export type BubbleMenuOptions = {
4
- customMenuItems?: BubbleItem[];
5
- };
6
- declare const BubbleMenu: Extension<any, any>;
7
- export default BubbleMenu;
@@ -1,157 +0,0 @@
1
- import { Extension, isNodeSelection, isTextSelection, posToDOMRect, } from "@tiptap/core";
2
- import { Plugin, PluginKey } from "@tiptap/pm/state";
3
- import { ReactRenderer } from "@tiptap/react";
4
- import BubbleMenuComponent from "../Components/BubbleMenu";
5
- class Menu {
6
- constructor({ view, editor, customMenuItems, }) {
7
- this.mousedownHandler = (event) => {
8
- this.preventShow = true;
9
- };
10
- this.mouseUpHandler = (event) => {
11
- this.preventShow = false;
12
- this.update(this.editor.view);
13
- };
14
- this.focusHandler = () => {
15
- // this.editor.commands.setTextSelection({ from: 0, to: 0 });
16
- // we use `setTimeout` to make sure `selection` is already updated
17
- setTimeout(() => this.update(this.editor.view));
18
- };
19
- this.blurHandler = ({ event }) => {
20
- var _a;
21
- if (this.preventShow) {
22
- this.preventShow = false;
23
- return;
24
- }
25
- if ((event === null || event === void 0 ? void 0 : event.relatedTarget) &&
26
- ((_a = this.floating) === null || _a === void 0 ? void 0 : _a.contains(event === null || event === void 0 ? void 0 : event.relatedTarget))) {
27
- return;
28
- }
29
- // clear text selection
30
- // this.editor.commands.setTextSelection({ from: 0, to: 0 });
31
- this.hide();
32
- };
33
- this.editor = editor;
34
- this.view = view;
35
- this.rect = null;
36
- this.preventShow = false;
37
- this.floating = null;
38
- this.isOpen = false;
39
- // create and mount react component
40
- if (!this.component) {
41
- this.component = new ReactRenderer(BubbleMenuComponent, {
42
- props: {
43
- editor: this.editor,
44
- open: false,
45
- onOpen: (ref) => {
46
- this.floating = ref;
47
- },
48
- customMenuItems,
49
- },
50
- editor: this.editor,
51
- });
52
- document.body.appendChild(this.component.element);
53
- }
54
- // don't show the bubble during selection of text
55
- this.view.dom.addEventListener("mousedown", this.mousedownHandler, {
56
- capture: true,
57
- });
58
- this.view.dom.addEventListener("mouseup", this.mouseUpHandler);
59
- this.editor.on("blur", this.blurHandler);
60
- this.editor.on("focus", this.focusHandler);
61
- }
62
- update(view, oldState) {
63
- var _a;
64
- const { state, composing } = view;
65
- const { doc, selection } = state;
66
- const { empty, ranges } = selection;
67
- const from = Math.min(...ranges.map((range) => range.$from.pos));
68
- const to = Math.max(...ranges.map((range) => range.$to.pos));
69
- const selectionChanged = !(oldState === null || oldState === void 0 ? void 0 : oldState.selection.eq(view.state.selection));
70
- const docChanged = !(oldState === null || oldState === void 0 ? void 0 : oldState.doc.eq(view.state.doc));
71
- const isSame = !selectionChanged && !docChanged;
72
- if (composing || isSame) {
73
- return;
74
- }
75
- // Sometime check for `empty` is not enough.
76
- // Doubleclick an empty paragraph returns a node size of 2.
77
- // So we check also for an empty text size.
78
- const isEmptyTextBlock = !doc.textBetween(from, to).length && isTextSelection(state.selection);
79
- // When clicking on a element inside the bubble menu the editor "blur" event
80
- // is called and the bubble menu item is focussed. In this case we should
81
- // consider the menu as part of the editor and keep showing the menu
82
- const isChildOfMenu = (_a = this === null || this === void 0 ? void 0 : this.floating) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement);
83
- const hasEditorFocus = view.hasFocus() || isChildOfMenu;
84
- if (!hasEditorFocus ||
85
- empty ||
86
- isEmptyTextBlock ||
87
- !this.editor.isEditable ||
88
- this.preventShow) {
89
- this.hide();
90
- }
91
- else {
92
- // only set position when it is not already open
93
- // otherwise the menu will jump around when the selection changes or text formatting is applied
94
- if (!this.isOpen) {
95
- if (isNodeSelection(state.selection)) {
96
- let node = view.nodeDOM(from);
97
- const nodeViewWrapper = node.dataset.nodeViewWrapper
98
- ? node
99
- : node.querySelector("[data-node-view-wrapper]");
100
- if (nodeViewWrapper) {
101
- node = nodeViewWrapper.firstChild;
102
- }
103
- if (node) {
104
- this.rect = node.getBoundingClientRect();
105
- }
106
- }
107
- else {
108
- this.rect = posToDOMRect(view, from, to);
109
- }
110
- }
111
- this.show();
112
- }
113
- }
114
- show() {
115
- if (this.component) {
116
- this.component.updateProps({ open: true, rect: this.rect });
117
- this.isOpen = true;
118
- }
119
- }
120
- hide() {
121
- if (this.component) {
122
- this.component.updateProps({ open: false, rect: null });
123
- this.isOpen = false;
124
- }
125
- }
126
- destroy() {
127
- if (this.component) {
128
- this.view.dom.removeEventListener("mousedown", this.mousedownHandler);
129
- this.view.dom.removeEventListener("mouseup", this.mouseUpHandler);
130
- this.component.destroy();
131
- }
132
- }
133
- }
134
- const BubbleMenu = Extension.create({
135
- name: "bubbleMenu",
136
- addOptions() {
137
- return {
138
- bubbleMenuOptions: {},
139
- };
140
- },
141
- addProseMirrorPlugins() {
142
- return [
143
- new Plugin({
144
- key: new PluginKey("bubbleMenu"),
145
- view: (view) => {
146
- var _a;
147
- return new Menu({
148
- view,
149
- editor: this.editor,
150
- customMenuItems: ((_a = this === null || this === void 0 ? void 0 : this.options) === null || _a === void 0 ? void 0 : _a.customMenuItems) || [],
151
- });
152
- },
153
- }),
154
- ];
155
- },
156
- });
157
- export default BubbleMenu;