@giddaa-housing/ui 0.1.0 → 1.1.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 (315) hide show
  1. package/README.md +18 -6
  2. package/css/base.css +37 -0
  3. package/css/components.css +135 -0
  4. package/css/generated/shared.css +1 -0
  5. package/css/giddaa.css +402 -0
  6. package/css/preset.css +77 -0
  7. package/css/styles.css +2 -0
  8. package/css/theme.css +255 -0
  9. package/dist/accordion.d.ts +9 -10
  10. package/dist/accordion.js +41 -117
  11. package/dist/activity-timeline.d.ts +29 -0
  12. package/dist/activity-timeline.js +97 -0
  13. package/dist/alert.d.ts +8 -9
  14. package/dist/alert.js +53 -141
  15. package/dist/auth-layout.d.ts +23 -0
  16. package/dist/auth-layout.js +153 -0
  17. package/dist/avatar.d.ts +6 -6
  18. package/dist/avatar.js +40 -142
  19. package/dist/badge.d.ts +12 -13
  20. package/dist/badge.js +66 -114
  21. package/dist/bar-chart.d.ts +136 -114
  22. package/dist/bar-chart.js +244 -297
  23. package/dist/breadcrumb.d.ts +5 -5
  24. package/dist/breadcrumb.js +56 -148
  25. package/dist/bullet-chart.d.ts +33 -33
  26. package/dist/bullet-chart.js +123 -183
  27. package/dist/button-BZIeyUw6.d.ts +17 -0
  28. package/dist/button-group.d.ts +13 -16
  29. package/dist/button-group.js +38 -163
  30. package/dist/button.d.ts +2 -19
  31. package/dist/button.js +90 -142
  32. package/dist/calendar.d.ts +6 -6
  33. package/dist/calendar.js +134 -288
  34. package/dist/card.d.ts +14 -16
  35. package/dist/card.js +58 -174
  36. package/dist/carousel.d.ts +34 -35
  37. package/dist/carousel.js +319 -378
  38. package/dist/chart.d.ts +58 -59
  39. package/dist/chart.js +350 -466
  40. package/dist/checkbox.d.ts +10 -12
  41. package/dist/checkbox.js +84 -84
  42. package/dist/cn-BI_4DMBf.js +23 -0
  43. package/dist/collapsible.d.ts +7 -8
  44. package/dist/collapsible.js +23 -77
  45. package/dist/combobox.d.ts +10 -10
  46. package/dist/combobox.js +220 -696
  47. package/dist/cta.d.ts +64 -0
  48. package/dist/cta.js +192 -0
  49. package/dist/currency-selector.d.ts +41 -0
  50. package/dist/currency-selector.js +114 -0
  51. package/dist/data-table.d.ts +164 -0
  52. package/dist/data-table.js +673 -0
  53. package/dist/date-picker.d.ts +27 -20
  54. package/dist/date-picker.js +122 -520
  55. package/dist/details-card.d.ts +19 -0
  56. package/dist/details-card.js +118 -0
  57. package/dist/dialog.d.ts +13 -14
  58. package/dist/dialog.js +117 -320
  59. package/dist/dropdown-menu.d.ts +14 -14
  60. package/dist/dropdown-menu.js +170 -365
  61. package/dist/dropzone.d.ts +55 -55
  62. package/dist/dropzone.js +328 -467
  63. package/dist/editorial-card.d.ts +27 -0
  64. package/dist/editorial-card.js +144 -0
  65. package/dist/empty-state.d.ts +8 -9
  66. package/dist/empty-state.js +48 -135
  67. package/dist/field.d.ts +23 -25
  68. package/dist/field.js +111 -327
  69. package/dist/filter.d.ts +118 -0
  70. package/dist/filter.js +775 -0
  71. package/dist/footer.d.ts +40 -0
  72. package/dist/footer.js +227 -0
  73. package/dist/funnel-chart.d.ts +38 -41
  74. package/dist/funnel-chart.js +168 -563
  75. package/dist/gallery-indicator.d.ts +17 -0
  76. package/dist/gallery-indicator.js +75 -0
  77. package/dist/heatmap-chart.d.ts +95 -0
  78. package/dist/heatmap-chart.js +197 -0
  79. package/dist/infotip.d.ts +10 -0
  80. package/dist/infotip.js +40 -0
  81. package/dist/input-CfoEJ8A6.d.ts +12 -0
  82. package/dist/input-group.d.ts +16 -19
  83. package/dist/input-group.js +92 -372
  84. package/dist/input-size-context-C-SFx9sx.d.ts +10 -0
  85. package/dist/input-size-context.d.ts +2 -11
  86. package/dist/input-size-context.js +14 -20
  87. package/dist/input.d.ts +2 -13
  88. package/dist/input.js +23 -79
  89. package/dist/item.d.ts +7 -8
  90. package/dist/item.js +22 -89
  91. package/dist/kpi-card.d.ts +18 -20
  92. package/dist/kpi-card.js +84 -232
  93. package/dist/label.d.ts +6 -6
  94. package/dist/label.js +39 -66
  95. package/dist/line-chart.d.ts +88 -89
  96. package/dist/line-chart.js +143 -262
  97. package/dist/logo.d.ts +4 -4
  98. package/dist/logo.js +87 -141
  99. package/dist/map-indicator.d.ts +13 -0
  100. package/dist/map-indicator.js +83 -0
  101. package/dist/match.d.ts +15 -0
  102. package/dist/match.js +68 -0
  103. package/dist/media-gallery-hero.d.ts +58 -0
  104. package/dist/media-gallery-hero.js +201 -0
  105. package/dist/media-player.d.ts +60 -0
  106. package/dist/media-player.js +919 -0
  107. package/dist/multi-step-form.d.ts +56 -0
  108. package/dist/multi-step-form.js +342 -0
  109. package/dist/number-input.d.ts +34 -34
  110. package/dist/number-input.js +62 -413
  111. package/dist/page-header.d.ts +10 -11
  112. package/dist/page-header.js +34 -107
  113. package/dist/pagination.d.ts +17 -20
  114. package/dist/pagination.js +132 -341
  115. package/dist/password-input.d.ts +5 -12
  116. package/dist/password-input.js +32 -354
  117. package/dist/phone-input.d.ts +19 -19
  118. package/dist/phone-input.js +232 -561
  119. package/dist/photo-gallery.d.ts +20 -20
  120. package/dist/photo-gallery.js +314 -746
  121. package/dist/picker-lzgmsNfG.js +32 -0
  122. package/dist/pie-chart.d.ts +60 -60
  123. package/dist/pie-chart.js +141 -184
  124. package/dist/popover.d.ts +9 -9
  125. package/dist/popover.js +60 -156
  126. package/dist/price-tag.d.ts +34 -0
  127. package/dist/price-tag.js +118 -0
  128. package/dist/progress.d.ts +18 -16
  129. package/dist/progress.js +156 -300
  130. package/dist/property-listing-card.d.ts +29 -0
  131. package/dist/property-listing-card.js +117 -0
  132. package/dist/radar-chart.d.ts +108 -0
  133. package/dist/radar-chart.js +172 -0
  134. package/dist/radio-group.d.ts +11 -13
  135. package/dist/radio-group.js +47 -96
  136. package/dist/review-block.d.ts +18 -0
  137. package/dist/review-block.js +154 -0
  138. package/dist/rich-text-editor.d.ts +30 -14
  139. package/dist/rich-text-editor.js +594 -979
  140. package/dist/scatter-chart.d.ts +140 -0
  141. package/dist/scatter-chart.js +248 -0
  142. package/dist/search-input.d.ts +8 -15
  143. package/dist/search-input.js +58 -408
  144. package/dist/select.d.ts +8 -8
  145. package/dist/select.js +131 -291
  146. package/dist/separator.d.ts +8 -9
  147. package/dist/separator.js +31 -70
  148. package/dist/sheet.d.ts +11 -11
  149. package/dist/sheet.js +84 -262
  150. package/dist/sidebar.d.ts +32 -37
  151. package/dist/sidebar.js +380 -1055
  152. package/dist/size-context-D4mYvcg8.d.ts +10 -0
  153. package/dist/size-context.d.ts +2 -11
  154. package/dist/size-context.js +14 -18
  155. package/dist/skeleton.d.ts +4 -5
  156. package/dist/skeleton.js +9 -45
  157. package/dist/slider.d.ts +27 -0
  158. package/dist/slider.js +81 -0
  159. package/dist/sparkline.d.ts +43 -46
  160. package/dist/sparkline.js +223 -561
  161. package/dist/stepper.d.ts +23 -24
  162. package/dist/stepper.js +149 -276
  163. package/dist/styles.css +9995 -2
  164. package/dist/switch.d.ts +10 -12
  165. package/dist/switch.js +49 -106
  166. package/dist/table-card.d.ts +35 -0
  167. package/dist/table-card.js +125 -0
  168. package/dist/table.d.ts +18 -14
  169. package/dist/table.js +114 -182
  170. package/dist/tabs.d.ts +23 -15
  171. package/dist/tabs.js +62 -149
  172. package/dist/tag.d.ts +22 -23
  173. package/dist/tag.js +104 -156
  174. package/dist/testimonial-block.d.ts +33 -0
  175. package/dist/testimonial-block.js +208 -0
  176. package/dist/textarea-IS_D8pXf.d.ts +11 -0
  177. package/dist/textarea.d.ts +2 -12
  178. package/dist/textarea.js +20 -74
  179. package/dist/time-picker.d.ts +11 -11
  180. package/dist/time-picker.js +247 -464
  181. package/dist/toast.d.ts +16 -18
  182. package/dist/toast.js +154 -310
  183. package/dist/tooltip.d.ts +9 -10
  184. package/dist/tooltip.js +33 -91
  185. package/dist/tree-map.d.ts +54 -57
  186. package/dist/tree-map.js +231 -646
  187. package/dist/use-uncontrolled-CLfM1XbB.js +21 -0
  188. package/dist/waterfall-chart.d.ts +126 -0
  189. package/dist/waterfall-chart.js +480 -0
  190. package/package.json +123 -34
  191. package/dist/accordion.js.map +0 -1
  192. package/dist/alert.js.map +0 -1
  193. package/dist/avatar.js.map +0 -1
  194. package/dist/badge.js.map +0 -1
  195. package/dist/bar-chart.js.map +0 -1
  196. package/dist/base.css +0 -32
  197. package/dist/breadcrumb.js.map +0 -1
  198. package/dist/bullet-chart.js.map +0 -1
  199. package/dist/button-group.js.map +0 -1
  200. package/dist/button.js.map +0 -1
  201. package/dist/calendar.js.map +0 -1
  202. package/dist/card.js.map +0 -1
  203. package/dist/carousel.js.map +0 -1
  204. package/dist/chart.js.map +0 -1
  205. package/dist/checkbox.js.map +0 -1
  206. package/dist/collapsible.js.map +0 -1
  207. package/dist/combobox.js.map +0 -1
  208. package/dist/date-picker.js.map +0 -1
  209. package/dist/dialog.js.map +0 -1
  210. package/dist/dropdown-menu.js.map +0 -1
  211. package/dist/dropzone.js.map +0 -1
  212. package/dist/empty-state.js.map +0 -1
  213. package/dist/field.js.map +0 -1
  214. package/dist/files/red-hat-display-latin-300-italic.woff +0 -0
  215. package/dist/files/red-hat-display-latin-300-italic.woff2 +0 -0
  216. package/dist/files/red-hat-display-latin-300-normal.woff +0 -0
  217. package/dist/files/red-hat-display-latin-300-normal.woff2 +0 -0
  218. package/dist/files/red-hat-display-latin-400-italic.woff +0 -0
  219. package/dist/files/red-hat-display-latin-400-italic.woff2 +0 -0
  220. package/dist/files/red-hat-display-latin-400-normal.woff +0 -0
  221. package/dist/files/red-hat-display-latin-400-normal.woff2 +0 -0
  222. package/dist/files/red-hat-display-latin-500-italic.woff +0 -0
  223. package/dist/files/red-hat-display-latin-500-italic.woff2 +0 -0
  224. package/dist/files/red-hat-display-latin-500-normal.woff +0 -0
  225. package/dist/files/red-hat-display-latin-500-normal.woff2 +0 -0
  226. package/dist/files/red-hat-display-latin-600-italic.woff +0 -0
  227. package/dist/files/red-hat-display-latin-600-italic.woff2 +0 -0
  228. package/dist/files/red-hat-display-latin-600-normal.woff +0 -0
  229. package/dist/files/red-hat-display-latin-600-normal.woff2 +0 -0
  230. package/dist/files/red-hat-display-latin-700-italic.woff +0 -0
  231. package/dist/files/red-hat-display-latin-700-italic.woff2 +0 -0
  232. package/dist/files/red-hat-display-latin-700-normal.woff +0 -0
  233. package/dist/files/red-hat-display-latin-700-normal.woff2 +0 -0
  234. package/dist/files/red-hat-display-latin-800-italic.woff +0 -0
  235. package/dist/files/red-hat-display-latin-800-italic.woff2 +0 -0
  236. package/dist/files/red-hat-display-latin-800-normal.woff +0 -0
  237. package/dist/files/red-hat-display-latin-800-normal.woff2 +0 -0
  238. package/dist/files/red-hat-display-latin-900-italic.woff +0 -0
  239. package/dist/files/red-hat-display-latin-900-italic.woff2 +0 -0
  240. package/dist/files/red-hat-display-latin-900-normal.woff +0 -0
  241. package/dist/files/red-hat-display-latin-900-normal.woff2 +0 -0
  242. package/dist/files/red-hat-display-latin-ext-300-italic.woff +0 -0
  243. package/dist/files/red-hat-display-latin-ext-300-italic.woff2 +0 -0
  244. package/dist/files/red-hat-display-latin-ext-300-normal.woff +0 -0
  245. package/dist/files/red-hat-display-latin-ext-300-normal.woff2 +0 -0
  246. package/dist/files/red-hat-display-latin-ext-400-italic.woff +0 -0
  247. package/dist/files/red-hat-display-latin-ext-400-italic.woff2 +0 -0
  248. package/dist/files/red-hat-display-latin-ext-400-normal.woff +0 -0
  249. package/dist/files/red-hat-display-latin-ext-400-normal.woff2 +0 -0
  250. package/dist/files/red-hat-display-latin-ext-500-italic.woff +0 -0
  251. package/dist/files/red-hat-display-latin-ext-500-italic.woff2 +0 -0
  252. package/dist/files/red-hat-display-latin-ext-500-normal.woff +0 -0
  253. package/dist/files/red-hat-display-latin-ext-500-normal.woff2 +0 -0
  254. package/dist/files/red-hat-display-latin-ext-600-italic.woff +0 -0
  255. package/dist/files/red-hat-display-latin-ext-600-italic.woff2 +0 -0
  256. package/dist/files/red-hat-display-latin-ext-600-normal.woff +0 -0
  257. package/dist/files/red-hat-display-latin-ext-600-normal.woff2 +0 -0
  258. package/dist/files/red-hat-display-latin-ext-700-italic.woff +0 -0
  259. package/dist/files/red-hat-display-latin-ext-700-italic.woff2 +0 -0
  260. package/dist/files/red-hat-display-latin-ext-700-normal.woff +0 -0
  261. package/dist/files/red-hat-display-latin-ext-700-normal.woff2 +0 -0
  262. package/dist/files/red-hat-display-latin-ext-800-italic.woff +0 -0
  263. package/dist/files/red-hat-display-latin-ext-800-italic.woff2 +0 -0
  264. package/dist/files/red-hat-display-latin-ext-800-normal.woff +0 -0
  265. package/dist/files/red-hat-display-latin-ext-800-normal.woff2 +0 -0
  266. package/dist/files/red-hat-display-latin-ext-900-italic.woff +0 -0
  267. package/dist/files/red-hat-display-latin-ext-900-italic.woff2 +0 -0
  268. package/dist/files/red-hat-display-latin-ext-900-normal.woff +0 -0
  269. package/dist/files/red-hat-display-latin-ext-900-normal.woff2 +0 -0
  270. package/dist/funnel-chart.js.map +0 -1
  271. package/dist/index.d.ts +0 -87
  272. package/dist/index.js +0 -10448
  273. package/dist/index.js.map +0 -1
  274. package/dist/input-group.js.map +0 -1
  275. package/dist/input-size-context.js.map +0 -1
  276. package/dist/input.js.map +0 -1
  277. package/dist/item.js.map +0 -1
  278. package/dist/kpi-card.js.map +0 -1
  279. package/dist/label.js.map +0 -1
  280. package/dist/line-chart.js.map +0 -1
  281. package/dist/logo.js.map +0 -1
  282. package/dist/number-input.js.map +0 -1
  283. package/dist/page-header.js.map +0 -1
  284. package/dist/pagination.js.map +0 -1
  285. package/dist/password-input.js.map +0 -1
  286. package/dist/phone-input.js.map +0 -1
  287. package/dist/photo-gallery.js.map +0 -1
  288. package/dist/pie-chart.js.map +0 -1
  289. package/dist/popover.js.map +0 -1
  290. package/dist/progress.js.map +0 -1
  291. package/dist/radio-group.js.map +0 -1
  292. package/dist/red-hat-display.css +0 -2
  293. package/dist/rich-text-editor.js.map +0 -1
  294. package/dist/search-input.js.map +0 -1
  295. package/dist/select.js.map +0 -1
  296. package/dist/separator.js.map +0 -1
  297. package/dist/sheet.js.map +0 -1
  298. package/dist/sidebar.js.map +0 -1
  299. package/dist/size-context.js.map +0 -1
  300. package/dist/skeleton.js.map +0 -1
  301. package/dist/sparkline.js.map +0 -1
  302. package/dist/stepper.js.map +0 -1
  303. package/dist/switch.js.map +0 -1
  304. package/dist/tab-list.d.ts +0 -38
  305. package/dist/tab-list.js +0 -394
  306. package/dist/tab-list.js.map +0 -1
  307. package/dist/table.js.map +0 -1
  308. package/dist/tabs.js.map +0 -1
  309. package/dist/tag.js.map +0 -1
  310. package/dist/textarea.js.map +0 -1
  311. package/dist/theme/giddaa.css +0 -2
  312. package/dist/time-picker.js.map +0 -1
  313. package/dist/toast.js.map +0 -1
  314. package/dist/tooltip.js.map +0 -1
  315. package/dist/tree-map.js.map +0 -1
