@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
@@ -10,4 +10,6 @@ import { EyeIcon } from '@resources/icons/EyeIcon.tsx';
10
10
  import { EyeOffIcon } from '@resources/icons/EyeOffIcon.tsx';
11
11
  import { InfoIcon } from '@resources/icons/InfoIcon.tsx';
12
12
  import { MoreHorizontalIcon } from '@resources/icons/MoreHorizontalIcon.tsx';
13
- export { AlertCircleIcon, AlertTriangleIcon, CalendarIcon, CheckCircleIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, InfoIcon, MoreHorizontalIcon, EyeIcon, EyeOffIcon, CloseIcon };
13
+ import { PlusIcon } from '@resources/icons/PlusIcon.tsx';
14
+ import { TrashIcon } from '@resources/icons/TrashIcon.tsx';
15
+ export { AlertCircleIcon, AlertTriangleIcon, CalendarIcon, CheckCircleIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, InfoIcon, MoreHorizontalIcon, EyeIcon, EyeOffIcon, CloseIcon, PlusIcon, TrashIcon };
@@ -0,0 +1,2 @@
1
+ import { FC } from 'react';
2
+ export declare const PlusIcon: FC;
@@ -0,0 +1,2 @@
1
+ import { FC } from 'react';
2
+ export declare const TrashIcon: FC;
@@ -2,6 +2,6 @@ import { ThemeContract } from '@/interfaces';
2
2
  import { StatusStyleParams } from '@components/data-display/status/Status.props.ts';
3
3
  export declare const getStatusColorStyles: (theme: ThemeContract, color: StatusStyleParams["color"], variant: StatusStyleParams["variant"], disabled: StatusStyleParams["disabled"]) => {
4
4
  backgroundColor: string;
5
- color: string;
6
5
  borderColor: string;
7
6
  };
7
+ export declare const getStatusTextColor: (theme: ThemeContract, color: StatusStyleParams["color"], disabled: StatusStyleParams["disabled"]) => string;
@@ -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/esm/index.js CHANGED
@@ -250,12 +250,10 @@ const getStatusColorStyles = (theme, color, variant, disabled) => {
250
250
  return {
251
251
  filled: {
252
252
  backgroundColor: theme.colors.disabled,
253
- color: theme.colors.disabledText,
254
253
  borderColor: 'transparent',
255
254
  },
256
255
  outlined: {
257
256
  backgroundColor: 'transparent',
258
- color: theme.colors.disabledText,
259
257
  borderColor: theme.colors.disabled,
260
258
  },
261
259
  }[variant];
@@ -264,138 +262,135 @@ const getStatusColorStyles = (theme, color, variant, disabled) => {
264
262
  default: {
265
263
  filled: {
266
264
  backgroundColor: theme.colors.defaultSubtle,
267
- color: theme.colors.default,
268
265
  borderColor: 'transparent',
269
266
  },
270
267
  outlined: {
271
268
  backgroundColor: theme.colors.defaultSubtle,
272
- color: theme.colors.default,
273
269
  borderColor: theme.colors.default,
274
270
  },
275
271
  },
276
272
  primary: {
277
273
  filled: {
278
274
  backgroundColor: theme.colors.primarySubtle,
279
- color: theme.colors.primary,
280
275
  borderColor: 'transparent',
281
276
  },
282
277
  outlined: {
283
278
  backgroundColor: theme.colors.primarySubtle,
284
- color: theme.colors.primary,
285
279
  borderColor: theme.colors.primary,
286
280
  },
287
281
  },
288
282
  success: {
289
283
  filled: {
290
284
  backgroundColor: theme.colors.successSubtle,
291
- color: theme.colors.success,
292
285
  borderColor: 'transparent',
293
286
  },
294
287
  outlined: {
295
288
  backgroundColor: theme.colors.successSubtle,
296
- color: theme.colors.success,
297
289
  borderColor: theme.colors.success,
298
290
  },
299
291
  },
300
292
  warning: {
301
293
  filled: {
302
294
  backgroundColor: theme.colors.warningSubtle,
303
- color: theme.colors.warning,
304
295
  borderColor: 'transparent',
305
296
  },
306
297
  outlined: {
307
298
  backgroundColor: theme.colors.warningSubtle,
308
- color: theme.colors.warning,
309
299
  borderColor: theme.colors.warning,
310
300
  },
311
301
  },
312
302
  error: {
313
303
  filled: {
314
304
  backgroundColor: theme.colors.errorSubtle,
315
- color: theme.colors.error,
316
305
  borderColor: 'transparent',
317
306
  },
318
307
  outlined: {
319
308
  backgroundColor: theme.colors.errorSubtle,
320
- color: theme.colors.error,
321
309
  borderColor: theme.colors.error,
322
310
  },
323
311
  },
324
312
  info: {
325
313
  filled: {
326
314
  backgroundColor: theme.colors.infoSubtle,
327
- color: theme.colors.info,
328
315
  borderColor: 'transparent',
329
316
  },
330
317
  outlined: {
331
318
  backgroundColor: theme.colors.infoSubtle,
332
- color: theme.colors.info,
333
319
  borderColor: theme.colors.info,
334
320
  },
335
321
  },
336
322
  highlight: {
337
323
  filled: {
338
324
  backgroundColor: theme.colors.highlightSubtle,
339
- color: theme.colors.highlight,
340
325
  borderColor: 'transparent',
341
326
  },
342
327
  outlined: {
343
328
  backgroundColor: theme.colors.highlightSubtle,
344
- color: theme.colors.highlight,
345
329
  borderColor: theme.colors.highlight,
346
330
  },
347
331
  },
348
332
  accent: {
349
333
  filled: {
350
334
  backgroundColor: theme.colors.accentSubtle,
351
- color: theme.colors.accent,
352
335
  borderColor: 'transparent',
353
336
  },
354
337
  outlined: {
355
338
  backgroundColor: theme.colors.accentSubtle,
356
- color: theme.colors.accent,
357
339
  borderColor: theme.colors.accent,
358
340
  },
359
341
  },
360
342
  new: {
361
343
  filled: {
362
344
  backgroundColor: theme.colors.newSubtle,
363
- color: theme.colors.new,
364
345
  borderColor: 'transparent',
365
346
  },
366
347
  outlined: {
367
348
  backgroundColor: theme.colors.newSubtle,
368
- color: theme.colors.new,
369
349
  borderColor: theme.colors.new,
370
350
  },
371
351
  },
372
352
  rose: {
373
353
  filled: {
374
354
  backgroundColor: theme.colors.roseSubtle,
375
- color: theme.colors.rose,
376
355
  borderColor: 'transparent',
377
356
  },
378
357
  outlined: {
379
358
  backgroundColor: theme.colors.roseSubtle,
380
- color: theme.colors.rose,
381
359
  borderColor: theme.colors.rose,
382
360
  },
383
361
  },
384
362
  yellow: {
385
363
  filled: {
386
364
  backgroundColor: theme.colors.yellowSubtle,
387
- color: theme.colors.yellow,
388
365
  borderColor: 'transparent',
389
366
  },
390
367
  outlined: {
391
368
  backgroundColor: theme.colors.yellowSubtle,
392
- color: theme.colors.yellow,
393
369
  borderColor: theme.colors.yellow,
394
370
  },
395
371
  },
396
372
  };
397
373
  return colorMap[color][variant];
398
374
  };
