@haklex/rich-ext-gallery 0.26.2 → 0.26.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GalleryEditRenderer.d.ts","sourceRoot":"","sources":["../src/GalleryEditRenderer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GalleryEditRenderer.d.ts","sourceRoot":"","sources":["../src/GalleryEditRenderer.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAA8C,EAAE,EAAE,MAAM,OAAO,CAAC;AAM5E,OAAO,KAAK,EAAgB,oBAAoB,EAAE,MAAM,SAAS,CAAC;AA0alE,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,oBAAoB,CA+DxD,CAAC"}
|
|
@@ -6,6 +6,7 @@ import { createElement, useCallback, useRef, useState } from "react";
|
|
|
6
6
|
import { DndContext, DragOverlay, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
|
|
7
7
|
import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
8
8
|
import { CSS } from "@dnd-kit/utilities";
|
|
9
|
+
import { useImageUpload } from "@haklex/rich-editor/plugins";
|
|
9
10
|
import { useColorScheme } from "@haklex/rich-editor/static";
|
|
10
11
|
import { SegmentedControl, presentDialog } from "@haklex/rich-editor-ui";
|
|
11
12
|
import { usePortalTheme, vars } from "@haklex/rich-style-token";
|
|
@@ -30,16 +31,71 @@ var nextId = 0;
|
|
|
30
31
|
function genId() {
|
|
31
32
|
return `img-${++nextId}`;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
function readAsDataUrl(file) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const reader = new FileReader();
|
|
37
|
+
reader.onload = () => resolve(String(reader.result));
|
|
38
|
+
reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("File read failed"));
|
|
39
|
+
reader.readAsDataURL(file);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async function fileToGalleryImage(file, uploader) {
|
|
43
|
+
if (uploader) {
|
|
44
|
+
const result = await uploader(file);
|
|
45
|
+
return {
|
|
46
|
+
src: result.src,
|
|
47
|
+
alt: result.altText ?? file.name
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
src: await readAsDataUrl(file),
|
|
52
|
+
alt: file.name
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function extractImageFiles(list) {
|
|
56
|
+
if (!list) return [];
|
|
57
|
+
const files = [];
|
|
58
|
+
for (let i = 0; i < list.length; i++) {
|
|
59
|
+
const f = list[i];
|
|
60
|
+
if (f && f.type.startsWith("image/")) files.push(f);
|
|
61
|
+
}
|
|
62
|
+
return files;
|
|
63
|
+
}
|
|
64
|
+
var SortableImageCard = ({ id, image, index, isLast, lastInputRef, onUpdate, onRemove, onDropFile }) => {
|
|
34
65
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
|
|
66
|
+
const [dropActive, setDropActive] = useState(false);
|
|
67
|
+
const style = {
|
|
68
|
+
transform: CSS.Transform.toString(transform),
|
|
69
|
+
transition: isDragging ? void 0 : transition,
|
|
70
|
+
opacity: isDragging ? .4 : 1,
|
|
71
|
+
outline: dropActive ? `2px dashed ${vars.color.accent}` : void 0,
|
|
72
|
+
outlineOffset: dropActive ? 2 : void 0
|
|
73
|
+
};
|
|
74
|
+
const handleDragOver = (e) => {
|
|
75
|
+
if (!e.dataTransfer?.types.includes("Files")) return;
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
e.dataTransfer.dropEffect = "copy";
|
|
78
|
+
if (!dropActive) setDropActive(true);
|
|
79
|
+
};
|
|
80
|
+
const handleDragLeave = (e) => {
|
|
81
|
+
if (e.currentTarget.contains(e.relatedTarget)) return;
|
|
82
|
+
setDropActive(false);
|
|
83
|
+
};
|
|
84
|
+
const handleDrop = (e) => {
|
|
85
|
+
const files = extractImageFiles(e.dataTransfer?.files);
|
|
86
|
+
setDropActive(false);
|
|
87
|
+
if (!files.length) return;
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
e.stopPropagation();
|
|
90
|
+
onDropFile(index, files[0]);
|
|
91
|
+
};
|
|
35
92
|
return /* @__PURE__ */ jsxs("div", {
|
|
36
93
|
className: galleryImageCard,
|
|
37
94
|
ref: setNodeRef,
|
|
38
|
-
style
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
},
|
|
95
|
+
style,
|
|
96
|
+
onDragLeave: handleDragLeave,
|
|
97
|
+
onDragOver: handleDragOver,
|
|
98
|
+
onDrop: handleDrop,
|
|
43
99
|
children: [
|
|
44
100
|
/* @__PURE__ */ jsx("div", {
|
|
45
101
|
className: galleryImageDragHandle,
|
|
@@ -49,6 +105,7 @@ var SortableImageCard = ({ id, image, index, isLast, lastInputRef, onUpdate, onR
|
|
|
49
105
|
}),
|
|
50
106
|
/* @__PURE__ */ jsx("div", {
|
|
51
107
|
className: galleryImageThumb,
|
|
108
|
+
title: "Drop an image here to replace",
|
|
52
109
|
children: image.src ? /* @__PURE__ */ jsx("img", {
|
|
53
110
|
alt: image.alt || "",
|
|
54
111
|
src: image.src
|
|
@@ -119,7 +176,7 @@ var DragOverlayCard = ({ image }) => /* @__PURE__ */ jsxs("div", {
|
|
|
119
176
|
})
|
|
120
177
|
]
|
|
121
178
|
});
|
|
122
|
-
var GalleryEditorDialogContent = ({ dismiss, initialImages, initialLayout, onImagesChange, onLayoutChange }) => {
|
|
179
|
+
var GalleryEditorDialogContent = ({ dismiss, initialImages, initialLayout, uploader, onImagesChange, onLayoutChange }) => {
|
|
123
180
|
const { className: portalClassName } = usePortalTheme();
|
|
124
181
|
const [entries, setEntries] = useState(() => initialImages.map((img) => ({
|
|
125
182
|
id: genId(),
|
|
@@ -128,6 +185,8 @@ var GalleryEditorDialogContent = ({ dismiss, initialImages, initialLayout, onIma
|
|
|
128
185
|
const [layout, setLayout] = useState(initialLayout);
|
|
129
186
|
const newInputRef = useRef(null);
|
|
130
187
|
const [dragActiveId, setDragActiveId] = useState(null);
|
|
188
|
+
const [bodyDropActive, setBodyDropActive] = useState(false);
|
|
189
|
+
const dragDepthRef = useRef(0);
|
|
131
190
|
const itemIds = entries.map((e) => e.id);
|
|
132
191
|
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }));
|
|
133
192
|
const handleAddImage = useCallback(() => {
|
|
@@ -145,6 +204,32 @@ var GalleryEditorDialogContent = ({ dismiss, initialImages, initialLayout, onIma
|
|
|
145
204
|
const handleRemoveImage = useCallback((index) => {
|
|
146
205
|
setEntries((prev) => prev.filter((_, i) => i !== index));
|
|
147
206
|
}, []);
|
|
207
|
+
const appendFiles = useCallback(async (files) => {
|
|
208
|
+
if (!files.length) return;
|
|
209
|
+
const fresh = (await Promise.all(files.map(async (file) => {
|
|
210
|
+
try {
|
|
211
|
+
return await fileToGalleryImage(file, uploader);
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}))).filter((r) => !!r && !!r.src).map((image) => ({
|
|
216
|
+
id: genId(),
|
|
217
|
+
image
|
|
218
|
+
}));
|
|
219
|
+
if (fresh.length) setEntries((prev) => [...prev, ...fresh]);
|
|
220
|
+
}, [uploader]);
|
|
221
|
+
const replaceEntryFile = useCallback(async (index, file) => {
|
|
222
|
+
try {
|
|
223
|
+
const next = await fileToGalleryImage(file, uploader);
|
|
224
|
+
setEntries((prev) => prev.map((entry, i) => i === index ? {
|
|
225
|
+
...entry,
|
|
226
|
+
image: {
|
|
227
|
+
src: next.src,
|
|
228
|
+
alt: next.alt || entry.image.alt || ""
|
|
229
|
+
}
|
|
230
|
+
} : entry));
|
|
231
|
+
} catch {}
|
|
232
|
+
}, [uploader]);
|
|
148
233
|
const handleUpdateImage = useCallback((index, field, value) => {
|
|
149
234
|
setEntries((prev) => prev.map((entry, i) => i === index ? {
|
|
150
235
|
...entry,
|
|
@@ -177,6 +262,29 @@ var GalleryEditorDialogContent = ({ dismiss, initialImages, initialLayout, onIma
|
|
|
177
262
|
dismiss
|
|
178
263
|
]);
|
|
179
264
|
const dragActiveEntry = dragActiveId ? entries.find((e) => e.id === dragActiveId) : null;
|
|
265
|
+
const handleBodyDragEnter = (e) => {
|
|
266
|
+
if (!e.dataTransfer?.types.includes("Files")) return;
|
|
267
|
+
dragDepthRef.current += 1;
|
|
268
|
+
setBodyDropActive(true);
|
|
269
|
+
};
|
|
270
|
+
const handleBodyDragOver = (e) => {
|
|
271
|
+
if (!e.dataTransfer?.types.includes("Files")) return;
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
e.dataTransfer.dropEffect = "copy";
|
|
274
|
+
};
|
|
275
|
+
const handleBodyDragLeave = (e) => {
|
|
276
|
+
if (!e.dataTransfer?.types.includes("Files")) return;
|
|
277
|
+
dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);
|
|
278
|
+
if (dragDepthRef.current === 0) setBodyDropActive(false);
|
|
279
|
+
};
|
|
280
|
+
const handleBodyDrop = (e) => {
|
|
281
|
+
const files = extractImageFiles(e.dataTransfer?.files);
|
|
282
|
+
dragDepthRef.current = 0;
|
|
283
|
+
setBodyDropActive(false);
|
|
284
|
+
if (!files.length) return;
|
|
285
|
+
e.preventDefault();
|
|
286
|
+
appendFiles(files);
|
|
287
|
+
};
|
|
180
288
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
181
289
|
/* @__PURE__ */ jsxs("div", {
|
|
182
290
|
className: galleryDialogHeader,
|
|
@@ -203,11 +311,20 @@ var GalleryEditorDialogContent = ({ dismiss, initialImages, initialLayout, onIma
|
|
|
203
311
|
}),
|
|
204
312
|
/* @__PURE__ */ jsx("div", {
|
|
205
313
|
className: galleryDialogBody,
|
|
314
|
+
style: bodyDropActive ? {
|
|
315
|
+
outline: `2px dashed ${vars.color.accent}`,
|
|
316
|
+
outlineOffset: -8,
|
|
317
|
+
borderRadius: 8
|
|
318
|
+
} : void 0,
|
|
319
|
+
onDragEnter: handleBodyDragEnter,
|
|
320
|
+
onDragLeave: handleBodyDragLeave,
|
|
321
|
+
onDragOver: handleBodyDragOver,
|
|
322
|
+
onDrop: handleBodyDrop,
|
|
206
323
|
children: entries.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
207
324
|
className: galleryDialogEmpty,
|
|
208
325
|
children: [
|
|
209
326
|
/* @__PURE__ */ jsx(ImageIcon, { size: 32 }),
|
|
210
|
-
/* @__PURE__ */ jsx("span", { children: "Add your first image" }),
|
|
327
|
+
/* @__PURE__ */ jsx("span", { children: "Add your first image or drop files here" }),
|
|
211
328
|
/* @__PURE__ */ jsxs("button", {
|
|
212
329
|
className: galleryAddBtn,
|
|
213
330
|
style: { maxWidth: 200 },
|
|
@@ -235,6 +352,7 @@ var GalleryEditorDialogContent = ({ dismiss, initialImages, initialLayout, onIma
|
|
|
235
352
|
index,
|
|
236
353
|
isLast: index === entries.length - 1,
|
|
237
354
|
lastInputRef: newInputRef,
|
|
355
|
+
onDropFile: replaceEntryFile,
|
|
238
356
|
onRemove: handleRemoveImage,
|
|
239
357
|
onUpdate: handleUpdateImage
|
|
240
358
|
}, entry.id))
|
|
@@ -278,6 +396,7 @@ var GalleryEditorDialogContent = ({ dismiss, initialImages, initialLayout, onIma
|
|
|
278
396
|
var GalleryEditRenderer = ({ images, layout, onImagesChange, onLayoutChange }) => {
|
|
279
397
|
const { className: portalClassName } = usePortalTheme();
|
|
280
398
|
const theme = useColorScheme();
|
|
399
|
+
const uploader = useImageUpload();
|
|
281
400
|
const handleOpenEditor = useCallback(() => {
|
|
282
401
|
if (!onImagesChange || !onLayoutChange) return;
|
|
283
402
|
presentDialog({
|
|
@@ -285,6 +404,7 @@ var GalleryEditRenderer = ({ images, layout, onImagesChange, onLayoutChange }) =
|
|
|
285
404
|
dismiss,
|
|
286
405
|
initialImages: images,
|
|
287
406
|
initialLayout: layout,
|
|
407
|
+
uploader,
|
|
288
408
|
onImagesChange,
|
|
289
409
|
onLayoutChange
|
|
290
410
|
}),
|
|
@@ -300,7 +420,8 @@ var GalleryEditRenderer = ({ images, layout, onImagesChange, onLayoutChange }) =
|
|
|
300
420
|
onImagesChange,
|
|
301
421
|
onLayoutChange,
|
|
302
422
|
portalClassName,
|
|
303
|
-
theme
|
|
423
|
+
theme,
|
|
424
|
+
uploader
|
|
304
425
|
]);
|
|
305
426
|
if (!onImagesChange) return /* @__PURE__ */ jsx(GalleryRenderer, {
|
|
306
427
|
images,
|
package/dist/edit.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { t as __augmentLoaded_gallery } from "./augment-DgwFVGf9.js";
|
|
2
|
-
import { a as GalleryEditRenderer, i as GalleryEditNode, n as $createGalleryEditNode, r as $isGalleryEditNode, t as galleryEditNodes } from "./edit-
|
|
2
|
+
import { a as GalleryEditRenderer, i as GalleryEditNode, n as $createGalleryEditNode, r as $isGalleryEditNode, t as galleryEditNodes } from "./edit-C_0ARS8I.js";
|
|
3
3
|
export { $createGalleryEditNode, $isGalleryEditNode, GalleryEditNode, GalleryEditRenderer, __augmentLoaded_gallery, galleryEditNodes };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __augmentLoaded_gallery } from "./augment-DgwFVGf9.js";
|
|
2
2
|
import { t as GalleryRenderer } from "./GalleryRenderer-CiHBgw2X.js";
|
|
3
|
-
import { a as GalleryEditRenderer, i as GalleryEditNode, n as $createGalleryEditNode, r as $isGalleryEditNode, t as galleryEditNodes } from "./edit-
|
|
3
|
+
import { a as GalleryEditRenderer, i as GalleryEditNode, n as $createGalleryEditNode, r as $isGalleryEditNode, t as galleryEditNodes } from "./edit-C_0ARS8I.js";
|
|
4
4
|
import { a as GALLERY_NODE_KEY, n as $isGalleryNode, r as GalleryNode, t as $createGalleryNode } from "./GalleryNode-CvAj_vjz.js";
|
|
5
5
|
import { galleryNodes } from "./node.mjs";
|
|
6
6
|
import "./renderer.mjs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haklex/rich-ext-gallery",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.4",
|
|
4
4
|
"description": "Image gallery extension with drag-and-drop",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"lucide-react": "^1.0.0",
|
|
61
61
|
"react": ">=19",
|
|
62
62
|
"react-dom": ">=19",
|
|
63
|
-
"@haklex/rich-editor": "0.26.
|
|
64
|
-
"@haklex/rich-editor-ui": "0.26.
|
|
65
|
-
"@haklex/rich-style-token": "0.26.
|
|
63
|
+
"@haklex/rich-editor": "^0.26.4",
|
|
64
|
+
"@haklex/rich-editor-ui": "^0.26.4",
|
|
65
|
+
"@haklex/rich-style-token": "^0.26.4"
|
|
66
66
|
},
|
|
67
67
|
"publishConfig": {
|
|
68
68
|
"access": "public"
|