package/dist/dropzone.js CHANGED
@@ -1,490 +1,351 @@
1
- // src/components/ui/dropzone.tsx
1
+ "use client";
2
+ import { t as cn } from "./cn-BI_4DMBf.js";
2
3
  import { UploadCloud } from "lucide-react";
3
- import {
4
- createContext,
5
- use
6
- } from "react";
7
-
8
- // src/lib/cn.ts
9
- import { clsx } from "clsx";
10
- import { extendTailwindMerge } from "tailwind-merge";
11
- var twMerge = extendTailwindMerge({
12
- extend: {
13
- classGroups: {
14
- "font-size": [
15
- {
16
- text: [
17
- "gdt-display",
18
- "gdt-h1",
19
- "gdt-h2",
20
- "gdt-h3",
21
- "gdt-h4",
22
- "gdt-h5",
23
- "gdt-xl",
24
- "gdt-lg",
25
- "gdt-md",
26
- "gdt-sm",
27
- "gdt-xs",
28
- "gdt-subtext",
29
- "gdt-capitalized"
30
- ]
31
- }
32
- ]
33
- }
34
- }
35
- });
36
- function cn(...inputs) {
37
- return twMerge(clsx(inputs));
38
- }
39
-
40
- // src/lib/use-file-picker.ts
41
- import {
42
- useCallback,
43
- useRef,
44
- useState
45
- } from "react";
46
- function useFilePicker({
47
- accept = "*",
48
- disabled = false,
49
- maxFiles = Number.POSITIVE_INFINITY,
50
- maxSize = Number.POSITIVE_INFINITY,
51
- multiple = false,
52
- onFilesAccepted,
53
- onFilesChange,
54
- onFilesRejected
55
- } = {}) {
56
- const inputRef = useRef(null);
57
- const dragDepthRef = useRef(0);
58
- const [isDragging, setIsDragging] = useState(false);
59
- const pickFiles = useCallback(
60
- (files) => {
61
- const incomingFiles = Array.from(files);
62
- const limit = multiple ? maxFiles : 1;
63
- const acceptedFiles = [];
64
- const rejectedFiles = [];
65
- if (incomingFiles.length === 0 || disabled) {
66
- return { acceptedFiles, rejectedFiles };
67
- }
68
- for (const [index, file] of incomingFiles.entries()) {
69
- const errors = [];
70
- if (index >= limit) {
71
- errors.push({
72
- code: "too-many-files",
73
- message: multiple ? `You can only select up to ${limit} files.` : "You can only select one file."
74
- });
75
- } else {
76
- errors.push(...validateFile(file, { accept, maxSize }));
77
- }
78
- if (errors.length > 0) {
79
- rejectedFiles.push({ file, errors });
80
- continue;
81
- }
82
- acceptedFiles.push(file);
83
- }
84
- const result = { acceptedFiles, rejectedFiles };
85
- if (acceptedFiles.length > 0) {
86
- onFilesAccepted?.(acceptedFiles);
87
- }
88
- if (rejectedFiles.length > 0) {
89
- onFilesRejected?.(rejectedFiles);
90
- }
91
- onFilesChange?.(result);
92
- if (inputRef.current) {
93
- inputRef.current.value = "";
94
- }
95
- return result;
96
- },
97
- [
98
- accept,
99
- disabled,
100
- maxFiles,
101
- maxSize,
102
- multiple,
103
- onFilesAccepted,
104
- onFilesChange,
105
- onFilesRejected
106
- ]
107
- );
108
- const handleFileChange = useCallback(
109
- (event) => {
110
- if (event.target.files) {
111
- pickFiles(event.target.files);
112
- }
113
- },
114
- [pickFiles]
115
- );
116
- const openFileDialog = useCallback(() => {
117
- if (disabled) return;
118
- inputRef.current?.click();
119
- }, [disabled]);
120
- const handleDragEnter = useCallback(
121
- (event) => {
122
- event.preventDefault();
123
- event.stopPropagation();
124
- if (disabled) return;
125
- dragDepthRef.current += 1;
126
- setIsDragging(true);
127
- },
128
- [disabled]
129
- );
130
- const handleDragLeave = useCallback(
131
- (event) => {
132
- event.preventDefault();
133
- event.stopPropagation();
134
- if (disabled) return;
135
- dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);
136
- if (dragDepthRef.current === 0) {
137
- setIsDragging(false);
138
- }
139
- },
140
- [disabled]
141
- );
142
- const handleDragOver = useCallback((event) => {
143
- event.preventDefault();
144
- event.stopPropagation();
145
- }, []);
146
- const handleDrop = useCallback(
147
- (event) => {
148
- event.preventDefault();
149
- event.stopPropagation();
150
- dragDepthRef.current = 0;
151
- setIsDragging(false);
152
- if (event.dataTransfer.files.length > 0) {
153
- pickFiles(event.dataTransfer.files);
154
- }
155
- },
156
- [pickFiles]
157
- );
158
- const getInputProps = useCallback(
159
- (props = {}) => {
160
- const { onChange, ref, ...inputProps } = props;
161
- return {
162
- ...inputProps,
163
- type: "file",
164
- accept: inputProps.accept ?? accept,
165
- disabled: inputProps.disabled ?? disabled,
166
- multiple: inputProps.multiple ?? multiple,
167
- ref: (node) => {
168
- inputRef.current = node;
169
- assignRef(ref, node);
170
- },
171
- onChange: (event) => {
172
- onChange?.(event);
173
- if (!event.defaultPrevented) {
174
- handleFileChange(event);
175
- }
176
- }
177
- };
178
- },
179
- [accept, disabled, handleFileChange, multiple]
180
- );
181
- return {
182
- getInputProps,
183
- handleDragEnter,
184
- handleDragLeave,
185
- handleDragOver,
186
- handleDrop,
187
- handleFileChange,
188
- inputRef,
189
- isDragging,
190
- openFileDialog,
191
- pickFiles
192
- };
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { createContext, use, useCallback, useRef, useState } from "react";
6
+ //#region src/lib/use-file-picker.ts
7
+ function useFilePicker({ accept = "*", disabled = false, maxFiles = Number.POSITIVE_INFINITY, maxSize = Number.POSITIVE_INFINITY, multiple = false, onFilesAccepted, onFilesChange, onFilesRejected } = {}) {
8
+ const inputRef = useRef(null);
9
+ const dragDepthRef = useRef(0);
10
+ const [isDragging, setIsDragging] = useState(false);
11
+ const pickFiles = useCallback((files) => {
12
+ const incomingFiles = Array.from(files);
13
+ const limit = multiple ? maxFiles : 1;
14
+ const acceptedFiles = [];
15
+ const rejectedFiles = [];
16
+ if (incomingFiles.length === 0 || disabled) return {
17
+ acceptedFiles,
18
+ rejectedFiles
19
+ };
20
+ for (const [index, file] of incomingFiles.entries()) {
21
+ const errors = [];
22
+ if (index >= limit) errors.push({
23
+ code: "too-many-files",
24
+ message: multiple ? `You can only select up to ${limit} files.` : "You can only select one file."
25
+ });
26
+ else errors.push(...validateFile(file, {
27
+ accept,
28
+ maxSize
29
+ }));
30
+ if (errors.length > 0) {
31
+ rejectedFiles.push({
32
+ file,
33
+ errors
34
+ });
35
+ continue;
36
+ }
37
+ acceptedFiles.push(file);
38
+ }
39
+ const result = {
40
+ acceptedFiles,
41
+ rejectedFiles
42
+ };
43
+ if (acceptedFiles.length > 0) onFilesAccepted?.(acceptedFiles);
44
+ if (rejectedFiles.length > 0) onFilesRejected?.(rejectedFiles);
45
+ onFilesChange?.(result);
46
+ if (inputRef.current) inputRef.current.value = "";
47
+ return result;
48
+ }, [
49
+ accept,
50
+ disabled,
51
+ maxFiles,
52
+ maxSize,
53
+ multiple,
54
+ onFilesAccepted,
55
+ onFilesChange,
56
+ onFilesRejected
57
+ ]);
58
+ const handleFileChange = useCallback((event) => {
59
+ if (event.target.files) pickFiles(event.target.files);
60
+ }, [pickFiles]);
61
+ const openFileDialog = useCallback(() => {
62
+ if (disabled) return;
63
+ inputRef.current?.click();
64
+ }, [disabled]);
65
+ const handleDragEnter = useCallback((event) => {
66
+ event.preventDefault();
67
+ event.stopPropagation();
68
+ if (disabled) return;
69
+ dragDepthRef.current += 1;
70
+ setIsDragging(true);
71
+ }, [disabled]);
72
+ const handleDragLeave = useCallback((event) => {
73
+ event.preventDefault();
74
+ event.stopPropagation();
75
+ if (disabled) return;
76
+ dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);
77
+ if (dragDepthRef.current === 0) setIsDragging(false);
78
+ }, [disabled]);
79
+ const handleDragOver = useCallback((event) => {
80
+ event.preventDefault();
81
+ event.stopPropagation();
82
+ }, []);
83
+ const handleDrop = useCallback((event) => {
84
+ event.preventDefault();
85
+ event.stopPropagation();
86
+ dragDepthRef.current = 0;
87
+ setIsDragging(false);
88
+ if (event.dataTransfer.files.length > 0) pickFiles(event.dataTransfer.files);
89
+ }, [pickFiles]);
90
+ return {
91
+ getInputProps: useCallback((props = {}) => {
92
+ const { onChange, ref, ...inputProps } = props;
93
+ return {
94
+ ...inputProps,
95
+ type: "file",
96
+ accept: inputProps.accept ?? accept,
97
+ disabled: inputProps.disabled ?? disabled,
98
+ multiple: inputProps.multiple ?? multiple,
99
+ ref: (node) => {
100
+ inputRef.current = node;
101
+ assignRef(ref, node);
102
+ },
103
+ onChange: (event) => {
104
+ onChange?.(event);
105
+ if (!event.defaultPrevented) handleFileChange(event);
106
+ }
107
+ };
108
+ }, [
109
+ accept,
110
+ disabled,
111
+ handleFileChange,
112
+ multiple
113
+ ]),
114
+ handleDragEnter,
115
+ handleDragLeave,
116
+ handleDragOver,
117
+ handleDrop,
118
+ handleFileChange,
119
+ inputRef,
120
+ isDragging,
121
+ openFileDialog,
122
+ pickFiles
123
+ };
193
124
  }
