@aurora-ds/components 0.23.6 → 0.24.1

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 (31) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/forms/file-picker/FilePicker.d.ts +24 -0
  3. package/dist/cjs/components/forms/file-picker/FilePicker.props.d.ts +99 -0
  4. package/dist/cjs/components/forms/file-picker/FilePicker.styles.d.ts +18 -0
  5. package/dist/cjs/components/forms/file-picker/index.d.ts +2 -0
  6. package/dist/cjs/components/forms/image-picker/ImagePicker.d.ts +18 -0
  7. package/dist/cjs/components/forms/image-picker/ImagePicker.props.d.ts +76 -0
  8. package/dist/cjs/components/forms/image-picker/index.d.ts +2 -0
  9. package/dist/cjs/components/index.d.ts +2 -0
  10. package/dist/cjs/index.js +172 -25
  11. package/dist/cjs/index.js.map +1 -1
  12. package/dist/cjs/resources/Icons.d.ts +3 -1
  13. package/dist/cjs/resources/icons/PlusIcon.d.ts +2 -0
  14. package/dist/cjs/resources/icons/TrashIcon.d.ts +2 -0
  15. package/dist/cjs/utils/ui/components/data-display/status/getStatusColorStyles.utils.d.ts +1 -1
  16. package/dist/esm/components/forms/file-picker/FilePicker.d.ts +24 -0
  17. package/dist/esm/components/forms/file-picker/FilePicker.props.d.ts +99 -0
  18. package/dist/esm/components/forms/file-picker/FilePicker.styles.d.ts +18 -0
  19. package/dist/esm/components/forms/file-picker/index.d.ts +2 -0
  20. package/dist/esm/components/forms/image-picker/ImagePicker.d.ts +18 -0
  21. package/dist/esm/components/forms/image-picker/ImagePicker.props.d.ts +76 -0
  22. package/dist/esm/components/forms/image-picker/index.d.ts +2 -0
  23. package/dist/esm/components/index.d.ts +2 -0
  24. package/dist/esm/index.js +171 -26
  25. package/dist/esm/index.js.map +1 -1
  26. package/dist/esm/resources/Icons.d.ts +3 -1
  27. package/dist/esm/resources/icons/PlusIcon.d.ts +2 -0
  28. package/dist/esm/resources/icons/TrashIcon.d.ts +2 -0
  29. package/dist/esm/utils/ui/components/data-display/status/getStatusColorStyles.utils.d.ts +1 -1
  30. package/dist/index.d.ts +216 -3
  31. package/package.json +1 -1
package/README.md CHANGED
@@ -211,7 +211,7 @@ The `defaultTheme` includes these token categories:
211
211
  **Buttons:** Button, IconButton
212
212
  **Layout:** Box, Card, Stack, Grid, Page, PageSection, Separator
213
213
  **Typography:** Text
214
- **Forms:** Input, TextArea, Select, DatePicker, Form
214
+ **Forms:** Input, TextArea, Select, DatePicker, Form, FilePicker, ImagePicker
215
215
  **Data Display:** Status, Avatar, AvatarGroup, Icon
216
216
  **Navigation:** Breadcrumb, Tabs, DrawerItem, Menu, Pagination
217
217
  **Feedback:** Alert, Accordion
