@appcorp/shadcn 1.1.90 → 1.1.91

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.
@@ -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 synchronously to avoid loading state
76
- var createObjectURLs = (0, react_1.useCallback)(function () {
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
- if (!resizeDialogOpen && pendingFileUrl) {
115
- URL.revokeObjectURL(pendingFileUrl);
116
- setPendingFileUrl("");
117
- setPendingFile(null);
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
- }, [resizeDialogOpen, pendingFileUrl]);
120
- var dropzoneOptions = {
138
+ // Already in object format
121
139
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
- accept: (Array.isArray(accept) ? undefined : accept) || undefined,
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
- return (react_1.default.createElement("div", { className: (0, utils_1.cn)("w-full", className), "data-testid": testIdDropzone },
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-[280px] flex items-center justify-center", isDragActive && "ring-2 ring-ring ring-offset-2", disabled && "opacity-60 pointer-events-none") }),
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
- maxFiles === 1 ? "an image" : "images"),
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.90",
3
+ "version": "1.1.91",
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",