@monolith-forensics/monolith-ui 1.8.1-dev.2 → 1.8.1-dev.4

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 (99) hide show
  1. package/dist/Calendar/Calendar.d.ts +4 -1
  2. package/dist/Calendar/Calendar.js +126 -50
  3. package/dist/Calendar/CalendarStyles.d.ts +3 -2
  4. package/dist/Calendar/CalendarStyles.js +62 -23
  5. package/dist/Calendar/calendarHelpers.d.ts +1 -1
  6. package/dist/CheckBox/CheckBox.js +35 -4
  7. package/dist/DropDownMenu/DropDownMenu.js +25 -15
  8. package/dist/DropDownMenu/components/MenuComponent.js +6 -1
  9. package/dist/DropDownMenu/components/MenuItem.js +6 -0
  10. package/dist/DropDownMenu/components/MenuItemList.d.ts +2 -0
  11. package/dist/DropDownMenu/components/MenuItemList.js +94 -13
  12. package/dist/DropDownMenu/components/StyledContent.js +1 -2
  13. package/dist/DropDownMenu/types.d.ts +1 -0
  14. package/dist/FileViewer/viewers/ImageViewer.js +75 -18
  15. package/dist/MonolithUIProvider/MonolithUIProvider.d.ts +1 -0
  16. package/dist/QueryFilter/QueryFilter.constants.d.ts +134 -0
  17. package/dist/QueryFilter/QueryFilter.constants.js +39 -0
  18. package/dist/QueryFilter/QueryFilter.d.ts +1 -1
  19. package/dist/QueryFilter/QueryFilter.js +3 -303
  20. package/dist/QueryFilter/QueryFilter.lib.d.ts +14 -0
  21. package/dist/QueryFilter/QueryFilter.lib.js +84 -0
  22. package/dist/QueryFilter/QueryFilter.styled.d.ts +89 -0
  23. package/dist/QueryFilter/QueryFilter.styled.js +184 -0
  24. package/dist/QueryFilter/{types.d.ts → QueryFilter.types.d.ts} +11 -2
  25. package/dist/QueryFilter/components/MultiSelectEditor.d.ts +7 -0
  26. package/dist/QueryFilter/components/MultiSelectEditor.js +44 -0
  27. package/dist/QueryFilter/components/RuleChip.d.ts +8 -0
  28. package/dist/QueryFilter/components/RuleChip.js +37 -0
  29. package/dist/QueryFilter/components/Rules.d.ts +11 -0
  30. package/dist/QueryFilter/components/Rules.js +41 -0
  31. package/dist/QueryFilter/components/UnavailableRuleChip.d.ts +5 -0
  32. package/dist/QueryFilter/components/UnavailableRuleChip.js +7 -0
  33. package/dist/QueryFilter/components/ValueEditor.d.ts +7 -0
  34. package/dist/QueryFilter/components/ValueEditor.js +45 -0
  35. package/dist/QueryFilter/components/ValueSelector.d.ts +7 -0
  36. package/dist/QueryFilter/components/ValueSelector.js +34 -0
  37. package/dist/QueryFilter/components/index.d.ts +6 -0
  38. package/dist/QueryFilter/components/index.js +6 -0
  39. package/dist/QueryFilter/index.d.ts +3 -2
  40. package/dist/QueryFilter/index.js +3 -2
  41. package/dist/QueryFilter/useQueryFilter.d.ts +1 -1
  42. package/dist/QueryFilter/useQueryFilter.js +23 -19
  43. package/dist/RichTextEditor/Extensions/getTiptapExtensions.js +5 -15
  44. package/dist/RichTextEditor/Plugins/ImageActionsPlugin.d.ts +3 -0
  45. package/dist/RichTextEditor/Plugins/ImageActionsPlugin.js +241 -0
  46. package/dist/RichTextEditor/Plugins/UploadImagesPlugin.d.ts +15 -0
  47. package/dist/RichTextEditor/Plugins/UploadImagesPlugin.js +115 -51
  48. package/dist/RichTextEditor/Plugins/index.d.ts +1 -0
  49. package/dist/RichTextEditor/Plugins/index.js +1 -0
  50. package/dist/RichTextEditor/RichTextEditor.d.ts +3 -2
  51. package/dist/RichTextEditor/RichTextEditor.js +309 -35
  52. package/dist/RichTextEditor/Toolbar/Toolbar.js +14 -2
  53. package/dist/SuperDatePicker/SuperDatePicker.constants.d.ts +24 -0
  54. package/dist/SuperDatePicker/SuperDatePicker.constants.js +30 -0
  55. package/dist/SuperDatePicker/SuperDatePicker.d.ts +3 -0
  56. package/dist/SuperDatePicker/SuperDatePicker.js +31 -0
  57. package/dist/SuperDatePicker/SuperDatePicker.lib.d.ts +17 -0
  58. package/dist/SuperDatePicker/SuperDatePicker.lib.js +206 -0
  59. package/dist/SuperDatePicker/SuperDatePicker.styled.d.ts +60 -0
  60. package/dist/SuperDatePicker/SuperDatePicker.styled.js +256 -0
  61. package/dist/SuperDatePicker/SuperDatePicker.types.d.ts +48 -0
  62. package/dist/SuperDatePicker/SuperDatePicker.types.js +1 -0
  63. package/dist/SuperDatePicker/components/CommonPresetGroups.d.ts +7 -0
  64. package/dist/SuperDatePicker/components/CommonPresetGroups.js +6 -0
  65. package/dist/SuperDatePicker/components/EndpointCalendarDropdown.d.ts +10 -0
  66. package/dist/SuperDatePicker/components/EndpointCalendarDropdown.js +9 -0
  67. package/dist/SuperDatePicker/components/EndpointDateInput.d.ts +16 -0
  68. package/dist/SuperDatePicker/components/EndpointDateInput.js +26 -0
  69. package/dist/SuperDatePicker/components/EndpointPopover.d.ts +18 -0
  70. package/dist/SuperDatePicker/components/EndpointPopover.js +11 -0
  71. package/dist/SuperDatePicker/components/QuickRangeDropdown.d.ts +14 -0
  72. package/dist/SuperDatePicker/components/QuickRangeDropdown.js +19 -0
  73. package/dist/SuperDatePicker/components/QuickRangePopover.d.ts +18 -0
  74. package/dist/SuperDatePicker/components/QuickRangePopover.js +12 -0
  75. package/dist/SuperDatePicker/components/index.d.ts +6 -0
  76. package/dist/SuperDatePicker/components/index.js +6 -0
  77. package/dist/SuperDatePicker/index.d.ts +3 -0
  78. package/dist/SuperDatePicker/index.js +3 -0
  79. package/dist/SuperDatePicker/useSuperDatePicker.d.ts +32 -0
  80. package/dist/SuperDatePicker/useSuperDatePicker.js +125 -0
  81. package/dist/Table/ColumnResizer.d.ts +6 -9
  82. package/dist/Table/ColumnResizer.js +30 -10
  83. package/dist/Table/TableComponents.js +14 -10
  84. package/dist/Table/TableHeader.js +31 -16
  85. package/dist/Table/TableMenu/TableMenu.js +1 -1
  86. package/dist/Table/TableProvider.js +91 -4
  87. package/dist/Table/TableRow.js +19 -12
  88. package/dist/Table/Utils/index.d.ts +0 -1
  89. package/dist/Table/Utils/index.js +0 -1
  90. package/dist/Table/types.d.ts +6 -19
  91. package/dist/index.d.ts +2 -0
  92. package/dist/index.js +1 -0
  93. package/dist/theme/variants.js +2 -0
  94. package/package.json +22 -18
  95. package/dist/QueryFilter/DefaultOperators.d.ts +0 -76
  96. package/dist/QueryFilter/DefaultOperators.js +0 -21
  97. package/dist/Table/Utils/resizeHandler.d.ts +0 -3
  98. package/dist/Table/Utils/resizeHandler.js +0 -84
  99. /package/dist/QueryFilter/{types.js → QueryFilter.types.js} +0 -0