@@ -0,0 +1,24 @@
1
+ import { FC } from 'react';
2
+ import { FilePickerProps } from '@components/forms/file-picker/FilePicker.props.ts';
3
+ /**
4
+ * Generic file picker component with drag-and-drop style interface.
5
+ * Supports custom preview, icons, and styling for use in external libraries.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * // Basic usage with image
10
+ * <FilePicker
11
+ * label="Upload Image"
12
+ * accept="image/*"
13
+ * preview={imagePreview}
14
+ * placeholder="Click to upload"
15
+ * hint="JPEG, PNG or WebP"
16
+ * icon={PlusIcon}
17
+ * clearIcon={TrashIcon}
18
+ * onSelect={handleSelect}
19
+ * onClear={handleClear}
20
+ * />
21
+ * ```
22
+ */
23
+ declare const FilePicker: FC<FilePickerProps>;
24
+ export default FilePicker;
@@ -0,0 +1,99 @@
1
+ import { ComponentType, CSSProperties, ReactNode, SVGProps } from 'react';
2
+ /**
3
+ * SVG icon component type (compatible with Aurora-DS Icon)
4
+ */
5
+ export type SvgIconComponent = ComponentType<SVGProps<SVGSVGElement>>;
6
+ export type FilePickerProps = {
7
+ /**
8
+ * Label for the input
9
+ */
10
+ label?: string;
11
+ /**
12
+ * Accepted file types (MIME types)
13
+ * @example 'image/jpeg,image/png,image/webp'
14
+ * @example 'application/pdf'
15
+ * @example '.jpg,.png,.pdf'
16
+ */
17
+ accept?: string;
18
+ /**
19
+ * Preview content to display when a file is selected
20
+ * Can be a URL string for images or a ReactNode for custom preview
21
+ */
22
+ preview?: string | ReactNode | null;
23
+ /**
24
+ * Whether the preview is an image URL
25
+ * @default true when preview is a string
26
+ */
27
+ previewIsImage?: boolean;
28
+ /**
29
+ * Alt text for image preview
30
+ */
31
+ previewAlt?: string;
32
+ /**
33
+ * Error message to display
34
+ */
35
+ error?: string | null;
36
+ /**
37
+ * Callback when a file is selected
38
+ */
39
+ onSelect: (file: File | null) => void;
40
+ /**
41
+ * Callback when the file is cleared
42
+ */
43
+ onClear: () => void;
44
+ /**
45
+ * Whether the input is disabled
46
+ */
47
+ disabled?: boolean;
48
+ /**
49
+ * Placeholder text shown in the dropzone
50
+ */
51
+ placeholder?: string;
52
+ /**
53
+ * Hint text shown below the placeholder (e.g., accepted formats)
54
+ */
55
+ hint?: string;
56
+ /**
57
+ * Icon component to display in the dropzone
58
+ * Should be an SVG component (e.g., from @resources/Icons)
59
+ */
60
+ icon?: SvgIconComponent;
61
+ /**
62
+ * Icon component to display on the clear button
63
+ * Should be an SVG component (e.g., from @resources/Icons)
64
+ */
65
+ clearIcon?: SvgIconComponent;
66
+ /**
67
+ * Aria label for the clear button
68
+ */
69
+ clearAriaLabel?: string;
70
+ /**
71
+ * Width of the FilePicker container
72
+ * @example '400px'
73
+ * @example '100%'
74
+ */
75
+ width?: CSSProperties['width'];
76
+ /**
77
+ * Height of the dropzone (when no preview)
78
+ * @example '200px'
79
+ * @default '120px'
80
+ */
81
+ dropzoneHeight?: CSSProperties['height'];
82
+ /**
83
+ * Max height of the preview image
84
+ * @example '300px'
85
+ * @default '200px'
86
+ */
87
+ previewMaxHeight?: CSSProperties['maxHeight'];
88
+ /**
89
+ * Max width of the preview image
90
+ * @example '400px'
91
+ * @default '100%'
92
+ */
93
+ previewMaxWidth?: CSSProperties['maxWidth'];
94
+ /**
95
+ * Object fit for the preview image
96
+ * @default 'cover'
97
+ */
98
+ previewObjectFit?: CSSProperties['objectFit'];
99
+ };
@@ -0,0 +1,18 @@
1
+ import { CSSProperties } from 'react';
2
+ type StyleParams = {
3
+ width?: CSSProperties['width'];
4
+ dropzoneHeight?: CSSProperties['height'];
5
+ previewMaxHeight?: CSSProperties['maxHeight'];
6
+ previewMaxWidth?: CSSProperties['maxWidth'];
7
+ previewObjectFit?: CSSProperties['objectFit'];
8
+ };
9
+ export declare const FILE_PICKER_STYLES: {
10
+ root: (params?: StyleParams | undefined) => string;
11
+ dropzone: (params?: StyleParams | undefined) => string;
12
+ dropzoneDisabled: string;
13
+ hiddenInput: string;
14
+ previewContainer: string;
15
+ preview: (params?: StyleParams | undefined) => string;
16
+ clearButton: string;
17
+ };
18
+ export {};
@@ -0,0 +1,2 @@
1
+ export { default as FilePicker } from '@components/forms/file-picker/FilePicker.tsx';
2
+ export type { FilePickerProps } from '@components/forms/file-picker/FilePicker.props.ts';
@@ -0,0 +1,18 @@
1
+ import { FC } from 'react';
2
+ import { ImagePickerProps } from '@components/forms/image-picker/ImagePicker.props.ts';
3
+ /**
4
+ * Image picker component built on top of the generic FilePicker.
5
+ * Pre-configured for image uploads with JPEG, PNG, and WebP support.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * <ImagePicker
10
+ * label="Profile Picture"
11
+ * preview={imageUrl}
12
+ * onSelect={handleImageSelect}
13
+ * onClear={handleImageClear}
14
+ * />
15
+ * ```
16
+ */
17
+ declare const ImagePicker: FC<ImagePickerProps>;
18
+ export default ImagePicker;
@@ -0,0 +1,76 @@
1
+ import { CSSProperties } from 'react';
2
+ export type ImagePickerProps = {
3
+ /**
4
+ * Label for the input
5
+ */
6
+ label?: string;
7
+ /**
8
+ * Preview URL of the selected image
9
+ */
10
+ preview: string | null;
11
+ /**
12
+ * Error message to display
13
+ */
14
+ error?: string | null;
15
+ /**
16
+ * Callback when a file is selected
17
+ */
18
+ onSelect: (file: File | null) => void;
19
+ /**
20
+ * Callback when the image is cleared
21
+ */
22
+ onClear: () => void;
23
+ /**
24
+ * Whether the input is disabled
25
+ */
26
+ disabled?: boolean;
27
+ /**
28
+ * Placeholder text shown in the dropzone
29
+ * @default 'Click to upload an image'
30
+ */
31
+ placeholder?: string;
32
+ /**
33
+ * Hint text shown below the placeholder
34
+ * @default 'JPEG, PNG or WebP'
35
+ */
36
+ hint?: string;
37
+ /**
38
+ * Alt text for image preview
39
+ * @default 'Image preview'
40
+ */
41
+ previewAlt?: string;
42
+ /**
43
+ * Aria label for the clear button
44
+ * @default 'Remove image'
45
+ */
46
+ clearAriaLabel?: string;
47
+ /**
48
+ * Width of the ImagePicker container
49
+ * @example '400px'
50
+ * @example '100%'
51
+ */
52
+ width?: CSSProperties['width'];
53
+ /**
54
+ * Height of the dropzone (when no preview)
55
+ * @example '200px'
56
+ * @default '120px'
57
+ */
58
+ dropzoneHeight?: CSSProperties['height'];
59
+ /**
60
+ * Max height of the preview image
61
+ * @example '300px'
62
+ * @default '200px'
63
+ */
64
+ previewMaxHeight?: CSSProperties['maxHeight'];
65
+ /**
66
+ * Max width of the preview image
67
+ * @example '400px'
68
+ * @default '100%'
69
+ */
70
+ previewMaxWidth?: CSSProperties['maxWidth'];
71
+ /**
72
+ * Object fit for the preview image
73
+ * @default 'cover'
74
+ */
75
+ previewObjectFit?: CSSProperties['objectFit'];
76
+ };
@@ -0,0 +1,2 @@
1
+ export { default as ImagePicker } from '@components/forms/image-picker/ImagePicker.tsx';
2
+ export type { ImagePickerProps } from '@components/forms/image-picker/ImagePicker.props.ts';
@@ -11,6 +11,8 @@ export * from '@components/forms/input';
11
11
  export * from '@components/forms/textarea';
