@lumir-company/editor 0.2.0 → 0.3.3
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/README.md +742 -925
- package/dist/index.d.mts +17 -21
- package/dist/index.d.ts +17 -21
- package/dist/index.js +230 -183
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -183
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +77 -4
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/components/LumirEditor.tsx
|
|
4
|
-
import { useEffect, useMemo } from "react";
|
|
4
|
+
import { useEffect, useMemo, useCallback, useState } from "react";
|
|
5
5
|
import {
|
|
6
6
|
useCreateBlockNote,
|
|
7
7
|
SideMenu as BlockSideMenu,
|
|
@@ -17,6 +17,62 @@ function cn(...inputs) {
|
|
|
17
17
|
return inputs.filter(Boolean).join(" ");
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// src/utils/s3-uploader.ts
|
|
21
|
+
var createS3Uploader = (config) => {
|
|
22
|
+
const { apiEndpoint, env, path } = config;
|
|
23
|
+
if (!apiEndpoint || apiEndpoint.trim() === "") {
|
|
24
|
+
throw new Error(
|
|
25
|
+
"apiEndpoint is required for S3 upload. Please provide a valid API endpoint."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (!env) {
|
|
29
|
+
throw new Error("env is required. Must be 'development' or 'production'.");
|
|
30
|
+
}
|
|
31
|
+
if (!path || path.trim() === "") {
|
|
32
|
+
throw new Error("path is required and cannot be empty.");
|
|
33
|
+
}
|
|
34
|
+
const generateHierarchicalFileName = (file) => {
|
|
35
|
+
const now = /* @__PURE__ */ new Date();
|
|
36
|
+
const filename = file.name;
|
|
37
|
+
return `${env}/${path}/${filename}`;
|
|
38
|
+
};
|
|
39
|
+
return async (file) => {
|
|
40
|
+
try {
|
|
41
|
+
if (!apiEndpoint || apiEndpoint.trim() === "") {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"Invalid apiEndpoint: Cannot upload file without a valid API ENDPOINT"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
const fileName = generateHierarchicalFileName(file);
|
|
47
|
+
const response = await fetch(
|
|
48
|
+
`${apiEndpoint}?key=${encodeURIComponent(fileName)}`
|
|
49
|
+
);
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const errorText = await response.text() || "";
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Failed to get presigned URL: ${response.statusText}, ${errorText}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const responseData = await response.json();
|
|
57
|
+
const { presignedUrl, publicUrl } = responseData;
|
|
58
|
+
const uploadResponse = await fetch(presignedUrl, {
|
|
59
|
+
method: "PUT",
|
|
60
|
+
headers: {
|
|
61
|
+
"Content-Type": file.type || "application/octet-stream"
|
|
62
|
+
},
|
|
63
|
+
body: file
|
|
64
|
+
});
|
|
65
|
+
if (!uploadResponse.ok) {
|
|
66
|
+
throw new Error(`Failed to upload file: ${uploadResponse.statusText}`);
|
|
67
|
+
}
|
|
68
|
+
return publicUrl;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error("S3 upload failed:", error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
20
76
|
// src/components/LumirEditor.tsx
|
|
21
77
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
78
|
var ContentUtils = class {
|
|
@@ -69,39 +125,34 @@ var ContentUtils = class {
|
|
|
69
125
|
* 콘텐츠 유효성 검증 및 기본값 설정
|
|
70
126
|
* @param content 사용자 제공 콘텐츠 (객체 배열 또는 JSON 문자열)
|
|
71
127
|
* @param emptyBlockCount 빈 블록 개수 (기본값: 3)
|
|
72
|
-
* @param placeholder 첫 번째 블록의 placeholder 텍스트
|
|
73
128
|
* @returns 검증된 콘텐츠 배열
|
|
74
129
|
*/
|
|
75
|
-
static validateContent(content, emptyBlockCount = 3
|
|
130
|
+
static validateContent(content, emptyBlockCount = 3) {
|
|
76
131
|
if (typeof content === "string") {
|
|
77
132
|
if (content.trim() === "") {
|
|
78
|
-
return this.createEmptyBlocks(emptyBlockCount
|
|
133
|
+
return this.createEmptyBlocks(emptyBlockCount);
|
|
79
134
|
}
|
|
80
135
|
const parsedContent = this.parseJSONContent(content);
|
|
81
136
|
if (parsedContent && parsedContent.length > 0) {
|
|
82
137
|
return parsedContent;
|
|
83
138
|
}
|
|
84
|
-
return this.createEmptyBlocks(emptyBlockCount
|
|
139
|
+
return this.createEmptyBlocks(emptyBlockCount);
|
|
85
140
|
}
|
|
86
141
|
if (!content || content.length === 0) {
|
|
87
|
-
return this.createEmptyBlocks(emptyBlockCount
|
|
142
|
+
return this.createEmptyBlocks(emptyBlockCount);
|
|
88
143
|
}
|
|
89
144
|
return content;
|
|
90
145
|
}
|
|
91
146
|
/**
|
|
92
147
|
* 빈 블록들을 생성합니다
|
|
93
148
|
* @param emptyBlockCount 생성할 블록 개수
|
|
94
|
-
* @param placeholder 첫 번째 블록의 placeholder 텍스트
|
|
95
149
|
* @returns 생성된 빈 블록 배열
|
|
96
150
|
*/
|
|
97
|
-
static createEmptyBlocks(emptyBlockCount
|
|
98
|
-
return Array.from(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
return block;
|
|
104
|
-
});
|
|
151
|
+
static createEmptyBlocks(emptyBlockCount) {
|
|
152
|
+
return Array.from(
|
|
153
|
+
{ length: emptyBlockCount },
|
|
154
|
+
() => this.createDefaultBlock()
|
|
155
|
+
);
|
|
105
156
|
}
|
|
106
157
|
};
|
|
107
158
|
var EditorConfig = class {
|
|
@@ -131,41 +182,32 @@ var EditorConfig = class {
|
|
|
131
182
|
* @param userExtensions 사용자 정의 비활성 확장
|
|
132
183
|
* @param allowVideo 비디오 업로드 허용 여부
|
|
133
184
|
* @param allowAudio 오디오 업로드 허용 여부
|
|
185
|
+
* @param allowFile 일반 파일 업로드 허용 여부
|
|
134
186
|
* @returns 비활성화할 확장 기능 목록
|
|
135
187
|
*/
|
|
136
|
-
static getDisabledExtensions(userExtensions, allowVideo = false, allowAudio = false) {
|
|
188
|
+
static getDisabledExtensions(userExtensions, allowVideo = false, allowAudio = false, allowFile = false) {
|
|
137
189
|
const set = new Set(userExtensions ?? []);
|
|
138
190
|
if (!allowVideo) set.add("video");
|
|
139
191
|
if (!allowAudio) set.add("audio");
|
|
192
|
+
if (!allowFile) set.add("file");
|
|
140
193
|
return Array.from(set);
|
|
141
194
|
}
|
|
142
195
|
};
|
|
143
|
-
var
|
|
144
|
-
return
|
|
196
|
+
var isImageFile = (file) => {
|
|
197
|
+
return file.size > 0 && (file.type?.startsWith("image/") || !file.type && /\.(png|jpe?g|gif|webp|bmp|svg)$/i.test(file.name || ""));
|
|
145
198
|
};
|
|
146
|
-
var fileToBase64 = async (file) => await new Promise((resolve, reject) => {
|
|
147
|
-
const reader = new FileReader();
|
|
148
|
-
reader.onload = () => resolve(String(reader.result));
|
|
149
|
-
reader.onerror = () => reject(new Error("FileReader failed"));
|
|
150
|
-
reader.readAsDataURL(file);
|
|
151
|
-
});
|
|
152
199
|
function LumirEditor({
|
|
153
200
|
// editor options
|
|
154
201
|
initialContent,
|
|
155
202
|
initialEmptyBlocks = 3,
|
|
156
|
-
placeholder,
|
|
157
203
|
uploadFile,
|
|
158
|
-
|
|
204
|
+
s3Upload,
|
|
159
205
|
tables,
|
|
160
206
|
heading,
|
|
161
|
-
animations = true,
|
|
162
207
|
defaultStyles = true,
|
|
163
208
|
disableExtensions,
|
|
164
|
-
domAttributes,
|
|
165
209
|
tabBehavior = "prefer-navigate-ui",
|
|
166
210
|
trailingBlock = true,
|
|
167
|
-
resolveFileUrl,
|
|
168
|
-
storeImagesAsBase64 = true,
|
|
169
211
|
allowVideoUpload = false,
|
|
170
212
|
allowAudioUpload = false,
|
|
171
213
|
allowFileUpload = false,
|
|
@@ -175,144 +217,135 @@ function LumirEditor({
|
|
|
175
217
|
formattingToolbar = true,
|
|
176
218
|
linkToolbar = true,
|
|
177
219
|
sideMenu = true,
|
|
178
|
-
slashMenu = true,
|
|
179
220
|
emojiPicker = true,
|
|
180
221
|
filePanel = true,
|
|
181
222
|
tableHandles = true,
|
|
182
|
-
comments = true,
|
|
183
223
|
onSelectionChange,
|
|
184
224
|
className = "",
|
|
185
|
-
|
|
186
|
-
sideMenuAddButton = true,
|
|
225
|
+
sideMenuAddButton = false,
|
|
187
226
|
// callbacks / refs
|
|
188
|
-
onContentChange
|
|
189
|
-
editorRef
|
|
227
|
+
onContentChange
|
|
190
228
|
}) {
|
|
229
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
191
230
|
const validatedContent = useMemo(() => {
|
|
192
|
-
return ContentUtils.validateContent(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
231
|
+
return ContentUtils.validateContent(initialContent, initialEmptyBlocks);
|
|
232
|
+
}, [initialContent, initialEmptyBlocks]);
|
|
233
|
+
const tableConfig = useMemo(() => {
|
|
234
|
+
return EditorConfig.getDefaultTableConfig(tables);
|
|
235
|
+
}, [
|
|
236
|
+
tables?.splitCells,
|
|
237
|
+
tables?.cellBackgroundColor,
|
|
238
|
+
tables?.cellTextColor,
|
|
239
|
+
tables?.headers
|
|
240
|
+
]);
|
|
241
|
+
const headingConfig = useMemo(() => {
|
|
242
|
+
return EditorConfig.getDefaultHeadingConfig(heading);
|
|
243
|
+
}, [heading?.levels?.join(",") ?? ""]);
|
|
244
|
+
const disabledExtensions = useMemo(() => {
|
|
245
|
+
return EditorConfig.getDisabledExtensions(
|
|
246
|
+
disableExtensions,
|
|
247
|
+
allowVideoUpload,
|
|
248
|
+
allowAudioUpload,
|
|
249
|
+
allowFileUpload
|
|
196
250
|
);
|
|
197
|
-
}, [
|
|
251
|
+
}, [disableExtensions, allowVideoUpload, allowAudioUpload, allowFileUpload]);
|
|
252
|
+
const memoizedS3Upload = useMemo(() => {
|
|
253
|
+
return s3Upload;
|
|
254
|
+
}, [s3Upload?.apiEndpoint, s3Upload?.env, s3Upload?.path]);
|
|
198
255
|
const editor = useCreateBlockNote(
|
|
199
256
|
{
|
|
200
257
|
initialContent: validatedContent,
|
|
201
|
-
tables:
|
|
202
|
-
heading:
|
|
203
|
-
animations,
|
|
258
|
+
tables: tableConfig,
|
|
259
|
+
heading: headingConfig,
|
|
260
|
+
animations: false,
|
|
261
|
+
// 기본적으로 애니메이션 비활성화
|
|
204
262
|
defaultStyles,
|
|
205
|
-
// 확장 비활성:
|
|
206
|
-
disableExtensions:
|
|
207
|
-
return EditorConfig.getDisabledExtensions(
|
|
208
|
-
disableExtensions,
|
|
209
|
-
allowVideoUpload,
|
|
210
|
-
allowAudioUpload
|
|
211
|
-
);
|
|
212
|
-
}, [disableExtensions, allowVideoUpload, allowAudioUpload]),
|
|
213
|
-
domAttributes,
|
|
263
|
+
// 확장 비활성: 비디오/오디오/파일 제어
|
|
264
|
+
disableExtensions: disabledExtensions,
|
|
214
265
|
tabBehavior,
|
|
215
266
|
trailingBlock,
|
|
216
|
-
resolveFileUrl,
|
|
217
267
|
uploadFile: async (file) => {
|
|
218
|
-
|
|
219
|
-
|
|
268
|
+
if (!isImageFile(file)) {
|
|
269
|
+
throw new Error("Only image files are allowed");
|
|
270
|
+
}
|
|
220
271
|
try {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
272
|
+
let imageUrl;
|
|
273
|
+
if (uploadFile) {
|
|
274
|
+
imageUrl = await uploadFile(file);
|
|
275
|
+
} else if (memoizedS3Upload?.apiEndpoint) {
|
|
276
|
+
const s3Uploader = createS3Uploader(memoizedS3Upload);
|
|
277
|
+
imageUrl = await s3Uploader(file);
|
|
278
|
+
} else {
|
|
279
|
+
throw new Error("No upload method available");
|
|
228
280
|
}
|
|
281
|
+
return imageUrl;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.error("Image upload failed:", error);
|
|
284
|
+
throw new Error(
|
|
285
|
+
"Upload failed: " + (error instanceof Error ? error.message : String(error))
|
|
286
|
+
);
|
|
229
287
|
}
|
|
230
288
|
},
|
|
231
289
|
pasteHandler: (ctx) => {
|
|
232
290
|
const { event, editor: editor2, defaultPasteHandler } = ctx;
|
|
233
291
|
const fileList = event?.clipboardData?.files ?? null;
|
|
234
292
|
const files = fileList ? Array.from(fileList) : [];
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
);
|
|
238
|
-
if (files.length > 0 && accepted.length === 0) {
|
|
293
|
+
const acceptedFiles = files.filter(isImageFile);
|
|
294
|
+
if (files.length > 0 && acceptedFiles.length === 0) {
|
|
239
295
|
event.preventDefault();
|
|
240
296
|
return true;
|
|
241
297
|
}
|
|
242
|
-
if (
|
|
298
|
+
if (acceptedFiles.length === 0) {
|
|
299
|
+
return defaultPasteHandler() ?? false;
|
|
300
|
+
}
|
|
243
301
|
event.preventDefault();
|
|
244
302
|
(async () => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
303
|
+
setIsUploading(true);
|
|
304
|
+
try {
|
|
305
|
+
for (const file of acceptedFiles) {
|
|
306
|
+
try {
|
|
307
|
+
const url = await editor2.uploadFile(file);
|
|
308
|
+
editor2.pasteHTML(`<img src="${url}" alt="image" />`);
|
|
309
|
+
} catch (err) {
|
|
310
|
+
console.warn(
|
|
311
|
+
"Image upload failed, skipped:",
|
|
312
|
+
file.name || "",
|
|
313
|
+
err
|
|
314
|
+
);
|
|
315
|
+
}
|
|
257
316
|
}
|
|
317
|
+
} finally {
|
|
318
|
+
setIsUploading(false);
|
|
258
319
|
}
|
|
259
320
|
})();
|
|
260
321
|
return true;
|
|
261
322
|
}
|
|
262
323
|
},
|
|
263
324
|
[
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
allowVideoUpload,
|
|
268
|
-
allowAudioUpload,
|
|
269
|
-
allowFileUpload,
|
|
270
|
-
tables?.splitCells,
|
|
271
|
-
tables?.cellBackgroundColor,
|
|
272
|
-
tables?.cellTextColor,
|
|
273
|
-
tables?.headers,
|
|
274
|
-
heading?.levels?.join(","),
|
|
275
|
-
animations,
|
|
325
|
+
validatedContent,
|
|
326
|
+
tableConfig,
|
|
327
|
+
headingConfig,
|
|
276
328
|
defaultStyles,
|
|
277
|
-
|
|
278
|
-
domAttributes ? JSON.stringify(domAttributes) : void 0,
|
|
329
|
+
disabledExtensions,
|
|
279
330
|
tabBehavior,
|
|
280
331
|
trailingBlock,
|
|
281
|
-
|
|
332
|
+
uploadFile,
|
|
333
|
+
memoizedS3Upload
|
|
282
334
|
]
|
|
283
335
|
);
|
|
284
336
|
useEffect(() => {
|
|
285
|
-
if (
|
|
286
|
-
|
|
287
|
-
const el = editor.domElement;
|
|
288
|
-
if (!editable) {
|
|
289
|
-
if (el) {
|
|
290
|
-
el.style.userSelect = "text";
|
|
291
|
-
el.style.webkitUserSelect = "text";
|
|
292
|
-
}
|
|
337
|
+
if (editor) {
|
|
338
|
+
editor.isEditable = editable;
|
|
293
339
|
}
|
|
294
340
|
}, [editor, editable]);
|
|
295
341
|
useEffect(() => {
|
|
296
342
|
if (!editor || !onContentChange) return;
|
|
297
|
-
let lastContent = "";
|
|
298
343
|
const handleContentChange = () => {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
if (lastContent === currentContent) return;
|
|
302
|
-
lastContent = currentContent;
|
|
303
|
-
onContentChange(topLevelBlocks);
|
|
304
|
-
};
|
|
305
|
-
editor.onEditorContentChange(handleContentChange);
|
|
306
|
-
return () => {
|
|
344
|
+
const blocks = editor.topLevelBlocks;
|
|
345
|
+
onContentChange(blocks);
|
|
307
346
|
};
|
|
347
|
+
return editor.onEditorContentChange(handleContentChange);
|
|
308
348
|
}, [editor, onContentChange]);
|
|
309
|
-
useEffect(() => {
|
|
310
|
-
if (!editorRef) return;
|
|
311
|
-
editorRef.current = editor ?? null;
|
|
312
|
-
return () => {
|
|
313
|
-
if (editorRef) editorRef.current = null;
|
|
314
|
-
};
|
|
315
|
-
}, [editor, editorRef]);
|
|
316
349
|
useEffect(() => {
|
|
317
350
|
const el = editor?.domElement;
|
|
318
351
|
if (!el) return;
|
|
@@ -322,9 +355,6 @@ function LumirEditor({
|
|
|
322
355
|
if (hasFiles) {
|
|
323
356
|
e.preventDefault();
|
|
324
357
|
e.stopPropagation();
|
|
325
|
-
if (typeof e.stopImmediatePropagation === "function") {
|
|
326
|
-
e.stopImmediatePropagation();
|
|
327
|
-
}
|
|
328
358
|
}
|
|
329
359
|
};
|
|
330
360
|
const handleDrop = (e) => {
|
|
@@ -333,23 +363,31 @@ function LumirEditor({
|
|
|
333
363
|
if (!hasFiles) return;
|
|
334
364
|
e.preventDefault();
|
|
335
365
|
e.stopPropagation();
|
|
336
|
-
e.stopImmediatePropagation?.();
|
|
337
366
|
const items = Array.from(e.dataTransfer.items ?? []);
|
|
338
367
|
const files = items.filter((it) => it.kind === "file").map((it) => it.getAsFile()).filter((f) => !!f);
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
);
|
|
342
|
-
if (accepted.length === 0) return;
|
|
368
|
+
const acceptedFiles = files.filter(isImageFile);
|
|
369
|
+
if (acceptedFiles.length === 0) return;
|
|
343
370
|
(async () => {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
371
|
+
setIsUploading(true);
|
|
372
|
+
try {
|
|
373
|
+
for (const file of acceptedFiles) {
|
|
374
|
+
try {
|
|
375
|
+
if (editor?.uploadFile) {
|
|
376
|
+
const url = await editor.uploadFile(file);
|
|
377
|
+
if (url) {
|
|
378
|
+
editor.pasteHTML(`<img src="${url}" alt="image" />`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} catch (err) {
|
|
382
|
+
console.warn(
|
|
383
|
+
"Image upload failed, skipped:",
|
|
384
|
+
file.name || "",
|
|
385
|
+
err
|
|
386
|
+
);
|
|
387
|
+
}
|
|
352
388
|
}
|
|
389
|
+
} finally {
|
|
390
|
+
setIsUploading(false);
|
|
353
391
|
}
|
|
354
392
|
})();
|
|
355
393
|
};
|
|
@@ -361,59 +399,66 @@ function LumirEditor({
|
|
|
361
399
|
});
|
|
362
400
|
el.removeEventListener("drop", handleDrop, { capture: true });
|
|
363
401
|
};
|
|
364
|
-
}, [
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
]);
|
|
371
|
-
const computedSideMenu = sideMenuAddButton ? sideMenu : false;
|
|
372
|
-
const DragHandleOnlySideMenu = (props) => {
|
|
373
|
-
return /* @__PURE__ */ jsx(BlockSideMenu, { ...props, children: /* @__PURE__ */ jsx(DragHandleButton, { ...props }) });
|
|
374
|
-
};
|
|
402
|
+
}, [editor]);
|
|
403
|
+
const computedSideMenu = useMemo(() => {
|
|
404
|
+
return sideMenuAddButton ? sideMenu : false;
|
|
405
|
+
}, [sideMenuAddButton, sideMenu]);
|
|
406
|
+
const DragHandleOnlySideMenu = useMemo(() => {
|
|
407
|
+
return (props) => /* @__PURE__ */ jsx(BlockSideMenu, { ...props, children: /* @__PURE__ */ jsx(DragHandleButton, { ...props }) });
|
|
408
|
+
}, []);
|
|
375
409
|
return /* @__PURE__ */ jsxs(
|
|
376
|
-
|
|
410
|
+
"div",
|
|
377
411
|
{
|
|
378
|
-
className: cn(
|
|
379
|
-
|
|
380
|
-
className
|
|
381
|
-
),
|
|
382
|
-
editor,
|
|
383
|
-
editable,
|
|
384
|
-
theme,
|
|
385
|
-
formattingToolbar,
|
|
386
|
-
linkToolbar,
|
|
387
|
-
sideMenu: computedSideMenu,
|
|
388
|
-
slashMenu: false,
|
|
389
|
-
emojiPicker,
|
|
390
|
-
filePanel,
|
|
391
|
-
tableHandles,
|
|
392
|
-
comments,
|
|
393
|
-
onSelectionChange,
|
|
412
|
+
className: cn("lumirEditor", className),
|
|
413
|
+
style: { position: "relative" },
|
|
394
414
|
children: [
|
|
395
|
-
/* @__PURE__ */
|
|
396
|
-
|
|
415
|
+
/* @__PURE__ */ jsxs(
|
|
416
|
+
BlockNoteView,
|
|
397
417
|
{
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
418
|
+
editor,
|
|
419
|
+
editable,
|
|
420
|
+
theme,
|
|
421
|
+
formattingToolbar,
|
|
422
|
+
linkToolbar,
|
|
423
|
+
sideMenu: computedSideMenu,
|
|
424
|
+
slashMenu: false,
|
|
425
|
+
emojiPicker,
|
|
426
|
+
filePanel,
|
|
427
|
+
tableHandles,
|
|
428
|
+
onSelectionChange,
|
|
429
|
+
children: [
|
|
430
|
+
/* @__PURE__ */ jsx(
|
|
431
|
+
SuggestionMenuController,
|
|
432
|
+
{
|
|
433
|
+
triggerCharacter: "/",
|
|
434
|
+
getItems: useCallback(
|
|
435
|
+
async (query) => {
|
|
436
|
+
const items = getDefaultReactSlashMenuItems(editor);
|
|
437
|
+
const filtered = items.filter((item) => {
|
|
438
|
+
const key = (item?.key || "").toString().toLowerCase();
|
|
439
|
+
const title = (item?.title || "").toString().toLowerCase();
|
|
440
|
+
if (["video", "audio", "file"].includes(key)) return false;
|
|
441
|
+
if (title.includes("video") || title.includes("audio") || title.includes("file"))
|
|
442
|
+
return false;
|
|
443
|
+
return true;
|
|
444
|
+
});
|
|
445
|
+
if (!query) return filtered;
|
|
446
|
+
const q = query.toLowerCase();
|
|
447
|
+
return filtered.filter(
|
|
448
|
+
(item) => item.title?.toLowerCase().includes(q) || (item.aliases || []).some(
|
|
449
|
+
(a) => a.toLowerCase().includes(q)
|
|
450
|
+
)
|
|
451
|
+
);
|
|
452
|
+
},
|
|
453
|
+
[editor]
|
|
454
|
+
)
|
|
455
|
+
}
|
|
456
|
+
),
|
|
457
|
+
!sideMenuAddButton && /* @__PURE__ */ jsx(SideMenuController, { sideMenu: DragHandleOnlySideMenu })
|
|
458
|
+
]
|
|
414
459
|
}
|
|
415
460
|
),
|
|
416
|
-
|
|
461
|
+
isUploading && /* @__PURE__ */ jsx("div", { className: "lumirEditor-upload-overlay", children: /* @__PURE__ */ jsx("div", { className: "lumirEditor-spinner" }) })
|
|
417
462
|
]
|
|
418
463
|
}
|
|
419
464
|
);
|
|
@@ -422,6 +467,7 @@ export {
|
|
|
422
467
|
ContentUtils,
|
|
423
468
|
EditorConfig,
|
|
424
469
|
LumirEditor,
|
|
425
|
-
cn
|
|
470
|
+
cn,
|
|
471
|
+
createS3Uploader
|
|
426
472
|
};
|
|
427
473
|
//# sourceMappingURL=index.mjs.map
|