375
+ const getStatusTextColor = (theme, color, disabled) => {
376
+ if (disabled) {
377
+ return theme.colors.disabledText;
378
+ }
379
+ const colorMap = {
380
+ default: theme.colors.default,
381
+ primary: theme.colors.primary,
382
+ success: theme.colors.success,
383
+ warning: theme.colors.warning,
384
+ error: theme.colors.error,
385
+ info: theme.colors.info,
386
+ highlight: theme.colors.highlight,
387
+ accent: theme.colors.accent,
388
+ new: theme.colors.new,
389
+ rose: theme.colors.rose,
390
+ yellow: theme.colors.yellow,
391
+ };
392
+ return colorMap[color];
393
+ };
399
394
 
400
395
  /**
401
396
  * Get status size styles based on the theme, size and icon-only state
@@ -483,6 +478,8 @@ const getStatusContentSize = (size) => {
483
478
  */
484
479
  const Status = ({ label, icon, variant = 'filled', color = 'default', size = 'md', gap, radius, disabled = false, ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
485
480
  const isIconOnly = Boolean(icon) && !label;
481
+ const theme = useTheme();
482
+ const textColor = getStatusTextColor(theme, color, disabled);
486
483
  return (jsxs("span", { className: STATUS_STYLES.root({
487
484
  variant,
488
485
  color,
@@ -491,7 +488,7 @@ const Status = ({ label, icon, variant = 'filled', color = 'default', size = 'md
491
488
  disabled,
492
489
  gap,
493
490
  radius,
494
- }), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, children: [icon && (jsx(Icon, { size: getStatusContentSize(size), children: icon })), label && (jsx(Text, { variant: 'label', fontSize: getStatusContentSize(size), children: label }))] }));
491
+ }), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, children: [icon && (jsx(Icon, { size: getStatusContentSize(size), color: textColor, children: icon })), label && (jsx(Text, { variant: 'label', fontSize: getStatusContentSize(size), color: textColor, children: label }))] }));
495
492
  };
