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