12
12
  export * from '@components/forms/select';
13
13
  export * from '@components/forms/date-picker';
14
+ export * from '@components/forms/file-picker';
15
+ export * from '@components/forms/image-picker';
14
16
  export * from '@components/layout/box';
15
17
  export * from '@components/layout/stack';
16
18
  export * from '@components/layout/card';
package/dist/cjs/index.js CHANGED
@@ -252,12 +252,10 @@ const getStatusColorStyles = (theme, color, variant, disabled) => {
252
252
  return {
253
253
  filled: {
254
254
  backgroundColor: theme.colors.disabled,
255
- color: theme.colors.disabledText,
256
255
  borderColor: 'transparent',
257
256
  },
258
257
  outlined: {
259
258
  backgroundColor: 'transparent',
260
- color: theme.colors.disabledText,
261
259
  borderColor: theme.colors.disabled,
262
260
  },
263
261
  }[variant];
@@ -266,138 +264,135 @@ const getStatusColorStyles = (theme, color, variant, disabled) => {
266
264
  default: {
267
265
  filled: {
268
266
  backgroundColor: theme.colors.defaultSubtle,
269
- color: theme.colors.default,
270
267
  borderColor: 'transparent',
271
268
  },
272
269
  outlined: {
273
270
  backgroundColor: theme.colors.defaultSubtle,
274
- color: theme.colors.default,
275
271
  borderColor: theme.colors.default,
276
272
  },
277
273
  },
278
274
  primary: {
279
275
  filled: {
280
276
  backgroundColor: theme.colors.primarySubtle,
281
- color: theme.colors.primary,
282
277
  borderColor: 'transparent',
283
278
  },
284
279
  outlined: {
285
280
  backgroundColor: theme.colors.primarySubtle,
286
- color: theme.colors.primary,
287
281
  borderColor: theme.colors.primary,
288
282
  },
289
283
  },
290
284
  success: {
291
285
  filled: {
292
286
  backgroundColor: theme.colors.successSubtle,
293
- color: theme.colors.success,
294
287
  borderColor: 'transparent',
295
288
  },
296
289
  outlined: {
297
290
  backgroundColor: theme.colors.successSubtle,
298
- color: theme.colors.success,
299
291
  borderColor: theme.colors.success,
300
292
  },
301
293
  },
302
294
  warning: {
303
295
  filled: {
304
296
  backgroundColor: theme.colors.warningSubtle,
305
- color: theme.colors.warning,
306
297
  borderColor: 'transparent',
307
298
  },
308
299
  outlined: {
309
300
  backgroundColor: theme.colors.warningSubtle,
310
- color: theme.colors.warning,
311
301
  borderColor: theme.colors.warning,
312
302
  },
313
303
  },
314
304
  error: {
315
305
  filled: {
316
306
  backgroundColor: theme.colors.errorSubtle,
317
- color: theme.colors.error,
318
307
  borderColor: 'transparent',
319
308
  },
320
309
  outlined: {
321
310
  backgroundColor: theme.colors.errorSubtle,
322
- color: theme.colors.error,
323
311
  borderColor: theme.colors.error,
324
312
  },
325
313
  },
326
314
  info: {
327
315
  filled: {
328
316
  backgroundColor: theme.colors.infoSubtle,
329
- color: theme.colors.info,
330
317
  borderColor: 'transparent',
331
318
  },
332
319
  outlined: {
333
320
  backgroundColor: theme.colors.infoSubtle,
334
- color: theme.colors.info,
335
321
  borderColor: theme.colors.info,
336
322
  },
337
323
  },
338
324
  highlight: {
339
325
  filled: {
340
326
  backgroundColor: theme.colors.highlightSubtle,
341
- color: theme.colors.highlight,
342
327
  borderColor: 'transparent',
343
328
  },
344
329
  outlined: {
345
330
  backgroundColor: theme.colors.highlightSubtle,
346
- color: theme.colors.highlight,
347
331
  borderColor: theme.colors.highlight,
348
332
  },
349
333
  },
350
334
  accent: {
351
335
  filled: {
352
336
  backgroundColor: theme.colors.accentSubtle,
353
- color: theme.colors.accent,
354
337
  borderColor: 'transparent',
355
338
  },
356
339
  outlined: {
357
340
  backgroundColor: theme.colors.accentSubtle,
358
- color: theme.colors.accent,
359
341
  borderColor: theme.colors.accent,
360
342
  },
361
343
  },
362
344
  new: {
363
345
  filled: {
364
346
  backgroundColor: theme.colors.newSubtle,
365
- color: theme.colors.new,
366
347
  borderColor: 'transparent',
367
348
  },
368
349
  outlined: {
369
350
  backgroundColor: theme.colors.newSubtle,
370
- color: theme.colors.new,
371
351
  borderColor: theme.colors.new,
372
352
  },
373
353
  },
374
354
  rose: {
375
355
  filled: {
376
356
  backgroundColor: theme.colors.roseSubtle,
377
- color: theme.colors.rose,
378
357
  borderColor: 'transparent',
379
358
  },
380
359
  outlined: {
381
360
  backgroundColor: theme.colors.roseSubtle,
382
- color: theme.colors.rose,
383
361
  borderColor: theme.colors.rose,
384
362
  },
385
363
  },
386
364
  yellow: {
387
365
  filled: {
388
366
  backgroundColor: theme.colors.yellowSubtle,
389
- color: theme.colors.yellow,
390
367
  borderColor: 'transparent',
391
368
  },
392
369
  outlined: {
393
370
  backgroundColor: theme.colors.yellowSubtle,
394
- color: theme.colors.yellow,
395
371
  borderColor: theme.colors.yellow,
396
372
  },
397
373
  },
398
374
  };
399
375
  return colorMap[color][variant];
400
376
  };