@@ -0,0 +1,241 @@
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, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
11
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
12
+ import { ReactRenderer } from "@tiptap/react";
13
+ import { CopyIcon, DownloadIcon, FullscreenIcon } from "lucide-react";
14
+ import styled from "styled-components";
15
+ import { Button } from "../../Button";
16
+ import { FileViewer } from "../../FileViewer";
17
+ import { useState } from "react";
18
+ const imageActionsKey = new PluginKey("image-actions");
19
+ const ImageActionsMenuRoot = styled.div `
20
+ display: flex;
21
+ gap: 4px;
22
+ padding: 4px;
23
+ border-radius: 5px;
24
+
25
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);
26
+
27
+ button {
28
+ width: 24px;
29
+ padding: 0;
30
+ }
31
+ `;
32
+ const ImageActionsMenu = ({ onCopy, onDownload, getFile, onMouseEnter, onMouseLeave, }) => {
33
+ const [viewerFile, setViewerFile] = useState(null);
34
+ const keepEditorFocus = (event) => {
35
+ event.preventDefault();
36
+ };
37
+ return (_jsxs(_Fragment, { children: [_jsxs(ImageActionsMenuRoot, { onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: [_jsx(Button, { type: "button", size: "xxs", variant: "outlined", "aria-label": "Copy image", title: "Copy image", onMouseDown: keepEditorFocus, onClick: onCopy, children: _jsx(CopyIcon, { size: 14 }) }), _jsx(Button, { type: "button", size: "xxs", variant: "outlined", "aria-label": "Download image", title: "Download image", onMouseDown: keepEditorFocus, onClick: onDownload, children: _jsx(DownloadIcon, { size: 14 }) }), _jsx(Button, { type: "button", size: "xxs", variant: "outlined", "aria-label": "View image fullscreen", title: "View image fullscreen", onMouseDown: keepEditorFocus, onClick: () => {
38
+ const file = getFile();
39
+ if (file) {
40
+ setViewerFile(file);
41
+ }
42
+ }, children: _jsx(FullscreenIcon, { size: 14 }) })] }), _jsx(FileViewer, { file: viewerFile || undefined, open: Boolean(viewerFile), onClose: () => setViewerFile(null) })] }));
43
+ };
44
+ const getImageFilename = (image) => {
45
+ var _a, _b;
46
+ const fromTitle = (_b = (_a = image.title) === null || _a === void 0 ? void 0 : _a.match(/Filename:\s*(.+)$/)) === null || _b === void 0 ? void 0 : _b[1];
47
+ if (fromTitle)
48
+ return fromTitle;
49
+ if (image.alt)
50
+ return image.alt;
51
+ try {
52
+ const url = new URL(image.src);
53
+ const pathname = url.pathname.split("/").filter(Boolean).pop();
54
+ if (pathname)
55
+ return pathname;
56
+ }
57
+ catch (_c) {
58
+ // Data URLs and blob URLs are fine; fall back to a generated filename.
59
+ }
60
+ return "image.png";
61
+ };
62
+ const getImageBlob = (src) => __awaiter(void 0, void 0, void 0, function* () {
63
+ const response = yield fetch(src);
64
+ if (!response.ok) {
65
+ throw new Error("Unable to load image.");
66
+ }
67
+ return response.blob();
68
+ });
69
+ const copyImage = (image) => __awaiter(void 0, void 0, void 0, function* () {
70
+ var _a;
71
+ const ClipboardItemCtor = window.ClipboardItem;
72
+ if (!((_a = navigator.clipboard) === null || _a === void 0 ? void 0 : _a.write) || !ClipboardItemCtor) {
73
+ throw new Error("Image copying is not supported by this browser.");
74
+ }
75
+ const blob = yield getImageBlob(image.src);
76
+ const type = blob.type || "image/png";
77
+ yield navigator.clipboard.write([
78
+ new ClipboardItemCtor({
79
+ [type]: blob,
80
+ }),
81
+ ]);
82
+ });
83
+ const downloadImage = (image) => __awaiter(void 0, void 0, void 0, function* () {
84
+ const filename = getImageFilename(image);
85
+ let href = image.src;
86
+ let objectUrl = null;
87
+ try {
88
+ const blob = yield getImageBlob(image.src);
89
+ objectUrl = URL.createObjectURL(blob);
90
+ href = objectUrl;
91
+ }
92
+ catch (_a) {
93
+ // Cross-origin images may not be fetchable, but the browser can still try
94
+ // the original URL as the download target.
95
+ }
96
+ const link = document.createElement("a");
97
+ link.href = href;
98
+ link.download = filename;
99
+ document.body.appendChild(link);
100
+ link.click();
101
+ link.remove();
102
+ if (objectUrl) {
103
+ URL.revokeObjectURL(objectUrl);
104
+ }
105
+ });
106
+ const getImageViewerFile = (image) => {
107
+ var _a;
108
+ const name = getImageFilename(image);
109
+ const ext = ((_a = name.split(".").pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || "png";
110
+ const url = image.currentSrc || image.src;
111
+ return {
112
+ name,
113
+ ext,
114
+ type: "image",
115
+ url,
116
+ };
117
+ };
118
+ class ImageActionsView {
119
+ constructor(view, editor) {
120
+ this.component = null;
121
+ this.toolbar = null;
122
+ this.activeImage = null;
123
+ this.hideTimer = null;
124
+ this.handleMouseOver = (event) => {
125
+ var _a;
126
+ const target = event.target;
127
+ const image = (_a = target === null || target === void 0 ? void 0 : target.closest) === null || _a === void 0 ? void 0 : _a.call(target, "img");
128
+ if (!(image instanceof HTMLImageElement) ||
129
+ image.classList.contains("uploading") ||
130
+ !this.view.dom.contains(image)) {
131
+ return;
132
+ }
133
+ this.activeImage = image;
134
+ this.cancelHide();
135
+ this.show();
136
+ };
137
+ this.handleMouseOut = (event) => {
138
+ var _a, _b;
139
+ const relatedTarget = event.relatedTarget;
140
+ if (relatedTarget &&
141
+ (((_a = this.toolbar) === null || _a === void 0 ? void 0 : _a.contains(relatedTarget)) ||
142
+ ((_b = this.activeImage) === null || _b === void 0 ? void 0 : _b.contains(relatedTarget)))) {
143
+ return;
144
+ }
145
+ this.scheduleHide();
146
+ };
147
+ this.handleCopy = () => __awaiter(this, void 0, void 0, function* () {
148
+ if (!this.activeImage)
149
+ return;
150
+ try {
151
+ yield copyImage(this.activeImage);
152
+ }
153
+ catch (error) {
154
+ alert(error.message || "Unable to copy image.");
155
+ }
156
+ });
157
+ this.handleDownload = () => __awaiter(this, void 0, void 0, function* () {
158
+ if (!this.activeImage)
159
+ return;
160
+ yield downloadImage(this.activeImage);
161
+ });
162
+ this.getFile = () => {
163
+ if (!this.activeImage)
164
+ return null;
165
+ return getImageViewerFile(this.activeImage);
166
+ };
167
+ this.hide = () => {
168
+ if (this.toolbar) {
169
+ this.toolbar.style.display = "none";
170
+ }
171
+ this.activeImage = null;
172
+ };
173
+ this.cancelHide = () => {
174
+ if (!this.hideTimer)
175
+ return;
176
+ clearTimeout(this.hideTimer);
177
+ this.hideTimer = null;
178
+ };
179
+ this.scheduleHide = () => {
180
+ this.cancelHide();
181
+ this.hideTimer = setTimeout(this.hide, 120);
182
+ };
183
+ this.updatePosition = () => {
184
+ if (!this.activeImage ||
185
+ !this.toolbar ||
186
+ this.toolbar.style.display === "none")
187
+ return;
188
+ const rect = this.activeImage.getBoundingClientRect();
189
+ const top = Math.max(8, rect.top + 8);
190
+ const left = Math.min(window.innerWidth - this.toolbar.offsetWidth - 8, Math.max(8, rect.right - this.toolbar.offsetWidth - 8));
191
+ this.toolbar.style.top = `${top}px`;
192
+ this.toolbar.style.left = `${left}px`;
193
+ };
194
+ this.view = view;
195
+ this.editor = editor;
196
+ this.view.dom.addEventListener("mouseover", this.handleMouseOver);
197
+ this.view.dom.addEventListener("mouseout", this.handleMouseOut);
198
+ window.addEventListener("scroll", this.updatePosition, true);
199
+ window.addEventListener("resize", this.updatePosition);
200
+ }
201
+ ensureToolbar() {
202
+ if (this.toolbar)
203
+ return this.toolbar;
204
+ this.component = new ReactRenderer(ImageActionsMenu, {
205
+ editor: this.editor,
206
+ props: {
207
+ onCopy: this.handleCopy,
208
+ onDownload: this.handleDownload,
209
+ getFile: this.getFile,
210
+ onMouseEnter: this.cancelHide,
211
+ onMouseLeave: this.scheduleHide,
212
+ },
213
+ className: "monolith-image-actions",
214
+ });
215
+ this.toolbar = this.component.element;
216
+ this.toolbar.style.position = "fixed";
217
+ this.toolbar.style.display = "none";
218
+ this.toolbar.style.zIndex = "16000";
219
+ this.view.dom.ownerDocument.body.appendChild(this.toolbar);
220
+ return this.toolbar;
221
+ }
222
+ show() {
223
+ const toolbar = this.ensureToolbar();
224
+ toolbar.style.display = "block";
225
+ this.updatePosition();
226
+ }
227
+ destroy() {
228
+ var _a, _b;
229
+ this.cancelHide();
230
+ this.view.dom.removeEventListener("mouseover", this.handleMouseOver);
231
+ this.view.dom.removeEventListener("mouseout", this.handleMouseOut);
232
+ window.removeEventListener("scroll", this.updatePosition, true);
233
+ window.removeEventListener("resize", this.updatePosition);
234
+ (_a = this.toolbar) === null || _a === void 0 ? void 0 : _a.remove();
235
+ (_b = this.component) === null || _b === void 0 ? void 0 : _b.destroy();
236
+ }
237
+ }
238
+ export const ImageActionsPlugin = (editor) => new Plugin({
239
+ key: imageActionsKey,
240
+ view: (view) => new ImageActionsView(view, editor),
241
+ });
@@ -1,5 +1,13 @@
1
1
  import { Plugin } from "@tiptap/pm/state";
2
2
  import { DecorationSet, EditorView } from "@tiptap/pm/view";
3
+ export declare const addImagePlaceholder: ({ view, id, pos, src, label, }: {
4
+ view: EditorView;
5
+ id: string;
6
+ pos: number;
7
+ src?: string;
8
+ label?: string;
9
+ }) => void;
10
+ export declare const removeImagePlaceholder: (view: EditorView, id: string) => any;
3
11
  export type HandleImageUploadProps = {
4
12
  file: File;
5
13
  name: string;
@@ -11,5 +19,12 @@ export type HandleImageUploadProps = {
11
19
  export type HandleImageUpload = {
12
20
  (props: HandleImageUploadProps): Promise<string> | undefined;
13
21
  };
22
+ export type HandleImageUrlUploadProps = {
23
+ src: string;
24
+ name: string;
25
+ };
26
+ export type HandleImageUrlUpload = {
27
+ (props: HandleImageUrlUploadProps): Promise<string | undefined> | string | undefined;
28
+ };
14
29
  export declare const startImageUpload: (file: File, view: EditorView, pos: number, handleImageUpload: HandleImageUpload | undefined) => void;
15
30
  export declare const UploadImagesPlugin: () => Plugin<DecorationSet>;
@@ -1,3 +1,12 @@
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
+ };
1
10
  import { Plugin, PluginKey } from "@tiptap/pm/state";
2
11
  import { Decoration, DecorationSet } from "@tiptap/pm/view";
3
12
  import { nanoid } from "nanoid";
@@ -5,10 +14,51 @@ import calculateFileHash from "../../Utilities/calculateFileHash";
5
14
  const uploadKey = new PluginKey("upload-image");
6
15
  function findPlaceholder(state, id) {
7
16
  const decos = uploadKey.getState(state);
17
+ if (!decos)
18
+ return null;
8
19
  const found = decos.find(null, null, (spec) => spec.id === id);
9
20
  return found.length ? found[0].from : null;
10
21
  }
22
+ const readFileAsDataUrl = (file) => {
23
+ return new Promise((resolve, reject) => {
24
+ const reader = new FileReader();
25
+ reader.addEventListener("load", () => {
26
+ resolve(reader.result);
27
+ });
28
+ reader.addEventListener("error", () => {
29
+ reject(reader.error || new Error("Unable to read image file."));
30
+ });
31
+ reader.addEventListener("abort", () => {
32
+ reject(new Error("Image file read was aborted."));
33
+ });
34
+ reader.readAsDataURL(file);
35
+ });
36
+ };
37
+ export const addImagePlaceholder = ({ view, id, pos, src, label = "Processing image...", }) => {
38
+ if (!uploadKey.getState(view.state))
39
+ return;
40
+ view.dispatch(view.state.tr.setMeta(uploadKey, {
41
+ add: {
42
+ id,
43
+ pos,
44
+ src,
45
+ label,
46
+ },
47
+ }));
48
+ };
49
+ export const removeImagePlaceholder = (view, id) => {
50
+ if (!uploadKey.getState(view.state))
51
+ return;
52
+ const position = findPlaceholder(view.state, id);
53
+ view.dispatch(view.state.tr.setMeta(uploadKey, { remove: { id } }));
54
+ return position;
55
+ };
11
56
  export const startImageUpload = (file, view, pos, handleImageUpload) => {
57
+ if (!handleImageUpload ||
58
+ !view.state.schema.nodes.image ||
59
+ !uploadKey.getState(view.state)) {
60
+ return;
61
+ }
12
62
  // check if the file is an image
13
63
  if (!file.type.includes("image/")) {
14
64
  alert("File type not supported.");
@@ -22,59 +72,61 @@ export const startImageUpload = (file, view, pos, handleImageUpload) => {
22
72
  // A fresh object to act as the ID for this upload
23
73
  const id = nanoid(25);
24
74
  // Replace the selection with a placeholder
25
- const tr = view.state.tr;
26
- if (!tr.selection.empty)
27
- tr.deleteSelection();
28
- const reader = new FileReader();
29
- reader.readAsDataURL(file);
30
- reader.onload = () => {
31
- tr.setMeta(uploadKey, {
32
- add: {
75
+ (() => __awaiter(void 0, void 0, void 0, function* () {
76
+ let placeholderAdded = false;
77
+ try {
78
+ const previewSrc = yield readFileAsDataUrl(file);
79
+ const transaction = view.state.tr;
80
+ if (!transaction.selection.empty) {
81
+ transaction.deleteSelection();
82
+ }
83
+ transaction.setMeta(uploadKey, {
84
+ add: {
85
+ id,
86
+ pos,
87
+ src: previewSrc,
88
+ },
89
+ });
90
+ view.dispatch(transaction);
91
+ placeholderAdded = true;
92
+ const hashes = yield calculateFileHash(file);
93
+ const src = yield handleImageUpload({
94
+ file,
95
+ name: `${id}.png`,
33
96
  id,
34
- pos,
35
- src: reader.result,
36
- },
37
- });
38
- view.dispatch(tr);
39
- };
40
- calculateFileHash(file).then((hashes) => {
41
- var _a;
42
- const md5 = hashes.md5Hash;
43
- const sha1 = hashes.sha1Hash;
44
- const sha256 = hashes.sha256Hash;
45
- (_a = handleImageUpload === null || handleImageUpload === void 0 ? void 0 : handleImageUpload({
46
- file,
47
- name: `${id}.png`,
48
- id,
49
- md5,
50
- sha1,
51
- sha256,
52
- })) === null || _a === void 0 ? void 0 : _a.then((src) => {
53
- if (!src)
54
- return;
55
- const { schema } = view.state;
56
- let pos = findPlaceholder(view.state, id);
57
- // If the content around the placeholder has been deleted, drop
58
- // the image
59
- if (pos == null)
97
+ md5: hashes.md5Hash,
98
+ sha1: hashes.sha1Hash,
99
+ sha256: hashes.sha256Hash,
100
+ });
101
+ if (!src) {
102
+ throw new Error("Image upload did not return a source URL.");
103
+ }
104
+ const placeholderPos = findPlaceholder(view.state, id);
105
+ // If the content around the placeholder has been deleted, drop the image.
106
+ if (placeholderPos == null) {
107
+ removeImagePlaceholder(view, id);
60
108
  return;
61
- // Otherwise, insert it at the placeholder's position, and remove
62
- // the placeholder
63
- // When BLOB_READ_WRITE_TOKEN is not valid or unavailable, read
64
- // the image locally
65
- const imageSrc = typeof src === "object" ? reader.result : src;
66
- const node = schema.nodes.image.create({
109
+ }
110
+ // When BLOB_READ_WRITE_TOKEN is not valid or unavailable, read the image locally.
111
+ const imageSrc = typeof src === "object" ? previewSrc : src;
112
+ const node = view.state.schema.nodes.image.create({
67
113
  src: imageSrc,
68
114
  alt: `${id}.png`,
69
115
  "data-uuid": id,
70
116
  title: `Filename: ${id}.png`,
71
117
  });
72
- const transaction = view.state.tr
73
- .replaceWith(pos, pos, node)
118
+ const insertTransaction = view.state.tr
119
+ .replaceWith(placeholderPos, placeholderPos, node)
74
120
  .setMeta(uploadKey, { remove: { id } });
75
- view.dispatch(transaction);
76
- });
77
- });
121
+ view.dispatch(insertTransaction);
122
+ }
123
+ catch (error) {
124
+ if (placeholderAdded) {
125
+ removeImagePlaceholder(view, id);
126
+ }
127
+ console.error("Image upload failed.", error);
128
+ }
129
+ }))();
78
130
  };
79
131
  export const UploadImagesPlugin = () => new Plugin({
80
132
  key: uploadKey,
@@ -87,13 +139,25 @@ export const UploadImagesPlugin = () => new Plugin({
87
139
  // See if the transaction adds or removes any placeholders
88
140
  const action = tr.getMeta(this);
89
141
  if (action && action.add) {
90
- const { id, pos, src } = action.add;
142
+ const { id, pos, src, label } = action.add;
91
143
  const placeholder = document.createElement("div");
92
- placeholder.setAttribute("class", "img-placeholder");
93
- const image = document.createElement("img");
94
- image.setAttribute("class", "monolith-image uploading");
95
- image.src = src;
96
- placeholder.appendChild(image);
144
+ placeholder.setAttribute("class", src ? "img-placeholder" : "img-placeholder processing");
145
+ if (src) {
146
+ const image = document.createElement("img");
147
+ image.setAttribute("class", "monolith-image uploading");
148
+ image.src = src;
149
+ placeholder.appendChild(image);
150
+ const text = document.createElement("span");
151
+ text.setAttribute("class", "img-placeholder-label");
152
+ text.textContent = label || "Uploading image...";
153
+ placeholder.appendChild(text);
154
+ }
155
+ else {
156
+ const text = document.createElement("span");
157
+ text.setAttribute("class", "img-placeholder-label");
158
+ text.textContent = label;
159
+ placeholder.appendChild(text);
160
+ }
97
161
  const deco = Decoration.widget(pos + 1, placeholder, {
98
162
  id,
99
163
  });
@@ -1 +1,2 @@
1
1
  export * from "./UploadImagesPlugin";
2
+ export * from "./ImageActionsPlugin";
@@ -1 +1,2 @@
1
1
  export * from "./UploadImagesPlugin";
2
+ export * from "./ImageActionsPlugin";
@@ -1,11 +1,11 @@
1
1
  import { Editor } from "@tiptap/react";
2
2
  import { ExtensionType } from "./Extensions/getTiptapExtensions";
3
- import { HandleImageUpload } from "./Plugins/UploadImagesPlugin";
3
+ import { HandleImageUrlUpload, HandleImageUpload } from "./Plugins/UploadImagesPlugin";
4
4
  import { BubbleMenuOptions } from "./Extensions/BubbleMenuExtension";
5
5
  import { ToolbarOptions } from "./Toolbar/Toolbar";
6
6
  type RichTextEditorProps = {
7
7
  className?: string;
8
- editorInstanceRef?: React.RefObject<Editor>;
8
+ editorInstanceRef?: React.RefObject<Editor | null>;
9
9
  extensions?: ExtensionType[];
10
10
  slashCommands?: any[];
11
11
  defaultValue?: string;
@@ -17,6 +17,7 @@ type RichTextEditorProps = {
17
17
  saving?: boolean;
18
18
  onChange?: (value: string) => void;
19
19
  handleImageUpload?: HandleImageUpload;
20
+ handleImageUrlUpload?: HandleImageUrlUpload;
20
21
  bubbleMenuOptions?: BubbleMenuOptions;
21
22
  toolbarOptions?: ToolbarOptions;
22
23
  autoFocus?: boolean;