194
125
  function validateFile(file, options = {}) {
195
- const { accept = "*", maxSize = Number.POSITIVE_INFINITY } = options;
196
- const errors = [];
197
- if (file.size > maxSize) {
198
- errors.push({
199
- code: "file-too-large",
200
- message: `File "${file.name}" exceeds the maximum size of ${formatBytes(maxSize)}.`
201
- });
202
- }
203
- if (!isAcceptedFile(file, accept)) {
204
- errors.push({
205
- code: "file-invalid-type",
206
- message: `File "${file.name}" is not an accepted file type.`
207
- });
208
- }
209
- return errors;
126
+ const { accept = "*", maxSize = Number.POSITIVE_INFINITY } = options;
127
+ const errors = [];
128
+ if (file.size > maxSize) errors.push({
129
+ code: "file-too-large",
130
+ message: `File "${file.name}" exceeds the maximum size of ${formatBytes(maxSize)}.`
131
+ });
132
+ if (!isAcceptedFile(file, accept)) errors.push({
133
+ code: "file-invalid-type",
134
+ message: `File "${file.name}" is not an accepted file type.`
135
+ });
136
+ return errors;
210
137
  }
211
138
  function isAcceptedFile(file, accept = "*") {
212
- if (accept === "*" || accept.trim() === "") return true;
213
- const acceptedTypes = accept.split(",").map((type) => type.trim().toLowerCase()).filter(Boolean);
214
- if (acceptedTypes.length === 0) return true;
215
- const fileType = file.type.toLowerCase();
216
- const fileExtension = getFileExtension(file.name);
217
- return acceptedTypes.some((acceptedType) => {
218
- if (acceptedType.startsWith(".")) {
219
- return fileExtension === acceptedType;
220
- }
221
- if (acceptedType.endsWith("/*")) {
222
- return fileType.startsWith(acceptedType.slice(0, -1));
223
- }
224
- return fileType === acceptedType;
225
- });
139
+ if (accept === "*" || accept.trim() === "") return true;
140
+ const acceptedTypes = accept.split(",").map((type) => type.trim().toLowerCase()).filter(Boolean);
141
+ if (acceptedTypes.length === 0) return true;
142
+ const fileType = file.type.toLowerCase();
143
+ const fileExtension = getFileExtension(file.name);
144
+ return acceptedTypes.some((acceptedType) => {
145
+ if (acceptedType.startsWith(".")) return fileExtension === acceptedType;
146
+ if (acceptedType.endsWith("/*")) return fileType.startsWith(acceptedType.slice(0, -1));
147
+ return fileType === acceptedType;
148
+ });
226
149
  }
