@appcorp/shadcn 1.2.0 → 2.0.0

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.
Files changed (231) hide show
  1. package/components/app-sidebar.js +16 -23
  2. package/components/audio.js +11 -19
  3. package/components/dashboard.js +27 -65
  4. package/components/drawer-generic.js +20 -28
  5. package/components/enhanced-checkbox.js +10 -57
  6. package/components/enhanced-combobox.js +99 -143
  7. package/components/enhanced-drawer-footer.js +22 -31
  8. package/components/enhanced-drawer-header.js +6 -14
  9. package/components/enhanced-dropzone-impl.d.ts +32 -0
  10. package/components/enhanced-dropzone-impl.js +218 -0
  11. package/components/enhanced-dropzone.d.ts +2 -63
  12. package/components/enhanced-dropzone.js +6 -294
  13. package/components/enhanced-input.js +10 -57
  14. package/components/enhanced-label.js +6 -52
  15. package/components/enhanced-radio.js +12 -20
  16. package/components/enhanced-select.js +13 -61
  17. package/components/enhanced-slider.js +11 -58
  18. package/components/enhanced-switch.js +11 -58
  19. package/components/enhanced-table-footer-action.js +11 -19
  20. package/components/enhanced-table-footer-page.js +16 -24
  21. package/components/enhanced-table-footer-pagination.js +11 -21
  22. package/components/enhanced-table-header-action.js +22 -30
  23. package/components/enhanced-table-header-search.js +9 -17
  24. package/components/enhanced-table.js +125 -153
  25. package/components/enhanced-textarea.js +10 -57
  26. package/components/field.d.ts +1 -1
  27. package/components/field.js +31 -88
  28. package/components/image-resize-dialog.js +61 -159
  29. package/components/nav-main.js +32 -78
  30. package/components/nav-projects.js +37 -48
  31. package/components/nav-user.js +41 -51
  32. package/components/show-toast.js +5 -9
  33. package/components/sidebar.d.ts +1 -1
  34. package/components/sidebar.js +115 -184
  35. package/components/team-switcher.js +36 -47
  36. package/components/theme-provider.js +36 -84
  37. package/components/timeline.js +12 -20
  38. package/components/tooltip.js +10 -59
  39. package/components/ui/accordion.js +17 -67
  40. package/components/ui/alert-dialog.js +32 -89
  41. package/components/ui/alert.d.ts +1 -1
  42. package/components/ui/alert.js +14 -63
  43. package/components/ui/area-chart-v1-impl.d.ts +19 -0
  44. package/components/ui/area-chart-v1-impl.js +75 -0
  45. package/components/ui/area-chart-v1.d.ts +2 -19
  46. package/components/ui/area-chart-v1.js +4 -113
  47. package/components/ui/aspect-ratio.js +3 -39
  48. package/components/ui/avatar.js +13 -62
  49. package/components/ui/badge.d.ts +2 -2
  50. package/components/ui/badge.js +7 -55
  51. package/components/ui/breadcrumb.js +27 -80
  52. package/components/ui/button.d.ts +2 -2
  53. package/components/ui/button.js +10 -58
  54. package/components/ui/calendar-impl.d.ts +8 -0
  55. package/components/ui/calendar-impl.js +55 -0
  56. package/components/ui/calendar.d.ts +2 -8
  57. package/components/ui/calendar.js +5 -108
  58. package/components/ui/card.js +21 -73
  59. package/components/ui/carousel-impl.d.ts +18 -0
  60. package/components/ui/carousel-impl.js +117 -0
  61. package/components/ui/carousel.d.ts +6 -18
  62. package/components/ui/carousel.js +8 -170
  63. package/components/ui/chart-impl.d.ts +62 -0
  64. package/components/ui/chart-impl.js +162 -0
  65. package/components/ui/chart.d.ts +7 -62
  66. package/components/ui/chart.js +9 -219
  67. package/components/ui/checkbox.js +10 -57
  68. package/components/ui/collapsible.js +5 -43
  69. package/components/ui/combobox.js +22 -58
  70. package/components/ui/command-impl.d.ts +80 -0
  71. package/components/ui/command-impl.js +63 -0
  72. package/components/ui/command.d.ts +9 -80
  73. package/components/ui/command.js +12 -117
  74. package/components/ui/context-menu.js +41 -102
  75. package/components/ui/dialog.js +28 -84
  76. package/components/ui/drawer-impl.d.ts +22 -0
  77. package/components/ui/drawer-impl.js +56 -0
  78. package/components/ui/drawer.d.ts +10 -22
  79. package/components/ui/drawer.js +13 -111
  80. package/components/ui/dropdown-menu.js +41 -102
  81. package/components/ui/form.d.ts +4 -2
  82. package/components/ui/form.js +41 -95
  83. package/components/ui/hover-card.js +9 -58
  84. package/components/ui/input-otp-impl.d.ts +34 -0
  85. package/components/ui/input-otp-impl.js +43 -0
  86. package/components/ui/input-otp.d.ts +4 -34
  87. package/components/ui/input-otp.js +7 -92
  88. package/components/ui/input.js +6 -53
  89. package/components/ui/label.d.ts +1 -1
  90. package/components/ui/label.js +9 -56
  91. package/components/ui/language-selector.js +17 -55
  92. package/components/ui/menubar.js +46 -108
  93. package/components/ui/navigation-menu.d.ts +1 -1
  94. package/components/ui/navigation-menu.js +29 -84
  95. package/components/ui/pagination.js +30 -83
  96. package/components/ui/popover.js +10 -60
  97. package/components/ui/progress.js +8 -55
  98. package/components/ui/radio-group.js +12 -60
  99. package/components/ui/resizable-impl.d.ts +24 -0
  100. package/components/ui/resizable-impl.js +27 -0
  101. package/components/ui/resizable.d.ts +3 -24
  102. package/components/ui/resizable.js +6 -78
  103. package/components/ui/scroll-area.js +10 -58
  104. package/components/ui/select.js +34 -90
  105. package/components/ui/separator.js +7 -54
  106. package/components/ui/sheet.d.ts +1 -1
  107. package/components/ui/sheet.js +30 -86
  108. package/components/ui/skeleton.js +5 -21
  109. package/components/ui/slider.js +10 -57
  110. package/components/ui/sonner.js +7 -24
  111. package/components/ui/switch.js +8 -55
  112. package/components/ui/table.js +27 -81
  113. package/components/ui/tabs.js +14 -64
  114. package/components/ui/textarea.js +6 -53
  115. package/components/ui/theme-switcher.js +41 -91
  116. package/components/ui/toggle.d.ts +2 -2
  117. package/components/ui/toggle.js +9 -57
  118. package/data/admin-dashboard.js +16 -19
  119. package/hooks/use-enhanced-combobox.js +47 -124
  120. package/hooks/use-mobile.js +8 -44
  121. package/hooks/use-outside-click.js +6 -10
  122. package/lib/themes.js +1 -4
  123. package/lib/toast-utils.js +78 -120
  124. package/lib/utils.js +4 -11
  125. package/package.json +9 -90
  126. package/utils/transform-breadcrumbs.js +4 -8
  127. package/utils/transform-navitems.js +7 -22
  128. package/components/aceternity-ui/apple-cards-carousel.d.ts +0 -35
  129. package/components/aceternity-ui/apple-cards-carousel.js +0 -263
  130. package/components/shadcn-example/ai-chat/chat-interface.d.ts +0 -11
  131. package/components/shadcn-example/ai-chat/chat-interface.js +0 -70
  132. package/components/shadcn-example/ai-chat/index.d.ts +0 -38
  133. package/components/shadcn-example/ai-chat/index.js +0 -66
  134. package/components/shadcn-example/ai-chat/sidebar.d.ts +0 -15
  135. package/components/shadcn-example/ai-chat/sidebar.js +0 -68
  136. package/components/shadcn-example/ai-chat/welcome-section.d.ts +0 -12
  137. package/components/shadcn-example/ai-chat/welcome-section.js +0 -26
  138. package/components/shadcn-example/authentication/index.d.ts +0 -24
  139. package/components/shadcn-example/authentication/index.js +0 -59
  140. package/components/shadcn-example/authentication/user-auth-form.d.ts +0 -11
  141. package/components/shadcn-example/authentication/user-auth-form.js +0 -54
  142. package/components/shadcn-example/blog-detail-page/comment-section.d.ts +0 -15
  143. package/components/shadcn-example/blog-detail-page/comment-section.js +0 -83
  144. package/components/shadcn-example/blog-detail-page/content.d.ts +0 -7
  145. package/components/shadcn-example/blog-detail-page/content.js +0 -15
  146. package/components/shadcn-example/blog-detail-page/header.d.ts +0 -11
  147. package/components/shadcn-example/blog-detail-page/header.js +0 -36
  148. package/components/shadcn-example/blog-detail-page/index.d.ts +0 -25
  149. package/components/shadcn-example/blog-detail-page/index.js +0 -74
  150. package/components/shadcn-example/blog-page/blog-post-card.d.ts +0 -13
  151. package/components/shadcn-example/blog-page/blog-post-card.js +0 -30
  152. package/components/shadcn-example/blog-page/featured-post-sidebar-item.d.ts +0 -10
  153. package/components/shadcn-example/blog-page/featured-post-sidebar-item.js +0 -19
  154. package/components/shadcn-example/blog-page/index.d.ts +0 -20
  155. package/components/shadcn-example/blog-page/index.js +0 -36
  156. package/components/shadcn-example/coming-soon/countdown.d.ts +0 -2
  157. package/components/shadcn-example/coming-soon/countdown.js +0 -82
  158. package/components/shadcn-example/coming-soon/index.d.ts +0 -10
  159. package/components/shadcn-example/coming-soon/index.js +0 -41
  160. package/components/shadcn-io/color-picker/index.d.ts +0 -43
  161. package/components/shadcn-io/color-picker/index.js +0 -304
  162. package/components/shadcn-io/copy-button/index.d.ts +0 -16
  163. package/components/shadcn-io/copy-button/index.js +0 -121
  164. package/components/shadcn-io/dropzone/index.d.ts +0 -19
  165. package/components/shadcn-io/dropzone/index.js +0 -131
  166. package/components/shadcn-io/gantt/index.d.ts +0 -145
  167. package/components/shadcn-io/gantt/index.js +0 -766
  168. package/components/shadcn-io/table/index.d.ts +0 -60
  169. package/components/shadcn-io/table/index.js +0 -138
  170. package/templates/appcorp-v1/footer/bottom-bar.d.ts +0 -6
  171. package/templates/appcorp-v1/footer/bottom-bar.js +0 -25
  172. package/templates/appcorp-v1/footer/cta.d.ts +0 -6
  173. package/templates/appcorp-v1/footer/cta.js +0 -21
  174. package/templates/appcorp-v1/footer/footer-grid.d.ts +0 -14
  175. package/templates/appcorp-v1/footer/footer-grid.js +0 -52
  176. package/templates/appcorp-v1/footer/index.d.ts +0 -3
  177. package/templates/appcorp-v1/footer/index.js +0 -28
  178. package/templates/appcorp-v1/footer/newsletter.d.ts +0 -11
  179. package/templates/appcorp-v1/footer/newsletter.js +0 -28
  180. package/templates/appcorp-v1/footer/stats.d.ts +0 -6
  181. package/templates/appcorp-v1/footer/stats.js +0 -15
  182. package/templates/appcorp-v1/header/announcement-bar.d.ts +0 -3
  183. package/templates/appcorp-v1/header/announcement-bar.js +0 -15
  184. package/templates/appcorp-v1/header/dropdown-panel.d.ts +0 -8
  185. package/templates/appcorp-v1/header/dropdown-panel.js +0 -72
  186. package/templates/appcorp-v1/header/index.d.ts +0 -12
  187. package/templates/appcorp-v1/header/index.js +0 -16
  188. package/templates/appcorp-v1/header/mobile-menu.d.ts +0 -14
  189. package/templates/appcorp-v1/header/mobile-menu.js +0 -101
  190. package/templates/appcorp-v1/header/navigation-bar.d.ts +0 -3
  191. package/templates/appcorp-v1/header/navigation-bar.js +0 -117
  192. package/templates/appcorp-v1/header/social-button.d.ts +0 -7
  193. package/templates/appcorp-v1/header/social-button.js +0 -48
  194. package/templates/appcorp-v1/home.d.ts +0 -3
  195. package/templates/appcorp-v1/home.js +0 -27
  196. package/templates/data/appcorp-v1.d.ts +0 -93
  197. package/templates/data/appcorp-v1.js +0 -348
  198. package/templates/data/edupilotpro-v1.d.ts +0 -33
  199. package/templates/data/edupilotpro-v1.js +0 -116
  200. package/templates/data/school-v1.d.ts +0 -328
  201. package/templates/data/school-v1.js +0 -1563
  202. package/templates/edupilotpro-v1/feature-card.d.ts +0 -13
  203. package/templates/edupilotpro-v1/feature-card.js +0 -28
  204. package/templates/edupilotpro-v1/mockups/dashboard-mockup.d.ts +0 -3
  205. package/templates/edupilotpro-v1/mockups/dashboard-mockup.js +0 -55
  206. package/templates/school-v1/footer.d.ts +0 -3
  207. package/templates/school-v1/footer.js +0 -75
  208. package/templates/school-v1/index.d.ts +0 -18
  209. package/templates/school-v1/index.js +0 -60
  210. package/templates/school-v1/navigation.d.ts +0 -3
  211. package/templates/school-v1/navigation.js +0 -123
  212. package/templates/school-v1/sections/about.d.ts +0 -3
  213. package/templates/school-v1/sections/about.js +0 -49
  214. package/templates/school-v1/sections/admissions.d.ts +0 -3
  215. package/templates/school-v1/sections/admissions.js +0 -77
  216. package/templates/school-v1/sections/contact.d.ts +0 -3
  217. package/templates/school-v1/sections/contact.js +0 -67
  218. package/templates/school-v1/sections/faqs.d.ts +0 -3
  219. package/templates/school-v1/sections/faqs.js +0 -50
  220. package/templates/school-v1/sections/hero.d.ts +0 -3
  221. package/templates/school-v1/sections/hero.js +0 -132
  222. package/templates/school-v1/sections/management.d.ts +0 -3
  223. package/templates/school-v1/sections/management.js +0 -34
  224. package/templates/school-v1/sections/policies.d.ts +0 -3
  225. package/templates/school-v1/sections/policies.js +0 -127
  226. package/templates/school-v1/sections/programs.d.ts +0 -3
  227. package/templates/school-v1/sections/programs.js +0 -107
  228. package/templates/school-v1/sections/testimonials.d.ts +0 -3
  229. package/templates/school-v1/sections/testimonials.js +0 -46
  230. package/templates/school-v1/sections/why-choose-us.d.ts +0 -3
  231. package/templates/school-v1/sections/why-choose-us.js +0 -57
