@appcorp/shadcn 1.1.90 → 1.1.92
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/components/enhanced-dropzone.js +92 -21
- package/package.json +1 -1
|
@@ -72,8 +72,8 @@ var EnhancedDropzone = function (_a) {
|
|
|
72
72
|
var _f = (0, react_1.useState)(false), resizeDialogOpen = _f[0], setResizeDialogOpen = _f[1];
|
|
73
73
|
var _g = (0, react_1.useState)(null), pendingFile = _g[0], setPendingFile = _g[1];
|
|
74
74
|
var _h = (0, react_1.useState)(""), pendingFileUrl = _h[0], setPendingFileUrl = _h[1];
|
|
75
|
-
// Create object URLs for local files
|
|
76
|
-
|
|
75
|
+
// Create and manage object URLs for local files
|
|
76
|
+
(0, react_1.useEffect)(function () {
|
|
77
77
|
var map = localPreviewsRef.current;
|
|
78
78
|
// Create URLs for new files
|
|
79
79
|
localFiles.forEach(function (file) {
|
|
@@ -97,8 +97,6 @@ var EnhancedDropzone = function (_a) {
|
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
99
|
}, [localFiles]);
|
|
100
|
-
// Run synchronously on every render to avoid "loading" flash
|
|
101
|
-
createObjectURLs();
|
|
102
100
|
// Cleanup on unmount
|
|
103
101
|
(0, react_1.useEffect)(function () {
|
|
104
102
|
var map = localPreviewsRef.current;
|
|
@@ -111,15 +109,38 @@ var EnhancedDropzone = function (_a) {
|
|
|
111
109
|
}, []);
|
|
112
110
|
// Cleanup pending file URL when dialog closes
|
|
113
111
|
(0, react_1.useEffect)(function () {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
return function () {
|
|
113
|
+
if (pendingFileUrl) {
|
|
114
|
+
URL.revokeObjectURL(pendingFileUrl);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}, [pendingFileUrl]);
|
|
118
|
+
// Check if accept includes image types
|
|
119
|
+
var isImageOnly = react_1.default.useMemo(function () {
|
|
120
|
+
if (!accept)
|
|
121
|
+
return false;
|
|
122
|
+
var acceptTypes = Array.isArray(accept)
|
|
123
|
+
? accept
|
|
124
|
+
: Object.keys(accept || {});
|
|
125
|
+
return acceptTypes.some(function (type) { return typeof type === "string" && type.toLowerCase().includes("image"); });
|
|
126
|
+
}, [accept]);
|
|
127
|
+
// Convert accept array to react-dropzone format if needed
|
|
128
|
+
var normalizedAccept = react_1.default.useMemo(function () {
|
|
129
|
+
if (!accept)
|
|
130
|
+
return undefined;
|
|
131
|
+
if (Array.isArray(accept)) {
|
|
132
|
+
// Convert string array like ['image/*'] to object format { 'image/*': [] }
|
|
133
|
+
return accept.reduce(function (acc, type) {
|
|
134
|
+
acc[type] = [];
|
|
135
|
+
return acc;
|
|
136
|
+
}, {});
|
|
118
137
|
}
|
|
119
|
-
|
|
120
|
-
var dropzoneOptions = {
|
|
138
|
+
// Already in object format
|
|
121
139
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
-
|
|
140
|
+
return accept;
|
|
141
|
+
}, [accept]);
|
|
142
|
+
var dropzoneOptions = {
|
|
143
|
+
accept: normalizedAccept,
|
|
123
144
|
maxFiles: maxFiles,
|
|
124
145
|
maxSize: maxSize,
|
|
125
146
|
minSize: minSize,
|
|
@@ -169,6 +190,9 @@ var EnhancedDropzone = function (_a) {
|
|
|
169
190
|
// Handle resize dialog close
|
|
170
191
|
var handleResizeDialogClose = (0, react_1.useCallback)(function () {
|
|
171
192
|
setResizeDialogOpen(false);
|
|
193
|
+
// Cleanup will happen via the pendingFileUrl useEffect
|
|
194
|
+
setPendingFileUrl("");
|
|
195
|
+
setPendingFile(null);
|
|
172
196
|
}, []);
|
|
173
197
|
// Get all preview URLs (remote + local)
|
|
174
198
|
var allPreviews = __spreadArray(__spreadArray([], value.map(function (url) { return ({ type: "remote", url: url, index: 0 }); }), true), localFiles.map(function (file, index) { return ({
|
|
@@ -176,11 +200,36 @@ var EnhancedDropzone = function (_a) {
|
|
|
176
200
|
url: localPreviewsRef.current.get(file) || "",
|
|
177
201
|
index: index,
|
|
178
202
|
}); }), true);
|
|
179
|
-
|
|
203
|
+
// Format bytes to human-readable size
|
|
204
|
+
var formatBytes = function (bytes) {
|
|
205
|
+
if (bytes === 0)
|
|
206
|
+
return "0 B";
|
|
207
|
+
var k = 1024;
|
|
208
|
+
var sizes = ["B", "KB", "MB", "GB"];
|
|
209
|
+
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
210
|
+
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
|
|
211
|
+
};
|
|
212
|
+
// Build constraint text for the note
|
|
213
|
+
var getConstraintText = function () {
|
|
214
|
+
var constraints = [];
|
|
215
|
+
if (maxFiles && maxFiles > 1) {
|
|
216
|
+
constraints.push("Up to ".concat(maxFiles, " files"));
|
|
217
|
+
}
|
|
218
|
+
if (minSize || maxSize) {
|
|
219
|
+
var sizes = [];
|
|
220
|
+
if (minSize)
|
|
221
|
+
sizes.push("min ".concat(formatBytes(minSize)));
|
|
222
|
+
if (maxSize)
|
|
223
|
+
sizes.push("max ".concat(formatBytes(maxSize)));
|
|
224
|
+
constraints.push("".concat(sizes.join(", ")));
|
|
225
|
+
}
|
|
226
|
+
return constraints.join(" • ");
|
|
227
|
+
};
|
|
228
|
+
return (react_1.default.createElement("div", { className: (0, utils_1.cn)("w-full", className), "data-slot": "enhanced-dropzone", "data-testid": testIdDropzone },
|
|
180
229
|
label && (react_1.default.createElement("label", { className: "mb-2 block text-sm font-medium" }, label)),
|
|
181
|
-
react_1.default.createElement("div", __assign({}, getRootProps(), { className: (0, utils_1.cn)("relative w-full rounded-md border border-dashed p-6 text-center min-h-
|
|
230
|
+
react_1.default.createElement("div", __assign({}, getRootProps(), { className: (0, utils_1.cn)("relative w-full rounded-md border border-dashed p-6 text-center min-h-70 flex items-center justify-center", isDragActive && "ring-2 ring-ring ring-offset-2", disabled && "opacity-60 pointer-events-none") }),
|
|
182
231
|
react_1.default.createElement("input", __assign({}, getInputProps(), { id: id, "data-testid": testIdInput })),
|
|
183
|
-
allPreviews.length > 0 ? (react_1.default.createElement("div", { className: "flex flex-col items-center w-full" },
|
|
232
|
+
isImageOnly && allPreviews.length > 0 ? (react_1.default.createElement("div", { className: "flex flex-col items-center w-full" },
|
|
184
233
|
react_1.default.createElement("div", { className: "relative w-full max-w-md" },
|
|
185
234
|
react_1.default.createElement(carousel_1.Carousel, { className: "w-full" },
|
|
186
235
|
react_1.default.createElement(carousel_1.CarouselPrevious, { type: "button", onClick: function (e) { return e.stopPropagation(); }, onPointerDown: function (e) { return e.stopPropagation(); }, onMouseDown: function (e) { return e.stopPropagation(); }, "data-testid": testIdPrev }),
|
|
@@ -204,6 +253,31 @@ var EnhancedDropzone = function (_a) {
|
|
|
204
253
|
" image",
|
|
205
254
|
allPreviews.length !== 1 ? "s" : "",
|
|
206
255
|
" ",
|
|
256
|
+
"selected"))) : !isImageOnly && allPreviews.length > 0 ? (react_1.default.createElement("div", { className: "flex flex-col items-center w-full gap-4" },
|
|
257
|
+
react_1.default.createElement("div", { className: "w-full max-w-md" },
|
|
258
|
+
react_1.default.createElement("div", { className: "space-y-2" }, allPreviews.map(function (preview, idx) {
|
|
259
|
+
var _a;
|
|
260
|
+
return (react_1.default.createElement("div", { key: "".concat(preview.type, "-").concat(preview.url, "-").concat(idx), "data-testid": "".concat(testIdPreviewPrefix !== null && testIdPreviewPrefix !== void 0 ? testIdPreviewPrefix : "dropzone-preview", "-").concat(idx), className: "flex items-center justify-between gap-3 p-3 rounded-lg border bg-muted/50" },
|
|
261
|
+
react_1.default.createElement("span", { className: "text-sm truncate flex-1", "data-testid": "".concat(testIdImagePrefix !== null && testIdImagePrefix !== void 0 ? testIdImagePrefix : "dropzone-image", "-").concat(idx) }, preview.type === "remote"
|
|
262
|
+
? new URL(preview.url).pathname.split("/").pop() ||
|
|
263
|
+
"File"
|
|
264
|
+
: ((_a = localFiles[preview.index]) === null || _a === void 0 ? void 0 : _a.name) || "File"),
|
|
265
|
+
react_1.default.createElement(button_1.Button, { type: "button", size: "icon", variant: "ghost", onClick: function (e) {
|
|
266
|
+
e.stopPropagation();
|
|
267
|
+
if (preview.type === "remote") {
|
|
268
|
+
handleRemoveRemote(preview.url);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
handleRemoveLocal(preview.index);
|
|
272
|
+
}
|
|
273
|
+
}, className: "h-8 w-8 shrink-0", "aria-label": "Remove file", "data-testid": "".concat(testIdRemovePrefix !== null && testIdRemovePrefix !== void 0 ? testIdRemovePrefix : "dropzone-remove", "-").concat(idx) },
|
|
274
|
+
react_1.default.createElement(lucide_react_1.XIcon, { className: "h-4 w-4" }))));
|
|
275
|
+
}))),
|
|
276
|
+
react_1.default.createElement("p", { className: "text-sm font-medium text-muted-foreground", "data-testid": testIdCount },
|
|
277
|
+
allPreviews.length,
|
|
278
|
+
" file",
|
|
279
|
+
allPreviews.length !== 1 ? "s" : "",
|
|
280
|
+
" ",
|
|
207
281
|
"selected"))) : (react_1.default.createElement("div", { className: "flex flex-col items-center justify-center gap-2" },
|
|
208
282
|
react_1.default.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-lg bg-muted text-muted-foreground", "data-testid": testIdEmptyIcon },
|
|
209
283
|
react_1.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "lucide lucide-image" },
|
|
@@ -211,14 +285,11 @@ var EnhancedDropzone = function (_a) {
|
|
|
211
285
|
react_1.default.createElement("circle", { cx: "9", cy: "9", r: "2" }),
|
|
212
286
|
react_1.default.createElement("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" }))),
|
|
213
287
|
react_1.default.createElement("div", { className: "space-y-1 text-center" },
|
|
214
|
-
react_1.default.createElement("p", { className: "font-medium text-sm", "data-testid": testIdEmptyTitle },
|
|
215
|
-
"Upload "
|
|
216
|
-
|
|
288
|
+
react_1.default.createElement("p", { className: "font-medium text-sm", "data-testid": testIdEmptyTitle }, maxFiles === 1
|
|
289
|
+
? "Upload ".concat(isImageOnly ? "an image" : "a file")
|
|
290
|
+
: "Upload ".concat(isImageOnly ? "images" : "files")),
|
|
217
291
|
react_1.default.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptySubtitle }, "Drag and drop or click to browse"),
|
|
218
|
-
maxFiles > 1 && (react_1.default.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptyNote },
|
|
219
|
-
"Up to ",
|
|
220
|
-
maxFiles,
|
|
221
|
-
" files")))))),
|
|
292
|
+
(maxFiles > 1 || minSize || maxSize) && (react_1.default.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptyNote }, getConstraintText())))))),
|
|
222
293
|
(error || info) && (react_1.default.createElement("div", { className: "mt-2", "data-testid": testIdMessage }, error ? (react_1.default.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (react_1.default.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null)),
|
|
223
294
|
resize && pendingFile && (react_1.default.createElement(image_resize_dialog_1.ImageResizeDialog, { open: resizeDialogOpen, onClose: handleResizeDialogClose, imageUrl: pendingFileUrl, onCropComplete: handleCropComplete, fileName: pendingFile.name }))));
|
|
224
295
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appcorp/shadcn",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.92",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build:next": "next build",
|
|
6
6
|
"build:storybook": "mv ../.pnp.cjs ../.pnp.cjs.bak 2>/dev/null || true && storybook build -c .storybook -o .out && mv ../.pnp.cjs.bak ../.pnp.cjs 2>/dev/null || true",
|