496
493
  Status.displayName = 'Status';
497
494
 
@@ -1225,6 +1222,10 @@ const MoreHorizontalIcon = () => {
1225
1222
  return (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: [jsx("circle", { cx: '12', cy: '12', r: '1' }), jsx("circle", { cx: '19', cy: '12', r: '1' }), jsx("circle", { cx: '5', cy: '12', r: '1' })] }));
1226
1223
  };
1227
1224
 
1225
+ const PlusIcon = () => (jsx("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg', children: jsx("path", { d: 'M8 3.33337V12.6667M3.33333 8H12.6667', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' }) }));
1226
+
1227
+ const TrashIcon = () => (jsxs("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg', children: [jsx("path", { d: 'M2 4H3.33333H14', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' }), 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' })] }));
1228
+
1228
1229
  /**
1229
1230
  * Input component
1230
1231
  */
@@ -2107,6 +2108,150 @@ const DatePicker = ({ value, onChange, label, mandatory = false, placeholder, di
2107
2108
  DatePicker.displayName = 'DatePicker';
2108
2109
  var DatePicker_default = memo(DatePicker);
2109
2110
 
2111
+ const FILE_PICKER_STYLES = createStyles((theme) => ({
2112
+ root: (params) => ({
2113
+ display: 'flex',
2114
+ flexDirection: 'column',
2115
+ gap: theme.spacing.xs,
2116
+ width: params?.width ?? '100%',
2117
+ }),
2118
+ dropzone: (params) => ({
2119
+ position: 'relative',
2120
+ display: 'flex',
2121
+ flexDirection: 'column',
2122
+ alignItems: 'center',
2123
+ justifyContent: 'center',
2124
+ gap: theme.spacing.sm,
2125
+ padding: theme.spacing.lg,
2126
+ border: `2px dashed ${theme.colors.border}`,
2127
+ borderRadius: theme.radius.md,
2128
+ backgroundColor: theme.colors.surface,
2129
+ cursor: 'pointer',
2130
+ transition: `all ${theme.transition.fast}`,
2131
+ minHeight: params?.dropzoneHeight ?? '120px',
2132
+ '&:hover': {
2133
+ borderColor: theme.colors.primary,
2134
+ backgroundColor: theme.colors.surfaceHover,
2135
+ },
2136
+ }),
2137
+ dropzoneDisabled: {
2138
+ opacity: 0.5,
2139
+ cursor: 'not-allowed',
2140
+ '&:hover': {
2141
+ borderColor: theme.colors.border,
2142
+ backgroundColor: theme.colors.surface,
2143
+ },
2144
+ },
2145
+ hiddenInput: {
2146
+ display: 'none',
2147
+ },
2148
+ previewContainer: {
2149
+ position: 'relative',
2150
+ width: '100%',
2151
+ borderRadius: theme.radius.md,
2152
+ overflow: 'hidden',
2153
+ },
2154
+ preview: (params) => ({
2155
+ width: '100%',
2156
+ maxHeight: params?.previewMaxHeight ?? '200px',
2157
+ maxWidth: params?.previewMaxWidth ?? '100%',
2158
+ objectFit: params?.previewObjectFit ?? 'cover',
2159
+ borderRadius: theme.radius.md,
2160
+ }),
2161
+ clearButton: {
2162
+ position: 'absolute',
2163
+ top: theme.spacing.xs,
2164
+ right: theme.spacing.xs,
2165
+ '& button': {
2166
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
2167
+ '&:hover': {
2168
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
2169
+ },
2170
+ },
2171
+ },
2172
+ }));
2173
+
2174
+ /**
2175
+ * Generic file picker component with drag-and-drop style interface.
2176
+ * Supports custom preview, icons, and styling for use in external libraries.
2177
+ *
2178
+ * @example
2179
+ * ```tsx
2180
+ * // Basic usage with image
2181
+ * <FilePicker
2182
+ * label="Upload Image"
2183
+ * accept="image/*"
2184
+ * preview={imagePreview}
2185
+ * placeholder="Click to upload"
2186
+ * hint="JPEG, PNG or WebP"
2187
+ * icon={PlusIcon}
2188
+ * clearIcon={TrashIcon}
2189
+ * onSelect={handleSelect}
2190
+ * onClear={handleClear}
2191
+ * />
2192
+ * ```
2193
+ */
2194
+ 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, }) => {
2195
+ // refs
2196
+ const inputRef = useRef(null);
2197
+ // handlers
2198
+ const handleClick = () => {
2199
+ if (!disabled && inputRef.current) {
2200
+ inputRef.current.click();
2201
+ }
2202
+ };
2203
+ const handleChange = (e) => {
2204
+ const file = e.target.files?.[0] || null;
2205
+ onSelect(file);
2206
+ // Reset input to allow selecting the same file again
2207
+ if (inputRef.current) {
2208
+ inputRef.current.value = '';
2209
+ }
2210
+ };
2211
+ const handleClear = (e) => {
2212
+ e.stopPropagation();
2213
+ onClear();
2214
+ };
2215
+ const handleKeyDown = (e) => {
2216
+ if (e.key === 'Enter' || e.key === ' ') {
2217
+ e.preventDefault();
2218
+ handleClick();
2219
+ }
2220
+ };
2221
+ // Determine if preview is a string (image URL) or ReactNode
2222
+ const isImagePreview = typeof preview === 'string' && previewIsImage;
2223
+ const hasPreview = preview !== null && preview !== undefined;
2224
+ // Style parameters
2225
+ const styleParams = {
2226
+ width,
2227
+ dropzoneHeight,
2228
+ previewMaxHeight,
2229
+ previewMaxWidth,
2230
+ previewObjectFit,
2231
+ };
2232
+ return (jsxs("div", { className: FILE_PICKER_STYLES.root(styleParams), children: [label && (jsx(Text, { fontSize: 'sm', variant: 'label', children: label })), jsx("input", { ref: inputRef, type: 'file', accept: accept, className: FILE_PICKER_STYLES.hiddenInput, onChange: handleChange, disabled: disabled }), hasPreview ? (jsxs("div", { className: FILE_PICKER_STYLES.previewContainer, children: [isImagePreview ? (jsx("img", { src: preview, alt: previewAlt, className: FILE_PICKER_STYLES.preview(styleParams) })) : (preview), !disabled && ClearIconComponent && (jsx("div", { className: FILE_PICKER_STYLES.clearButton, children: jsx(IconButton, { icon: jsx(ClearIconComponent, {}), onClick: handleClear, ariaLabel: clearAriaLabel, variant: 'contained', size: 'small', textColor: 'surface' }) }))] })) : (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 && (jsx(Icon, { size: 'lg', color: 'text', children: jsx(IconComponent, {}) })), jsx(Text, { children: placeholder }), hint && (jsx(Text, { fontSize: 'xs', color: 'textSecondary', children: hint }))] })), error && (jsx(Text, { fontSize: 'sm', color: 'error', children: error }))] }));
2233
+ };
2234
+ FilePicker.displayName = 'FilePicker';
2235
+
2236
+ /**
2237
+ * Image picker component built on top of the generic FilePicker.
2238
+ * Pre-configured for image uploads with JPEG, PNG, and WebP support.
2239
+ *
2240
+ * @example
2241
+ * ```tsx
2242
+ * <ImagePicker
2243
+ * label="Profile Picture"
2244
+ * preview={imageUrl}
2245
+ * onSelect={handleImageSelect}
2246
+ * onClear={handleImageClear}
2247
+ * />
2248
+ * ```
2249
+ */
2250
+ 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, }) => {
2251
+ return (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 }));
2252
+ };
2253
+ ImagePicker.displayName = 'ImagePicker';
2254
+
2110
2255
  /**
2111
2256
  * Box styles using createStyles from @aurora-ds/theme
2112
2257
  */