@@ -0,0 +1,218 @@
1
+ "use client";
2
+ import React, { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useDropzone } from "react-dropzone";
4
+ import { cn } from "../lib/utils";
5
+ import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext, } from "./ui/carousel";
6
+ import { Button } from "./ui/button";
7
+ import { ImageResizeDialog } from "./image-resize-dialog";
8
+ import { XIcon } from "lucide-react";
9
+ export const EnhancedDropzone = ({ id, label, info, error, accept, maxFiles = 10, maxSize, minSize, disabled, value = [], onChange, onRemoveRemote, resize = false, className, testIdDropzone, testIdInput, testIdPrev, testIdNext, testIdPreviewPrefix, testIdImagePrefix, testIdRemovePrefix, testIdCount, testIdEmptyIcon, testIdEmptyTitle, testIdEmptySubtitle, testIdEmptyNote, testIdMessage, }) => {
10
+ const [localFiles, setLocalFiles] = useState([]);
11
+ const localPreviewsRef = useRef(new Map());
12
+ const [resizeDialogOpen, setResizeDialogOpen] = useState(false);
13
+ const [pendingFile, setPendingFile] = useState(null);
14
+ const [pendingFileUrl, setPendingFileUrl] = useState("");
15
+ useEffect(() => {
16
+ const map = localPreviewsRef.current;
17
+ localFiles.forEach((file) => {
18
+ if (!map.has(file)) {
19
+ try {
20
+ const url = URL.createObjectURL(file);
21
+ map.set(file, url);
22
+ }
23
+ catch (error) {
24
+ console.error("Failed to create object URL:", error);
25
+ }
26
+ }
27
+ });
28
+ const currentFiles = new Set(localFiles);
29
+ Array.from(map.entries()).forEach(([file, url]) => {
30
+ if (!currentFiles.has(file)) {
31
+ URL.revokeObjectURL(url);
32
+ map.delete(file);
33
+ }
34
+ });
35
+ }, [localFiles]);
36
+ useEffect(() => {
37
+ const map = localPreviewsRef.current;
38
+ return () => {
39
+ Array.from(map.values()).forEach((url) => {
40
+ URL.revokeObjectURL(url);
41
+ });
42
+ map.clear();
43
+ };
44
+ }, []);
45
+ useEffect(() => {
46
+ return () => {
47
+ if (pendingFileUrl) {
48
+ URL.revokeObjectURL(pendingFileUrl);
49
+ }
50
+ };
51
+ }, [pendingFileUrl]);
52
+ const isImageOnly = React.useMemo(() => {
53
+ if (!accept)
54
+ return false;
55
+ const acceptTypes = Array.isArray(accept)
56
+ ? accept
57
+ : Object.keys(accept || {});
58
+ return acceptTypes.some((type) => typeof type === "string" && type.toLowerCase().includes("image"));
59
+ }, [accept]);
60
+ const normalizedAccept = React.useMemo(() => {
61
+ if (!accept)
62
+ return undefined;
63
+ if (Array.isArray(accept)) {
64
+ return accept.reduce((acc, type) => {
65
+ acc[type] = [];
66
+ return acc;
67
+ }, {});
68
+ }
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ return accept;
71
+ }, [accept]);
72
+ const dropzoneOptions = {
73
+ accept: normalizedAccept,
74
+ maxFiles,
75
+ maxSize,
76
+ minSize,
77
+ disabled,
78
+ onDrop: useCallback((acceptedFiles) => {
79
+ if (resize && acceptedFiles.length > 0) {
80
+ const file = acceptedFiles[0];
81
+ const url = URL.createObjectURL(file);
82
+ setPendingFile(file);
83
+ setPendingFileUrl(url);
84
+ setResizeDialogOpen(true);
85
+ }
86
+ else {
87
+ setLocalFiles((prev) => {
88
+ const combined = [...prev, ...acceptedFiles];
89
+ const limited = maxFiles ? combined.slice(0, maxFiles) : combined;
90
+ onChange === null || onChange === void 0 ? void 0 : onChange(limited);
91
+ return limited;
92
+ });
93
+ }
94
+ }, [maxFiles, onChange, resize]),
95
+ };
96
+ const { getRootProps, getInputProps, isDragActive } = useDropzone(dropzoneOptions);
97
+ const handleRemoveRemote = useCallback((url) => {
98
+ onRemoveRemote === null || onRemoveRemote === void 0 ? void 0 : onRemoveRemote(url);
99
+ }, [onRemoveRemote]);
100
+ const handleRemoveLocal = useCallback((index) => {
101
+ setLocalFiles((prev) => {
102
+ const updated = prev.filter((_, i) => i !== index);
103
+ onChange === null || onChange === void 0 ? void 0 : onChange(updated);
104
+ return updated;
105
+ });
106
+ }, [onChange]);
107
+ const handleCropComplete = useCallback((croppedImage) => {
108
+ setLocalFiles((prev) => {
109
+ const combined = [...prev, croppedImage];
110
+ const limited = maxFiles ? combined.slice(0, maxFiles) : combined;
111
+ onChange === null || onChange === void 0 ? void 0 : onChange(limited);
112
+ return limited;
113
+ });
114
+ }, [maxFiles, onChange]);
115
+ const handleResizeDialogClose = useCallback(() => {
116
+ setResizeDialogOpen(false);
117
+ setPendingFileUrl("");
118
+ setPendingFile(null);
119
+ }, []);
120
+ const allPreviews = [
121
+ ...value.map((url) => ({ type: "remote", url, index: 0 })),
122
+ ...localFiles.map((file, index) => ({
123
+ type: "local",
124
+ url: localPreviewsRef.current.get(file) || "",
125
+ index,
126
+ })),
127
+ ];
128
+ const formatBytes = (bytes) => {
129
+ if (bytes === 0)
130
+ return "0 B";
131
+ const k = 1024;
132
+ const sizes = ["B", "KB", "MB", "GB"];
133
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
134
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
135
+ };
136
+ const getConstraintText = () => {
137
+ const constraints = [];
138
+ if (maxFiles && maxFiles > 1) {
139
+ constraints.push(`Up to ${maxFiles} files`);
140
+ }
141
+ if (minSize || maxSize) {
142
+ const sizes = [];
143
+ if (minSize)
144
+ sizes.push(`min ${formatBytes(minSize)}`);
145
+ if (maxSize)
146
+ sizes.push(`max ${formatBytes(maxSize)}`);
147
+ constraints.push(`${sizes.join(", ")}`);
148
+ }
149
+ return constraints.join(" • ");
150
+ };
151
+ return (React.createElement("div", { className: cn("w-full", className), "data-slot": "enhanced-dropzone", "data-testid": testIdDropzone },
152
+ label && (React.createElement("label", { className: "mb-2 block text-sm font-medium" }, label)),
153
+ React.createElement("div", Object.assign({}, getRootProps(), { className: 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") }),
154
+ React.createElement("input", Object.assign({}, getInputProps(), { id: id, "data-testid": testIdInput })),
155
+ isImageOnly && allPreviews.length > 0 ? (React.createElement("div", { className: "flex flex-col items-center w-full" },
156
+ React.createElement("div", { className: "relative w-full max-w-md" },
157
+ React.createElement(Carousel, { className: "w-full" },
158
+ React.createElement(CarouselPrevious, { type: "button", onClick: (e) => e.stopPropagation(), onPointerDown: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), "data-testid": testIdPrev }),
159
+ React.createElement(CarouselNext, { type: "button", onClick: (e) => e.stopPropagation(), onPointerDown: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), "data-testid": testIdNext }),
160
+ React.createElement(CarouselContent, { className: "ml-0" }, allPreviews.map((preview, idx) => (React.createElement(CarouselItem, { key: `${preview.type}-${preview.url}-${idx}`, className: "pl-4", "data-testid": `${testIdPreviewPrefix !== null && testIdPreviewPrefix !== void 0 ? testIdPreviewPrefix : "dropzone-preview"}-${idx}` },
161
+ React.createElement("div", { className: "relative aspect-square w-full max-w-xs mx-auto" }, preview.url ? (React.createElement(React.Fragment, null,
162
+ React.createElement("img", { src: preview.url, alt: `Preview ${idx + 1}`, className: "h-full w-full rounded-lg object-cover", onClick: (e) => e.stopPropagation(), "data-testid": `${testIdImagePrefix !== null && testIdImagePrefix !== void 0 ? testIdImagePrefix : "dropzone-image"}-${idx}` }),
163
+ React.createElement(Button, { type: "button", size: "icon", variant: "destructive", onClick: (e) => {
164
+ e.stopPropagation();
165
+ if (preview.type === "remote") {
166
+ handleRemoveRemote(preview.url);
167
+ }
168
+ else {
169
+ handleRemoveLocal(preview.index);
170
+ }
171
+ }, className: "absolute right-2 top-2 h-8 w-8 rounded-full", "aria-label": "Remove image", "data-testid": `${testIdRemovePrefix !== null && testIdRemovePrefix !== void 0 ? testIdRemovePrefix : "dropzone-remove"}-${idx}` },
172
+ React.createElement(XIcon, { className: "h-4 w-4" })))) : (React.createElement("div", { className: "flex h-full w-full items-center justify-center rounded-lg bg-muted text-muted-foreground" },
173
+ React.createElement("span", { className: "text-sm" }, "Loading...")))))))))),
174
+ React.createElement("p", { className: "mt-4 text-sm font-medium text-muted-foreground", "data-testid": testIdCount },
175
+ allPreviews.length,
176
+ " image",
177
+ allPreviews.length !== 1 ? "s" : "",
178
+ " ",
179
+ "selected"))) : !isImageOnly && allPreviews.length > 0 ? (React.createElement("div", { className: "flex flex-col items-center w-full gap-4" },
180
+ React.createElement("div", { className: "w-full max-w-md" },
181
+ React.createElement("div", { className: "space-y-2" }, allPreviews.map((preview, idx) => {
182
+ var _a;
183
+ return (React.createElement("div", { key: `${preview.type}-${preview.url}-${idx}`, "data-testid": `${testIdPreviewPrefix !== null && testIdPreviewPrefix !== void 0 ? testIdPreviewPrefix : "dropzone-preview"}-${idx}`, className: "flex items-center justify-between gap-3 p-3 rounded-lg border bg-muted/50" },
184
+ React.createElement("span", { className: "text-sm truncate flex-1", "data-testid": `${testIdImagePrefix !== null && testIdImagePrefix !== void 0 ? testIdImagePrefix : "dropzone-image"}-${idx}` }, preview.type === "remote"
185
+ ? new URL(preview.url).pathname.split("/").pop() ||
186
+ "File"
187
+ : ((_a = localFiles[preview.index]) === null || _a === void 0 ? void 0 : _a.name) || "File"),
188
+ React.createElement(Button, { type: "button", size: "icon", variant: "ghost", onClick: (e) => {
189
+ e.stopPropagation();
190
+ if (preview.type === "remote") {
191
+ handleRemoveRemote(preview.url);
192
+ }
193
+ else {
194
+ handleRemoveLocal(preview.index);
195
+ }
196
+ }, className: "h-8 w-8 shrink-0", "aria-label": "Remove file", "data-testid": `${testIdRemovePrefix !== null && testIdRemovePrefix !== void 0 ? testIdRemovePrefix : "dropzone-remove"}-${idx}` },
197
+ React.createElement(XIcon, { className: "h-4 w-4" }))));
198
+ }))),
199
+ React.createElement("p", { className: "text-sm font-medium text-muted-foreground", "data-testid": testIdCount },
200
+ allPreviews.length,
201
+ " file",
202
+ allPreviews.length !== 1 ? "s" : "",
203
+ " ",
204
+ "selected"))) : (React.createElement("div", { className: "flex flex-col items-center justify-center gap-2" },
205
+ React.createElement("div", { className: "flex h-12 w-12 items-center justify-center rounded-lg bg-muted text-muted-foreground", "data-testid": testIdEmptyIcon },
206
+ React.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" },
207
+ React.createElement("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
208
+ React.createElement("circle", { cx: "9", cy: "9", r: "2" }),
209
+ React.createElement("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" }))),
210
+ React.createElement("div", { className: "space-y-1 text-center" },
211
+ React.createElement("p", { className: "font-medium text-sm", "data-testid": testIdEmptyTitle }, maxFiles === 1
212
+ ? `Upload ${isImageOnly ? "an image" : "a file"}`
213
+ : `Upload ${isImageOnly ? "images" : "files"}`),
214
+ React.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptySubtitle }, "Drag and drop or click to browse"),
215
+ (maxFiles > 1 || minSize || maxSize) && (React.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptyNote }, getConstraintText())))))),
216
+ (error || info) && (React.createElement("div", { className: "mt-2", "data-testid": testIdMessage }, error ? (React.createElement("p", { className: "text-xs text-destructive" }, error)) : info ? (React.createElement("p", { className: "text-xs text-blue-600 dark:text-blue-400" }, info)) : null)),
217
+ resize && pendingFile && (React.createElement(ImageResizeDialog, { open: resizeDialogOpen, onClose: handleResizeDialogClose, imageUrl: pendingFileUrl, onCropComplete: handleCropComplete, fileName: pendingFile.name }))));
218
+ };
@@ -1,65 +1,4 @@
1
1
  import React from "react";