227
150
  function formatBytes(bytes, decimals = 2) {
228
- if (bytes === 0) return "0 Bytes";
229
- if (!Number.isFinite(bytes)) return "unlimited";
230
- const unit = 1024;
231
- const precision = Math.max(0, decimals);
232
- const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
233
- const sizeIndex = Math.min(
234
- Math.floor(Math.log(bytes) / Math.log(unit)),
235
- sizes.length - 1
236
- );
237
- return `${Number.parseFloat((bytes / unit ** sizeIndex).toFixed(precision))} ${sizes[sizeIndex]}`;
151
+ if (bytes === 0) return "0 Bytes";
152
+ if (!Number.isFinite(bytes)) return "unlimited";
153
+ const unit = 1024;
154
+ const precision = Math.max(0, decimals);
155
+ const sizes = [
156
+ "Bytes",
157
+ "KB",
158
+ "MB",
159
+ "GB",
160
+ "TB",
161
+ "PB"
162
+ ];
163
+ const sizeIndex = Math.min(Math.floor(Math.log(bytes) / Math.log(unit)), sizes.length - 1);
164
+ return `${Number.parseFloat((bytes / unit ** sizeIndex).toFixed(precision))} ${sizes[sizeIndex]}`;
238
165
  }
239
166
  function getFileExtension(fileName) {
240
- const extension = fileName.toLowerCase().split(".").pop();
241
- return extension && extension !== fileName.toLowerCase() ? `.${extension}` : "";
167
+ const extension = fileName.toLowerCase().split(".").pop();
168
+ return extension && extension !== fileName.toLowerCase() ? `.${extension}` : "";
242
169
  }