377
+ const getStatusTextColor = (theme, color, disabled) => {
378
+ if (disabled) {
379
+ return theme.colors.disabledText;
380
+ }
381
+ const colorMap = {
382
+ default: theme.colors.default,
383
+ primary: theme.colors.primary,
384
+ success: theme.colors.success,
385
+ warning: theme.colors.warning,
386
+ error: theme.colors.error,
387
+ info: theme.colors.info,
388
+ highlight: theme.colors.highlight,
389
+ accent: theme.colors.accent,
390
+ new: theme.colors.new,
391
+ rose: theme.colors.rose,
392
+ yellow: theme.colors.yellow,
393
+ };
394
+ return colorMap[color];
395
+ };
401
396
 
402
397
  /**
403
398
  * Get status size styles based on the theme, size and icon-only state
@@ -485,6 +480,8 @@ const getStatusContentSize = (size) => {
485
480
  */
486
481
  const Status = ({ label, icon, variant = 'filled', color = 'default', size = 'md', gap, radius, disabled = false, ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
487
482
  const isIconOnly = Boolean(icon) && !label;
483
+ const theme$1 = theme.useTheme();
484
+ const textColor = getStatusTextColor(theme$1, color, disabled);
488
485
  return (jsxRuntime.jsxs("span", { className: STATUS_STYLES.root({
489
486
  variant,
490
487
  color,
@@ -493,7 +490,7 @@ const Status = ({ label, icon, variant = 'filled', color = 'default', size = 'md
493
490
  disabled,
494
491
  gap,
495
492
  radius,
496
- }), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, children: [icon && (jsxRuntime.jsx(Icon, { size: getStatusContentSize(size), children: icon })), label && (jsxRuntime.jsx(Text, { variant: 'label', fontSize: getStatusContentSize(size), children: label }))] }));
493
+ }), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, children: [icon && (jsxRuntime.jsx(Icon, { size: getStatusContentSize(size), color: textColor, children: icon })), label && (jsxRuntime.jsx(Text, { variant: 'label', fontSize: getStatusContentSize(size), color: textColor, children: label }))] }));
497
494
  };