2
- import type { DropzoneOptions } from "react-dropzone";
3
- /**
4
- * Props for `EnhancedDropzone` component.
5
- */
6
- export type EnhancedDropzoneProps = {
7
- /** HTML id attribute applied to the hidden file input and used for labeling. */
8
- id?: string;
9
- /** Additional className(s) applied to the root container. */
10
- className?: string;
11
- /** Optional label text displayed above the dropzone. */
12
- label?: string;
13
- /** Informational helper text shown below the component (non-error). */
14
- info?: string;
15
- /** Error message text shown below the component; puts component into error state. */
16
- error?: string;
17
- /**
18
- * Accepted file types. Matches `react-dropzone` `accept` option or a list of
19
- * mime types / extensions (e.g. `['image/*']` or `{ 'image/*': [] }`).
20
- */
21
- accept?: DropzoneOptions["accept"] | string[];
22
- /** Maximum number of files allowed to be selected. Defaults to `10`. */
23
- maxFiles?: number;
24
- /** Maximum file size in bytes. */
25
- maxSize?: number;
26
- /** Minimum file size in bytes. */
27
- minSize?: number;
28
- /** Whether the dropzone is disabled (prevents interactions). */
29
- disabled?: boolean;
30
- /** Remote image URLs to display as previews (read-only remote values). */
31
- value?: string[];
32
- /** Called when the selected files change (local additions/removals). */
33
- onChange?: (files: File[]) => void;
34
- /** Called when a remote URL preview is removed by the user. */
35
- onRemoveRemote?: (url: string) => void;
36
- /** Enable image resize/crop functionality. When true, opens a dialog to crop images to square aspect ratio. */
37
- resize?: boolean;
38
- /** `data-testid` for the root dropzone container. */
39
- testIdDropzone?: string;
40
- /** `data-testid` for the hidden file input element. */
41
- testIdInput?: string;
42
- /** `data-testid` for the carousel previous button. */
43
- testIdPrev?: string;
44
- /** `data-testid` for the carousel next button. */
45
- testIdNext?: string;
46
- /** Prefix used for preview items: `${prefix}-${index}`. */
47
- testIdPreviewPrefix?: string;
48
- /** Prefix for preview image elements: `${prefix}-${index}`. */
49
- testIdImagePrefix?: string;
50
- /** Prefix for remove buttons on previews: `${prefix}-${index}`. */
51
- testIdRemovePrefix?: string;
52
- /** `data-testid` for the preview count text element. */
53
- testIdCount?: string;
54
- /** `data-testid` for the empty-state icon wrapper. */
55
- testIdEmptyIcon?: string;
56
- /** `data-testid` for the empty-state title. */
57
- testIdEmptyTitle?: string;
58
- /** `data-testid` for the empty-state subtitle. */
59
- testIdEmptySubtitle?: string;
60
- /** `data-testid` for the empty-state note (max files info). */
61
- testIdEmptyNote?: string;
62
- /** `data-testid` for the error/info message container. */
63
- testIdMessage?: string;
64
- };
2
+ import type { EnhancedDropzoneProps } from "./enhanced-dropzone-impl";
3
+ export type { EnhancedDropzoneProps };
65
4
  export declare const EnhancedDropzone: React.FC<EnhancedDropzoneProps>;