@@ -3555,5 +3700,5 @@ const defaultTheme = {
3555
3700
  breakpoints: themeBreakpoints,
3556
3701
  };
3557
3702
 
3558
- export { Accordion, Alert, AlertProvider, Avatar, AvatarGroup, Box, Breadcrumb, BreadcrumbEllipsis, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonToggle, ButtonToggleGroup, Card, DatePicker_default as DatePicker, DrawerItem, Form, Grid, Icon, IconButton, Input_default as Input, Menu, MenuGroup, MenuItem, Modal, Page, PageSection, Pagination, Select, Separator, Skeleton, Stack, Status, TabItem, Tabs, Text, TextArea_default as TextArea, defaultTheme, useAlert, useAnchorPosition, useClickOutside, useTransitionRender };
3703
+ export { Accordion, Alert, AlertProvider, Avatar, AvatarGroup, Box, Breadcrumb, BreadcrumbEllipsis, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonToggle, ButtonToggleGroup, Card, DatePicker_default as DatePicker, DrawerItem, FilePicker, Form, Grid, Icon, IconButton, ImagePicker, Input_default as Input, Menu, MenuGroup, MenuItem, Modal, Page, PageSection, Pagination, Select, Separator, Skeleton, Stack, Status, TabItem, Tabs, Text, TextArea_default as TextArea, defaultTheme, useAlert, useAnchorPosition, useClickOutside, useTransitionRender };
3559
3704
  //# sourceMappingURL=index.js.map