243
170
  function assignRef(ref, value) {
244
- if (!ref) return;
245
- if (typeof ref === "function") {
246
- ref(value);
247
- return;
248
- }
249
- ref.current = value;
171
+ if (!ref) return;
172
+ if (typeof ref === "function") {
173
+ ref(value);
174
+ return;
175
+ }
176
+ ref.current = value;
250
177
  }
251
-
252
- // src/components/ui/dropzone.tsx
253
- import { jsx, jsxs } from "react/jsx-runtime";
254
- var DropzoneContext = createContext(null);
178
+ //#endregion
179
+ //#region src/dropzone.tsx
180
+ const DropzoneContext = createContext(null);
255
181
  function useDropzoneContext() {
256
- const ctx = use(DropzoneContext);
257
- if (!ctx) {
258
- throw new Error(
259
- "Dropzone compound components must be rendered inside <Dropzone>."
260
- );
261
- }
262
- return ctx;
182
+ const ctx = use(DropzoneContext);
183
+ if (!ctx) throw new Error("Dropzone compound components must be rendered inside <Dropzone>.");
184
+ return ctx;
263
185
  }
264
- function Dropzone({
265
- children,
266
- className,
267
- disabled,
268
- ...options
269
- }) {
270
- const {
271
- isDragging,
272
- openFileDialog,
273
- getInputProps,
274
- handleDragEnter,
275
- handleDragLeave,
276
- handleDragOver,
277
- handleDrop
278
- } = useFilePicker({ ...options, disabled });
279
- return /* @__PURE__ */ jsx(
280
- DropzoneContext.Provider,
281
- {
282
- value: {
283
- isDragging,
284
- isDisabled: disabled ?? false,
285
- openFileDialog,
286
- getInputProps,
287
- handleDragEnter,
288
- handleDragLeave,
289
- handleDragOver,
290
- handleDrop
291
- },
292
- children: /* @__PURE__ */ jsx("div", { "data-slot": "dropzone", className: cn("relative", className), children })
293
- }
294
- );
186
+ /**
187
+ * Context provider that owns the `useFilePicker` instance.
188
+ * Wrap `<DropzoneArea>` and `<DropzoneInput>` (plus any content helpers)
189
+ * inside this component.
190
+ *
191
+ * @example
192
+ * ```tsx
193
+ * <Dropzone accept="image/*" multiple maxFiles={5} onFilesAccepted={handleFiles}>
194
+ * <DropzoneArea>
195
+ * <DropzoneIcon />
196
+ * <DropzoneTitle>Choose a file or drag & drop</DropzoneTitle>
197
+ * <DropzoneDescription>JPEG, PNG – up to 2 MB</DropzoneDescription>
198
+ * <DropzoneTrigger />
199
+ * </DropzoneArea>
200
+ * <DropzoneInput />
201
+ * </Dropzone>
202
+ * ```
203
+ */
204
+ function Dropzone({ children, className, disabled, ...options }) {
205
+ const { isDragging, openFileDialog, getInputProps, handleDragEnter, handleDragLeave, handleDragOver, handleDrop } = useFilePicker({
206
+ ...options,
207
+ disabled
208
+ });
209
+ return /* @__PURE__ */ jsx(DropzoneContext.Provider, {
210
+ value: {
211
+ isDragging,
212
+ isDisabled: disabled ?? false,
213
+ openFileDialog,
214
+ getInputProps,
215
+ handleDragEnter,
216
+ handleDragLeave,
217
+ handleDragOver,
218
+ handleDrop
219
+ },
220
+ children: /* @__PURE__ */ jsx("div", {
221
+ "data-slot": "dropzone",
222
+ className: cn("relative", className),
223
+ children
224
+ })
225
+ });
295
226
  }
296
- function DropzoneArea({
297
- children,
298
- className,
299
- label = "Upload files. Click to browse, or drag and drop files here.",
300
- ...props
301
- }) {
302
- const {
303
- isDragging,
304
- isDisabled,
305
- openFileDialog,
306
- handleDragEnter,
307
- handleDragLeave,
308
- handleDragOver,
309
- handleDrop
310
- } = useDropzoneContext();
311
- function handleKeyDown(e) {
312
- if (e.key === "Enter" || e.key === " ") {
313
- e.preventDefault();
314
- openFileDialog();
315
- }
316
- }
317
- return (
318
- // biome-ignore lint/a11y/useSemanticElements: The dropzone contains block-level compound content and owns drag events.
319
- /* @__PURE__ */ jsxs(
320
- "div",
321
- {
322
- "data-slot": "dropzone-area",
323
- "data-dragging": isDragging ? "true" : void 0,
324
- "data-disabled": isDisabled ? "true" : void 0,
325
- role: "button",
326
- tabIndex: isDisabled ? -1 : 0,
327
- "aria-label": label,
328
- "aria-disabled": isDisabled,
329
- onClick: isDisabled ? void 0 : openFileDialog,
330
- onKeyDown: isDisabled ? void 0 : handleKeyDown,
331
- onDragEnter: isDisabled ? void 0 : handleDragEnter,
332
- onDragLeave: isDisabled ? void 0 : handleDragLeave,
333
- onDragOver: isDisabled ? void 0 : handleDragOver,
334
- onDrop: isDisabled ? void 0 : handleDrop,
335
- className: cn(
336
- // Layout & base
337
- "flex min-h-48 w-full cursor-pointer flex-col items-center justify-center rounded-xl border p-8 text-center transition-colors duration-150 select-none",
338
- // Default colours (design tokens)
339
- "border-line bg-surface-brand-subtle",
340
- // Focus ring – matches other interactive controls in the system
341
- "focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-ring/20 focus-visible:border-ring",
342
- // Dragging variant
343
- "data-[dragging=true]:border-dashed data-[dragging=true]:border-line-focus data-[dragging=true]:bg-surface-brand/10",
344
- // Disabled variant
345
- "data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50",
346
- className
347
- ),
348
- ...props,
349
- children: [
350
- children,
351
- /* @__PURE__ */ jsx("span", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", children: isDragging ? "Drop files to upload." : "" })
352
- ]
353
- }
354
- )
355
- );
227
+ /**
228
+ * The visual, interactive drop zone.
229
+ *
230
+ * - Behaves as a `button` (role, tabIndex, Enter/Space keyboard support).
231
+ * - Announces drag-state changes to screen readers via a live region.
232
+ * - Applies design-token-based styles and reacts to `data-dragging` /
233
+ * `data-disabled` for visual feedback.
234
+ */
235
+ function DropzoneArea({ children, className, label = "Upload files. Click to browse, or drag and drop files here.", ...props }) {
236
+ const { isDragging, isDisabled, openFileDialog, handleDragEnter, handleDragLeave, handleDragOver, handleDrop } = useDropzoneContext();
237
+ function handleKeyDown(e) {
238
+ if (e.key === "Enter" || e.key === " ") {
239
+ e.preventDefault();
240
+ openFileDialog();
241
+ }
242
+ }
243
+ return /* @__PURE__ */ jsxs("div", {
244
+ "data-slot": "dropzone-area",
245
+ "data-dragging": isDragging ? "true" : void 0,
246
+ "data-disabled": isDisabled ? "true" : void 0,
247
+ role: "button",
248
+ tabIndex: isDisabled ? -1 : 0,
249
+ "aria-label": label,
250
+ "aria-disabled": isDisabled,
251
+ onClick: isDisabled ? void 0 : openFileDialog,
252
+ onKeyDown: isDisabled ? void 0 : handleKeyDown,
253
+ onDragEnter: isDisabled ? void 0 : handleDragEnter,
254
+ onDragLeave: isDisabled ? void 0 : handleDragLeave,
255
+ onDragOver: isDisabled ? void 0 : handleDragOver,
256
+ onDrop: isDisabled ? void 0 : handleDrop,
257
+ className: cn("flex min-h-48 w-full cursor-pointer flex-col items-center justify-center rounded-xl border p-8 text-center transition-colors duration-150 select-none", "border-line bg-surface-brand-subtle", "focus-visible:border-line-focus focus-visible:ring-2 focus-visible:ring-line-focus/35 focus-visible:outline-none", "data-[dragging=true]:border-dashed data-[dragging=true]:border-line-focus data-[dragging=true]:bg-surface-brand/10", "data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50", className),
258
+ ...props,
259
+ children: [children, /* @__PURE__ */ jsx("span", {
260
+ className: "sr-only",
261
+ "aria-live": "polite",
262
+ "aria-atomic": "true",
263
+ children: isDragging ? "Drop files to upload." : ""
264
+ })]
265
+ });
356
266
  }
267
+ /**
268
+ * The hidden `<input type="file">` that backs the drop zone.
269
+ * Rendered off-screen (not `display:none`) so it remains accessible.
270
+ * Place it as a sibling to `<DropzoneArea>` inside `<Dropzone>`.
271
+ */
357
272
  function DropzoneInput({ className, ...props }) {
358
- const { getInputProps } = useDropzoneContext();
359
- return /* @__PURE__ */ jsx(
360
- "input",
361
- {
362
- ...getInputProps({
363
- // sr-only keeps it in the a11y tree without being visible
364
- className: cn("sr-only", className),
365
- // Prevent it from receiving keyboard focus independently
366
- tabIndex: -1,
367
- ...props
368
- })
369
- }
370
- );
273
+ const { getInputProps } = useDropzoneContext();
274
+ return /* @__PURE__ */ jsx("input", { ...getInputProps({
275
+ className: cn("sr-only", className),
276
+ tabIndex: -1,
277
+ ...props
278
+ }) });
371
279
  }
372
- function useDropzoneStateLabel({
373
- children,
374
- disabledLabel,
375
- draggingLabel,
376
- idleLabel
377
- }) {
378
- const { isDragging, isDisabled } = useDropzoneContext();
379
- if (isDisabled && disabledLabel !== void 0) {
380
- return disabledLabel;
381
- }
382
- if (isDragging && draggingLabel !== void 0) {
383
- return draggingLabel;
384
- }
385
- return idleLabel ?? children;
280
+ function useDropzoneStateLabel({ children, disabledLabel, draggingLabel, idleLabel }) {
281
+ const { isDragging, isDisabled } = useDropzoneContext();
282
+ if (isDisabled && disabledLabel !== void 0) return disabledLabel;
283
+ if (isDragging && draggingLabel !== void 0) return draggingLabel;
284
+ return idleLabel ?? children;
386
285
  }
387
- function DropzoneIcon({
388
- className,
389
- children,
390
- ...props
391
- }) {
392
- return /* @__PURE__ */ jsx(
393
- "div",
394
- {
395
- "data-slot": "dropzone-icon",
396
- "aria-hidden": "true",
397
- className: cn(
398
- "mb-4 flex size-11 shrink-0 items-center justify-center rounded-full",
399
- // Solid brand fill with an inset ring for depth
400
- "bg-surface-brand text-fg-on-brand ring-1 ring-inset ring-white/10",
401
- className
402
- ),
403
- ...props,
404
- children: children ?? /* @__PURE__ */ jsx(UploadCloud, { className: "size-5" })
405
- }
406
- );
286
+ /**
287
+ * A centred, brand-coloured icon container pre-populated with an upload cloud
288
+ * icon. Pass `children` to swap the icon.
289
+ */
290
+ function DropzoneIcon({ className, children, ...props }) {
291
+ return /* @__PURE__ */ jsx("div", {
292
+ "data-slot": "dropzone-icon",
293
+ "aria-hidden": "true",
294
+ className: cn("mb-4 flex size-11 shrink-0 items-center justify-center rounded-full", "bg-surface-brand text-fg-on-brand ring-1 ring-fg-on-brand/10 ring-inset", className),
295
+ ...props,
296
+ children: children ?? /* @__PURE__ */ jsx(UploadCloud, { className: "size-5" })
297
+ });
407
298
  }
408
- function DropzoneTitle({
409
- className,
410
- children,
411
- disabledLabel,
412
- draggingLabel,
413
- idleLabel,
414
- ...props
415
- }) {
416
- const label = useDropzoneStateLabel({
417
- children,
418
- disabledLabel,
419
- draggingLabel,
420
- idleLabel
421
- });
422
- return /* @__PURE__ */ jsx(
423
- "p",
424
- {
425
- "data-slot": "dropzone-title",
426
- className: cn(
427
- "text-lg font-semibold leading-snug text-fg-primary",
428
- className
429
- ),
430
- ...props,
431
- children: label
432
- }
433
- );
299
+ /**
300
+ * Primary heading text inside the drop zone.
301
+ */
302
+ function DropzoneTitle({ className, children, disabledLabel, draggingLabel, idleLabel, ...props }) {
303
+ const label = useDropzoneStateLabel({
304
+ children,
305
+ disabledLabel,
306
+ draggingLabel,
307
+ idleLabel
308
+ });
309
+ return /* @__PURE__ */ jsx("p", {
310
+ "data-slot": "dropzone-title",
311
+ className: cn("text-gdt-md font-semibold leading-snug text-fg-primary", className),
312
+ ...props,
313
+ children: label
314
+ });
434
315
  }
435
- function DropzoneDescription({
436
- className,
437
- children,
438
- disabledLabel,
439
- draggingLabel,
440
- idleLabel,
441
- ...props
442
- }) {
443
- const label = useDropzoneStateLabel({
444
- children,
445
- disabledLabel,
446
- draggingLabel,
447
- idleLabel
448
- });
449
- return /* @__PURE__ */ jsx(
450
- "p",
451
- {
452
- "data-slot": "dropzone-description",
453
- className: cn("mt-1 text-sm text-fg-muted", className),
454
- ...props,
455
- children: label
456
- }
457
- );
316
+ /**
317
+ * Secondary descriptive text inside the drop zone (file-type hints, size
318
+ * limits, etc.).
319
+ */
320
+ function DropzoneDescription({ className, children, disabledLabel, draggingLabel, idleLabel, ...props }) {
321
+ const label = useDropzoneStateLabel({
322
+ children,
323
+ disabledLabel,
324
+ draggingLabel,
325
+ idleLabel
326
+ });
327
+ return /* @__PURE__ */ jsx("p", {
328
+ "data-slot": "dropzone-description",
329
+ className: cn("mt-1 text-gdt-sm text-fg-secondary", className),
330
+ ...props,
331
+ children: label
332
+ });
458
333
  }
459
- function DropzoneTrigger({
460
- className,
461
- children = "Browse File",
462
- ...props
463
- }) {
464
- return /* @__PURE__ */ jsx(
465
- "span",
466
- {
467
- "data-slot": "dropzone-trigger",
468
- "aria-hidden": "true",
469
- className: cn(
470
- "mt-5 inline-flex h-8 shrink-0 items-center justify-center rounded-lg px-4",
471
- "bg-primary text-sm font-medium text-primary-foreground",
472
- "transition-colors duration-150",
473
- className
474
- ),
475
- ...props,
476
- children
477
- }
478
- );
334
+ /**
335
+ * A decorative "Browse File" pill that visually suggests the click action.
336
+ *
337
+ * This element is `aria-hidden` because the surrounding `<DropzoneArea>`
338
+ * already exposes a labelled `button` role to assistive technologies.
339
+ * The pill is purely a visual affordance.
340
+ */
341
+ function DropzoneTrigger({ className, children = "Browse File", ...props }) {
342
+ return /* @__PURE__ */ jsx("span", {
343
+ "data-slot": "dropzone-trigger",
344
+ "aria-hidden": "true",
345
+ className: cn("mt-5 inline-flex h-8 shrink-0 items-center justify-center rounded-full px-4", "bg-action-primary text-gdt-sm font-medium text-fg-on-brand", "transition-colors duration-150", className),
346
+ ...props,
347
+ children
348
+ });
479
349
  }
480
- export {
481
- Dropzone,
482
- DropzoneArea,
483
- DropzoneDescription,
484
- DropzoneIcon,
485
- DropzoneInput,
486
- DropzoneTitle,
487
- DropzoneTrigger,
488
- useDropzoneContext
489
- };
490
- //# sourceMappingURL=dropzone.js.map
350
+ //#endregion
351
+ export { Dropzone, DropzoneArea, DropzoneDescription, DropzoneIcon, DropzoneInput, DropzoneTitle, DropzoneTrigger, useDropzoneContext };