@@ -1,296 +1,8 @@
1
1
  "use client";
2
- "use strict";
3
- var __assign = (this && this.__assign) || function () {
4
- __assign = Object.assign || function(t) {
5
- for (var s, i = 1, n = arguments.length; i < n; i++) {
6
- s = arguments[i];
7
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
8
- t[p] = s[p];
9
- }
10
- return t;
11
- };
12
- return __assign.apply(this, arguments);
2
+ import React, { Suspense } from "react";
3
+ const DropzoneImpl = React.lazy(() => import("./enhanced-dropzone-impl").then((m) => ({ default: m.EnhancedDropzone })));
4
+ export const EnhancedDropzone = (props) => {
5
+ return (React.createElement(Suspense, { fallback: React.createElement("div", { className: "w-full min-h-70 rounded-md border border-dashed p-6 text-center flex items-center justify-center" },
6
+ React.createElement("p", { className: "text-sm text-muted-foreground" }, "Loading dropzone...")) },
7
+ React.createElement(DropzoneImpl, Object.assign({}, props))));
13
8
  };
14
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- var desc = Object.getOwnPropertyDescriptor(m, k);
17
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
- desc = { enumerable: true, get: function() { return m[k]; } };
19
- }
20
- Object.defineProperty(o, k2, desc);
21
- }) : (function(o, m, k, k2) {
22
- if (k2 === undefined) k2 = k;
23
- o[k2] = m[k];
24
- }));
25
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
- Object.defineProperty(o, "default", { enumerable: true, value: v });
27
- }) : function(o, v) {
28
- o["default"] = v;
29
- });
30
- var __importStar = (this && this.__importStar) || (function () {
31
- var ownKeys = function(o) {
32
- ownKeys = Object.getOwnPropertyNames || function (o) {
33
- var ar = [];
34
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
- return ar;
36
- };
37
- return ownKeys(o);
38
- };
39
- return function (mod) {
40
- if (mod && mod.__esModule) return mod;
41
- var result = {};
42
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
- __setModuleDefault(result, mod);
44
- return result;
45
- };
46
- })();
47
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
48
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
49
- if (ar || !(i in from)) {
50
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
51
- ar[i] = from[i];
52
- }
53
- }
54
- return to.concat(ar || Array.prototype.slice.call(from));
55
- };
56
- Object.defineProperty(exports, "__esModule", { value: true });
57
- exports.EnhancedDropzone = void 0;
58
- var react_1 = __importStar(require("react"));
59
- var react_dropzone_1 = require("react-dropzone");
60
- var utils_1 = require("../lib/utils");
61
- var carousel_1 = require("./ui/carousel");
62
- var button_1 = require("./ui/button");
63
- var image_resize_dialog_1 = require("./image-resize-dialog");
64
- var lucide_react_1 = require("lucide-react");
65
- var EnhancedDropzone = function (_a) {
66
- var id = _a.id, label = _a.label, info = _a.info, error = _a.error, accept = _a.accept, _b = _a.maxFiles, maxFiles = _b === void 0 ? 10 : _b, maxSize = _a.maxSize, minSize = _a.minSize, disabled = _a.disabled, _c = _a.value, value = _c === void 0 ? [] : _c, onChange = _a.onChange, onRemoveRemote = _a.onRemoveRemote, _d = _a.resize, resize = _d === void 0 ? false : _d, className = _a.className, testIdDropzone = _a.testIdDropzone, testIdInput = _a.testIdInput, testIdPrev = _a.testIdPrev, testIdNext = _a.testIdNext, testIdPreviewPrefix = _a.testIdPreviewPrefix, testIdImagePrefix = _a.testIdImagePrefix, testIdRemovePrefix = _a.testIdRemovePrefix, testIdCount = _a.testIdCount, testIdEmptyIcon = _a.testIdEmptyIcon, testIdEmptyTitle = _a.testIdEmptyTitle, testIdEmptySubtitle = _a.testIdEmptySubtitle, testIdEmptyNote = _a.testIdEmptyNote, testIdMessage = _a.testIdMessage;
67
- // Local files selected by user
68
- var _e = (0, react_1.useState)([]), localFiles = _e[0], setLocalFiles = _e[1];
69
- // Track object URLs created for local files
70
- var localPreviewsRef = (0, react_1.useRef)(new Map());
71
- // State for resize dialog
72
- var _f = (0, react_1.useState)(false), resizeDialogOpen = _f[0], setResizeDialogOpen = _f[1];
73
- var _g = (0, react_1.useState)(null), pendingFile = _g[0], setPendingFile = _g[1];
74
- var _h = (0, react_1.useState)(""), pendingFileUrl = _h[0], setPendingFileUrl = _h[1];
75
- // Create and manage object URLs for local files
76
- (0, react_1.useEffect)(function () {
77
- var map = localPreviewsRef.current;
78
- // Create URLs for new files
79
- localFiles.forEach(function (file) {
80
- if (!map.has(file)) {
81
- try {
82
- var url = URL.createObjectURL(file);
83
- map.set(file, url);
84
- }
85
- catch (error) {
86
- console.error("Failed to create object URL:", error);
87
- }
88
- }
89
- });
90
- // Clean up URLs for removed files
91
- var currentFiles = new Set(localFiles);
92
- Array.from(map.entries()).forEach(function (_a) {
93
- var file = _a[0], url = _a[1];
94
- if (!currentFiles.has(file)) {
95
- URL.revokeObjectURL(url);
96
- map.delete(file);
97
- }
98
- });
99
- }, [localFiles]);
100
- // Cleanup on unmount
101
- (0, react_1.useEffect)(function () {
102
- var map = localPreviewsRef.current;
103
- return function () {
104
- Array.from(map.values()).forEach(function (url) {
105
- URL.revokeObjectURL(url);
106
- });
107
- map.clear();
108
- };
109
- }, []);
110
- // Cleanup pending file URL when dialog closes
111
- (0, react_1.useEffect)(function () {
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
- }, {});
137
- }
138
- // Already in object format
139
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
- return accept;
141
- }, [accept]);
142
- var dropzoneOptions = {
143
- accept: normalizedAccept,
144
- maxFiles: maxFiles,
145
- maxSize: maxSize,
146
- minSize: minSize,
147
- disabled: disabled,
148
- onDrop: (0, react_1.useCallback)(function (acceptedFiles) {
149
- if (resize && acceptedFiles.length > 0) {
150
- // If resize is enabled, open the resize dialog with the first file
151
- var file = acceptedFiles[0];
152
- var url = URL.createObjectURL(file);
153
- setPendingFile(file);
154
- setPendingFileUrl(url);
155
- setResizeDialogOpen(true);
156
- }
157
- else {
158
- // If resize is disabled, add files directly
159
- setLocalFiles(function (prev) {
160
- var combined = __spreadArray(__spreadArray([], prev, true), acceptedFiles, true);
161
- var limited = maxFiles ? combined.slice(0, maxFiles) : combined;
162
- onChange === null || onChange === void 0 ? void 0 : onChange(limited);
163
- return limited;
164
- });
165
- }
166
- }, [maxFiles, onChange, resize]),
167
- };
168
- var _j = (0, react_dropzone_1.useDropzone)(dropzoneOptions), getRootProps = _j.getRootProps, getInputProps = _j.getInputProps, isDragActive = _j.isDragActive;
169
- // Remove remote URL
170
- var handleRemoveRemote = (0, react_1.useCallback)(function (url) {
171
- onRemoveRemote === null || onRemoveRemote === void 0 ? void 0 : onRemoveRemote(url);
172
- }, [onRemoveRemote]);
173
- // Remove local file
174
- var handleRemoveLocal = (0, react_1.useCallback)(function (index) {
175
- setLocalFiles(function (prev) {
176
- var updated = prev.filter(function (_, i) { return i !== index; });
177
- onChange === null || onChange === void 0 ? void 0 : onChange(updated);
178
- return updated;
179
- });
180
- }, [onChange]);
181
- // Handle crop completion from resize dialog
182
- var handleCropComplete = (0, react_1.useCallback)(function (croppedImage) {
183
- setLocalFiles(function (prev) {
184
- var combined = __spreadArray(__spreadArray([], prev, true), [croppedImage], false);
185
- var limited = maxFiles ? combined.slice(0, maxFiles) : combined;
186
- onChange === null || onChange === void 0 ? void 0 : onChange(limited);
187
- return limited;
188
- });
189
- }, [maxFiles, onChange]);
190
- // Handle resize dialog close
191
- var handleResizeDialogClose = (0, react_1.useCallback)(function () {
192
- setResizeDialogOpen(false);
193
- // Cleanup will happen via the pendingFileUrl useEffect
194
- setPendingFileUrl("");
195
- setPendingFile(null);
196
- }, []);
197
- // Get all preview URLs (remote + local)
198
- var allPreviews = __spreadArray(__spreadArray([], value.map(function (url) { return ({ type: "remote", url: url, index: 0 }); }), true), localFiles.map(function (file, index) { return ({
199
- type: "local",
200
- url: localPreviewsRef.current.get(file) || "",
201
- index: index,
202
- }); }), true);
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 },
229
- label && (react_1.default.createElement("label", { className: "mb-2 block text-sm font-medium" }, label)),
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") }),
231
- react_1.default.createElement("input", __assign({}, getInputProps(), { id: id, "data-testid": testIdInput })),
232
- isImageOnly && allPreviews.length > 0 ? (react_1.default.createElement("div", { className: "flex flex-col items-center w-full" },
233
- react_1.default.createElement("div", { className: "relative w-full max-w-md" },
234
- react_1.default.createElement(carousel_1.Carousel, { className: "w-full" },
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 }),
236
- react_1.default.createElement(carousel_1.CarouselNext, { type: "button", onClick: function (e) { return e.stopPropagation(); }, onPointerDown: function (e) { return e.stopPropagation(); }, onMouseDown: function (e) { return e.stopPropagation(); }, "data-testid": testIdNext }),
237
- react_1.default.createElement(carousel_1.CarouselContent, { className: "ml-0" }, allPreviews.map(function (preview, idx) { return (react_1.default.createElement(carousel_1.CarouselItem, { key: "".concat(preview.type, "-").concat(preview.url, "-").concat(idx), className: "pl-4", "data-testid": "".concat(testIdPreviewPrefix !== null && testIdPreviewPrefix !== void 0 ? testIdPreviewPrefix : "dropzone-preview", "-").concat(idx) },
238
- react_1.default.createElement("div", { className: "relative aspect-square w-full max-w-xs mx-auto" }, preview.url ? (react_1.default.createElement(react_1.default.Fragment, null,
239
- react_1.default.createElement("img", { src: preview.url, alt: "Preview ".concat(idx + 1), className: "h-full w-full rounded-lg object-cover", onClick: function (e) { return e.stopPropagation(); }, "data-testid": "".concat(testIdImagePrefix !== null && testIdImagePrefix !== void 0 ? testIdImagePrefix : "dropzone-image", "-").concat(idx) }),
240
- react_1.default.createElement(button_1.Button, { type: "button", size: "icon", variant: "destructive", onClick: function (e) {
241
- e.stopPropagation();
242
- if (preview.type === "remote") {
243
- handleRemoveRemote(preview.url);
244
- }
245
- else {
246
- handleRemoveLocal(preview.index);
247
- }
248
- }, className: "absolute right-2 top-2 h-8 w-8 rounded-full", "aria-label": "Remove image", "data-testid": "".concat(testIdRemovePrefix !== null && testIdRemovePrefix !== void 0 ? testIdRemovePrefix : "dropzone-remove", "-").concat(idx) },
249
- react_1.default.createElement(lucide_react_1.XIcon, { className: "h-4 w-4" })))) : (react_1.default.createElement("div", { className: "flex h-full w-full items-center justify-center rounded-lg bg-muted text-muted-foreground" },
250
- react_1.default.createElement("span", { className: "text-sm" }, "Loading...")))))); })))),
251
- react_1.default.createElement("p", { className: "mt-4 text-sm font-medium text-muted-foreground", "data-testid": testIdCount },
252
- allPreviews.length,
253
- " image",
254
- allPreviews.length !== 1 ? "s" : "",
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
- " ",
281
- "selected"))) : (react_1.default.createElement("div", { className: "flex flex-col items-center justify-center gap-2" },
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 },
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" },
284
- react_1.default.createElement("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
285
- react_1.default.createElement("circle", { cx: "9", cy: "9", r: "2" }),
286
- react_1.default.createElement("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" }))),
287
- react_1.default.createElement("div", { className: "space-y-1 text-center" },
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")),
291
- react_1.default.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptySubtitle }, "Drag and drop or click to browse"),
292
- (maxFiles > 1 || minSize || maxSize) && (react_1.default.createElement("p", { className: "text-muted-foreground text-xs", "data-testid": testIdEmptyNote }, getConstraintText())))))),
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)),
294
- resize && pendingFile && (react_1.default.createElement(image_resize_dialog_1.ImageResizeDialog, { open: resizeDialogOpen, onClose: handleResizeDialogClose, imageUrl: pendingFileUrl, onCropComplete: handleCropComplete, fileName: pendingFile.name }))));
295
- };
296
- exports.EnhancedDropzone = EnhancedDropzone;