498
495
  Status.displayName = 'Status';
499
496
 
@@ -1227,6 +1224,10 @@ const MoreHorizontalIcon = () => {
1227
1224
  return (jsxRuntime.jsxs("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-ellipsis-icon lucide-ellipsis', children: [jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '1' }), jsxRuntime.jsx("circle", { cx: '19', cy: '12', r: '1' }), jsxRuntime.jsx("circle", { cx: '5', cy: '12', r: '1' })] }));
1228
1225
  };
1229
1226
 
1227
+ const PlusIcon = () => (jsxRuntime.jsx("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg', children: jsxRuntime.jsx("path", { d: 'M8 3.33337V12.6667M3.33333 8H12.6667', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' }) }));
1228
+
1229
+ const TrashIcon = () => (jsxRuntime.jsxs("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg', children: [jsxRuntime.jsx("path", { d: 'M2 4H3.33333H14', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' }), jsxRuntime.jsx("path", { d: 'M5.33325 4.00004V2.66671C5.33325 2.31309 5.47373 1.97395 5.72378 1.7239C5.97382 1.47385 6.31296 1.33337 6.66659 1.33337H9.33325C9.68687 1.33337 10.026 1.47385 10.276 1.7239C10.5261 1.97395 10.6666 2.31309 10.6666 2.66671V4.00004M12.6666 4.00004V13.3334C12.6666 13.687 12.5261 14.0261 12.276 14.2762C12.026 14.5262 11.6868 14.6667 11.3333 14.6667H4.66659C4.31296 14.6667 3.97382 14.5262 3.72378 14.2762C3.47373 14.0261 3.33325 13.687 3.33325 13.3334V4.00004H12.6666Z', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' })] }));
1230
+
1230
1231
  /**
1231
1232
  * Input component
1232
1233
  */
@@ -2109,6 +2110,150 @@ const DatePicker = ({ value, onChange, label, mandatory = false, placeholder, di
2109
2110
  DatePicker.displayName = 'DatePicker';
2110
2111
  var DatePicker_default = React.memo(DatePicker);
2111
2112
 
2113
+ const FILE_PICKER_STYLES = theme.createStyles((theme) => ({
2114
+ root: (params) => ({
2115
+ display: 'flex',
2116
+ flexDirection: 'column',
2117
+ gap: theme.spacing.xs,
2118
+ width: params?.width ?? '100%',
2119
+ }),
2120
+ dropzone: (params) => ({
2121
+ position: 'relative',
2122
+ display: 'flex',
2123
+ flexDirection: 'column',
2124
+ alignItems: 'center',
2125
+ justifyContent: 'center',
2126
+ gap: theme.spacing.sm,
2127
+ padding: theme.spacing.lg,
2128
+ border: `2px dashed ${theme.colors.border}`,
2129
+ borderRadius: theme.radius.md,
2130
+ backgroundColor: theme.colors.surface,
2131
+ cursor: 'pointer',
2132
+ transition: `all ${theme.transition.fast}`,
2133
+ minHeight: params?.dropzoneHeight ?? '120px',
2134
+ '&:hover': {
2135
+ borderColor: theme.colors.primary,
2136
+ backgroundColor: theme.colors.surfaceHover,
2137
+ },
2138
+ }),
2139
+ dropzoneDisabled: {
2140
+ opacity: 0.5,
2141
+ cursor: 'not-allowed',
2142
+ '&:hover': {
2143
+ borderColor: theme.colors.border,
2144
+ backgroundColor: theme.colors.surface,
2145
+ },
2146
+ },
2147
+ hiddenInput: {
2148
+ display: 'none',
2149
+ },
2150
+ previewContainer: {
2151
+ position: 'relative',
2152
+ width: '100%',
2153
+ borderRadius: theme.radius.md,
2154
+ overflow: 'hidden',
2155
+ },
2156
+ preview: (params) => ({
2157
+ width: '100%',
2158
+ maxHeight: params?.previewMaxHeight ?? '200px',
2159
+ maxWidth: params?.previewMaxWidth ?? '100%',
2160
+ objectFit: params?.previewObjectFit ?? 'cover',
2161
+ borderRadius: theme.radius.md,
2162
+ }),
2163
+ clearButton: {
2164
+ position: 'absolute',
2165
+ top: theme.spacing.xs,
2166
+ right: theme.spacing.xs,
2167
+ '& button': {
2168
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
2169
+ '&:hover': {
2170
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
2171
+ },
2172
+ },
2173
+ },
2174
+ }));
2175
+
2176
+ /**
2177
+ * Generic file picker component with drag-and-drop style interface.
2178
+ * Supports custom preview, icons, and styling for use in external libraries.
2179
+ *
2180
+ * @example
2181
+ * ```tsx
2182
+ * // Basic usage with image
2183
+ * <FilePicker
2184
+ * label="Upload Image"
2185
+ * accept="image/*"
2186
+ * preview={imagePreview}
2187
+ * placeholder="Click to upload"
2188
+ * hint="JPEG, PNG or WebP"
2189
+ * icon={PlusIcon}
2190
+ * clearIcon={TrashIcon}
2191
+ * onSelect={handleSelect}
2192
+ * onClear={handleClear}
2193
+ * />
2194
+ * ```
2195
+ */
2196
+ const FilePicker = ({ label, accept, preview, previewIsImage = true, previewAlt = 'Preview', error, onSelect, onClear, disabled = false, placeholder = 'Click to upload a file', hint, icon: IconComponent, clearIcon: ClearIconComponent, clearAriaLabel = 'Remove file', width, dropzoneHeight, previewMaxHeight, previewMaxWidth, previewObjectFit, }) => {
2197
+ // refs
2198
+ const inputRef = React.useRef(null);
2199
+ // handlers
2200
+ const handleClick = () => {
2201
+ if (!disabled && inputRef.current) {
2202
+ inputRef.current.click();
2203
+ }
2204
+ };
2205
+ const handleChange = (e) => {
2206
+ const file = e.target.files?.[0] || null;
2207
+ onSelect(file);
2208
+ // Reset input to allow selecting the same file again
2209
+ if (inputRef.current) {
2210
+ inputRef.current.value = '';
2211
+ }
2212
+ };
2213
+ const handleClear = (e) => {
2214
+ e.stopPropagation();
2215
+ onClear();
2216
+ };
2217
+ const handleKeyDown = (e) => {
2218
+ if (e.key === 'Enter' || e.key === ' ') {
2219
+ e.preventDefault();
2220
+ handleClick();
2221
+ }
2222
+ };
2223
+ // Determine if preview is a string (image URL) or ReactNode
2224
+ const isImagePreview = typeof preview === 'string' && previewIsImage;
2225
+ const hasPreview = preview !== null && preview !== undefined;
2226
+ // Style parameters
2227
+ const styleParams = {
2228
+ width,
2229
+ dropzoneHeight,
2230
+ previewMaxHeight,
2231
+ previewMaxWidth,
2232
+ previewObjectFit,
2233
+ };
2234
+ return (jsxRuntime.jsxs("div", { className: FILE_PICKER_STYLES.root(styleParams), children: [label && (jsxRuntime.jsx(Text, { fontSize: 'sm', variant: 'label', children: label })), jsxRuntime.jsx("input", { ref: inputRef, type: 'file', accept: accept, className: FILE_PICKER_STYLES.hiddenInput, onChange: handleChange, disabled: disabled }), hasPreview ? (jsxRuntime.jsxs("div", { className: FILE_PICKER_STYLES.previewContainer, children: [isImagePreview ? (jsxRuntime.jsx("img", { src: preview, alt: previewAlt, className: FILE_PICKER_STYLES.preview(styleParams) })) : (preview), !disabled && ClearIconComponent && (jsxRuntime.jsx("div", { className: FILE_PICKER_STYLES.clearButton, children: jsxRuntime.jsx(IconButton, { icon: jsxRuntime.jsx(ClearIconComponent, {}), onClick: handleClear, ariaLabel: clearAriaLabel, variant: 'contained', size: 'small', textColor: 'surface' }) }))] })) : (jsxRuntime.jsxs("div", { className: `${FILE_PICKER_STYLES.dropzone(styleParams)} ${disabled ? FILE_PICKER_STYLES.dropzoneDisabled : ''}`, onClick: handleClick, role: 'button', tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown, children: [IconComponent && (jsxRuntime.jsx(Icon, { size: 'lg', color: 'text', children: jsxRuntime.jsx(IconComponent, {}) })), jsxRuntime.jsx(Text, { children: placeholder }), hint && (jsxRuntime.jsx(Text, { fontSize: 'xs', color: 'textSecondary', children: hint }))] })), error && (jsxRuntime.jsx(Text, { fontSize: 'sm', color: 'error', children: error }))] }));
2235
+ };
2236
+ FilePicker.displayName = 'FilePicker';
2237
+
2238
+ /**
2239
+ * Image picker component built on top of the generic FilePicker.
2240
+ * Pre-configured for image uploads with JPEG, PNG, and WebP support.
2241
+ *
2242
+ * @example
2243
+ * ```tsx
2244
+ * <ImagePicker
2245
+ * label="Profile Picture"
2246
+ * preview={imageUrl}
2247
+ * onSelect={handleImageSelect}
2248
+ * onClear={handleImageClear}
2249
+ * />
2250
+ * ```
2251
+ */
2252
+ const ImagePicker = ({ label, preview, error, onSelect, onClear, disabled = false, placeholder = 'Click to upload an image', hint = 'JPEG, PNG or WebP', previewAlt = 'Image preview', clearAriaLabel = 'Remove image', width, dropzoneHeight, previewMaxHeight, previewMaxWidth, previewObjectFit, }) => {
2253
+ return (jsxRuntime.jsx(FilePicker, { label: label, accept: 'image/jpeg,image/png,image/webp', preview: preview, previewIsImage: true, previewAlt: previewAlt, error: error, onSelect: onSelect, onClear: onClear, disabled: disabled, placeholder: placeholder, hint: hint, icon: PlusIcon, clearIcon: TrashIcon, clearAriaLabel: clearAriaLabel, width: width, dropzoneHeight: dropzoneHeight, previewMaxHeight: previewMaxHeight, previewMaxWidth: previewMaxWidth, previewObjectFit: previewObjectFit }));
2254
+ };
2255
+ ImagePicker.displayName = 'ImagePicker';
2256
+
2112
2257
  /**
2113
2258
  * Box styles using createStyles from @aurora-ds/theme
2114
2259
  */
@@ -3574,10 +3719,12 @@ exports.ButtonToggleGroup = ButtonToggleGroup;
3574
3719
  exports.Card = Card;
3575
3720
  exports.DatePicker = DatePicker_default;
3576
3721
  exports.DrawerItem = DrawerItem;
3722
+ exports.FilePicker = FilePicker;
3577
3723
  exports.Form = Form$1;
3578
3724
  exports.Grid = Grid;
3579
3725
  exports.Icon = Icon;
3580
3726
  exports.IconButton = IconButton;
3727
+ exports.ImagePicker = ImagePicker;
3581
3728
  exports.Input = Input_default;
3582
3729
  exports.Menu = Menu;
3583
3730
  exports.MenuGroup = MenuGroup;