@am92/react-design-system 2.9.10 → 2.10.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 (28) hide show
  1. package/dist/Components/DsFileUploader/DsFileUploader.Component.d.ts +2 -3
  2. package/dist/Components/DsFileUploader/DsFileUploader.Component.js +109 -158
  3. package/dist/Components/DsFileUploader/DsFileUploader.Overrides.d.ts +5 -1
  4. package/dist/Components/DsFileUploader/DsFileUploader.Overrides.js +6 -1
  5. package/dist/Components/DsFileUploader/DsFileUploader.Types.d.ts +306 -13
  6. package/dist/Components/DsFileUploader/DsFileUploader.Types.js +46 -4
  7. package/dist/Components/DsFileUploader/DsFileUploaderPreview/DsFileUploaderImagePreview.Component.d.ts +11 -0
  8. package/dist/Components/DsFileUploader/DsFileUploaderPreview/DsFileUploaderImagePreview.Component.js +68 -0
  9. package/dist/Components/DsFileUploader/DsFileUploaderPreview/DsFileUploaderPreview.Component.d.ts +14 -0
  10. package/dist/Components/DsFileUploader/DsFileUploaderPreview/DsFileUploaderPreview.Component.js +28 -0
  11. package/dist/Components/DsFileUploader/FileUploaderFiles.d.ts +3 -0
  12. package/dist/Components/DsFileUploader/FileUploaderFiles.js +48 -0
  13. package/dist/Components/DsFileUploader/Slots/DsFileUploaderActionButton.Component.d.ts +10 -0
  14. package/dist/Components/DsFileUploader/Slots/DsFileUploaderActionButton.Component.js +18 -0
  15. package/dist/Components/DsFileUploader/Slots/DsFileUploaderDropZone.d.ts +10 -0
  16. package/dist/Components/DsFileUploader/Slots/DsFileUploaderDropZone.js +130 -0
  17. package/dist/Components/DsFileUploader/Slots/DsFileUploaderSelectedFilesSegment.d.ts +10 -0
  18. package/dist/Components/DsFileUploader/Slots/DsFileUploaderSelectedFilesSegment.js +21 -0
  19. package/dist/Components/DsFileUploader/Slots/DsFileUploaderUploadedFilesSegment.d.ts +10 -0
  20. package/dist/Components/DsFileUploader/Slots/DsFileUploaderUploadedFilesSegment.js +21 -0
  21. package/dist/Components/DsFileUploader/converter.d.ts +2 -0
  22. package/dist/Components/DsFileUploader/converter.js +25 -0
  23. package/dist/Components/DsFileUploader/helpers.d.ts +9 -0
  24. package/dist/Components/DsFileUploader/helpers.js +88 -0
  25. package/dist/Components/DsFileUploader/validator.d.ts +2 -0
  26. package/dist/Components/DsFileUploader/validator.js +37 -0
  27. package/dist/Theme/componentOverrides.d.ts +3 -0
  28. package/package.json +1 -1
@@ -1,9 +1,51 @@
1
+ import { DsFileUploaderActionButton } from './Slots/DsFileUploaderActionButton.Component';
2
+ import { DsFileUploaderDropZone } from './Slots/DsFileUploaderDropZone';
3
+ import { DsFileUploaderSelectedFilesSegment } from './Slots/DsFileUploaderSelectedFilesSegment';
4
+ import { DsFileUploaderUploadedFilesSegment } from './Slots/DsFileUploaderUploadedFilesSegment';
5
+ export var CONTENT_TYPE;
6
+ (function (CONTENT_TYPE) {
7
+ CONTENT_TYPE["FILE"] = "FILE";
8
+ CONTENT_TYPE["BASE64"] = "BASE64";
9
+ })(CONTENT_TYPE || (CONTENT_TYPE = {}));
10
+ export var VARIANT;
11
+ (function (VARIANT) {
12
+ VARIANT["FULL"] = "FULL";
13
+ VARIANT["COMPRESSED"] = "COMPRESSED";
14
+ })(VARIANT || (VARIANT = {}));
15
+ export var ERROR_CODES;
16
+ (function (ERROR_CODES) {
17
+ ERROR_CODES["INVALID_FILE_TYPE"] = "INVALID_FILE_TYPE";
18
+ ERROR_CODES["MAX_FILE_SIZE_EXCEEDED"] = "MAX_FILE_SIZE_EXCEEDED";
19
+ ERROR_CODES["FILE_SIZE_BELOW_MIN"] = "FILE_SIZE_BELOW_MIN";
20
+ })(ERROR_CODES || (ERROR_CODES = {}));
1
21
  export const DsFileUploaderDefaultProps = {
2
22
  name: '',
3
23
  onChange: () => { },
4
- titleButtonText: 'Upload document',
5
- descriptionTypograpghyText: 'Click to browse or drop here to upload'
24
+ value: undefined,
25
+ accept: '*',
26
+ variant: 'FULL',
27
+ multiple: true,
28
+ slots: {
29
+ DropZone: DsFileUploaderDropZone,
30
+ UploadedItemSegment: DsFileUploaderUploadedFilesSegment,
31
+ SelectedItemSegment: DsFileUploaderSelectedFilesSegment,
32
+ DeleteButton: DsFileUploaderActionButton,
33
+ PreviewButton: DsFileUploaderActionButton,
34
+ DownloadButton: DsFileUploaderActionButton
35
+ },
36
+ slotProps: {
37
+ SelectedItemSegment: {
38
+ label: 'Selected Documents',
39
+ showDeleteIcon: true
40
+ },
41
+ UploadedItemSegment: {
42
+ label: 'Uploaded documents',
43
+ showDeleteIcon: true
44
+ }
45
+ }
6
46
  };
7
- export const DsFileUploaderDefaultState = {
8
- files: []
47
+ export const DsFileUploaderDropzoneDefaultProps = {
48
+ variant: DsFileUploaderDefaultProps.variant,
49
+ title: 'Upload document',
50
+ description: 'Click to browse or drop here to upload'
9
51
  };
@@ -0,0 +1,11 @@
1
+ import { TContentType, TFile } from '../DsFileUploader.Types';
2
+ interface DsFileUploaderImagePreview {
3
+ file: TFile<TContentType>;
4
+ }
5
+ /**
6
+ * This component is responsible for rendering a preview of an uploaded image file.
7
+ * If the file content is valid and can be loaded, it shows the image preview.
8
+ * Otherwise, it displays a fallback icon.
9
+ */
10
+ export declare const DsFileUploaderImagePreview: ({ file }: DsFileUploaderImagePreview) => import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { DsImage } from '../../DsImage';
4
+ import { DsRemixIcon } from '../../DsRemixIcon';
5
+ /**
6
+ * This component is responsible for rendering a preview of an uploaded image file.
7
+ * If the file content is valid and can be loaded, it shows the image preview.
8
+ * Otherwise, it displays a fallback icon.
9
+ */
10
+ export const DsFileUploaderImagePreview = ({ file }) => {
11
+ const [src, setSrc] = useState(null);
12
+ useEffect(() => {
13
+ let isMounted = true;
14
+ const loadImage = async () => {
15
+ try {
16
+ let content = file.content;
17
+ if (typeof content === 'string') {
18
+ const img = new Image();
19
+ if (!content.startsWith('data:')) {
20
+ content = `data:${file.type};base64,${content}`;
21
+ }
22
+ img.src = content;
23
+ img.onload = () => isMounted && setSrc(img.src);
24
+ img.onerror = () => isMounted && setSrc(null);
25
+ }
26
+ else if (content instanceof File) {
27
+ const url = URL.createObjectURL(content);
28
+ const img = new Image();
29
+ img.src = url;
30
+ img.onload = () => isMounted && setSrc(url);
31
+ img.onerror = () => {
32
+ if (isMounted)
33
+ setSrc(null);
34
+ URL.revokeObjectURL(url);
35
+ };
36
+ }
37
+ }
38
+ catch {
39
+ if (isMounted)
40
+ setSrc(null);
41
+ }
42
+ };
43
+ loadImage();
44
+ return () => {
45
+ isMounted = false;
46
+ if (src && src.startsWith('blob:')) {
47
+ URL.revokeObjectURL(src);
48
+ }
49
+ };
50
+ }, [file]);
51
+ if (src) {
52
+ return (_jsx(DsImage, { srcSet: [{ src: src, alt: file.name }], width: '32px', height: '32px', WrapperProps: {
53
+ sx: {
54
+ width: '32px',
55
+ height: '32px'
56
+ }
57
+ }, style: {
58
+ objectFit: 'cover',
59
+ borderRadius: 'var(--ds-radius-quickFreeze)'
60
+ } }));
61
+ }
62
+ return (_jsx(DsRemixIcon, { sx: {
63
+ p: 'var(--ds-spacing-quickFreeze)',
64
+ borderRadius: 'var(--ds-radius-quickFreeze)',
65
+ backgroundColor: 'var(--ds-colour-neutral2)',
66
+ color: 'var(--ds-colour-actionTertiary)'
67
+ }, className: 'ri-image-2-line' }));
68
+ };
@@ -0,0 +1,14 @@
1
+ import { TContentType, TFile } from '../DsFileUploader.Types';
2
+ interface IDsFileUploaderPreviewProps {
3
+ file: TFile<TContentType>;
4
+ }
5
+ /**
6
+ * DsFileUploaderPreview is a functional component that renders an appropriate preview
7
+ * based on the MIME type of the uploaded file.
8
+ *
9
+ * - For image files (e.g., image/jpeg, image/png), it shows an actual image preview.
10
+ * - For video files, it shows a video icon.
11
+ * - For all other file types, it shows a generic file icon.
12
+ */
13
+ export declare const DsFileUploaderPreview: ({ file }: IDsFileUploaderPreviewProps) => import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { DsRemixIcon } from '../../DsRemixIcon';
3
+ import { DsFileUploaderImagePreview } from './DsFileUploaderImagePreview.Component';
4
+ /**
5
+ * DsFileUploaderPreview is a functional component that renders an appropriate preview
6
+ * based on the MIME type of the uploaded file.
7
+ *
8
+ * - For image files (e.g., image/jpeg, image/png), it shows an actual image preview.
9
+ * - For video files, it shows a video icon.
10
+ * - For all other file types, it shows a generic file icon.
11
+ */
12
+ export const DsFileUploaderPreview = ({ file }) => {
13
+ const { type: mimeType } = file;
14
+ const IMAGE_REGEX = new RegExp('^image/.*');
15
+ const VIDEO_REGEX = new RegExp('^video/.*');
16
+ if (IMAGE_REGEX.test(mimeType)) {
17
+ return _jsx(DsFileUploaderImagePreview, { file: file });
18
+ }
19
+ const iconName = VIDEO_REGEX.test(mimeType)
20
+ ? 'ri-video-line'
21
+ : 'ri-file-list-2-line';
22
+ return (_jsx(DsRemixIcon, { sx: {
23
+ p: 'var(--ds-spacing-quickFreeze)',
24
+ borderRadius: 'var(--ds-radius-quickFreeze)',
25
+ backgroundColor: 'var(--ds-colour-neutral2)',
26
+ color: 'var(--ds-colour-actionTertiary)'
27
+ }, className: iconName }));
28
+ };
@@ -0,0 +1,3 @@
1
+ import { type IDsFileUploaderItemSegmentProps } from './DsFileUploader.Types';
2
+ declare const FileUploaderFiles: (inProps: IDsFileUploaderItemSegmentProps) => import("react/jsx-runtime").JSX.Element | import("react/jsx-runtime").JSX.Element[] | null;
3
+ export default FileUploaderFiles;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DsBox } from '../DsBox';
3
+ import { DsStack } from '../DsStack';
4
+ import { DsTypography } from '../DsTypography';
5
+ import { DsFileUploaderDefaultProps } from './DsFileUploader.Types';
6
+ import { DsFileUploaderPreview } from './DsFileUploaderPreview/DsFileUploaderPreview.Component';
7
+ import { humanizeFileSize } from './helpers';
8
+ const FileUploaderFiles = (inProps) => {
9
+ const mergedSlotProps = {
10
+ ...DsFileUploaderDefaultProps.slots,
11
+ ...inProps.slots
12
+ };
13
+ const props = { ...inProps, slots: mergedSlotProps };
14
+ const { files, onPreview, onDelete, onDownload, showDeleteIcon, showDownloadIcon, showPreviewIcon, slots = {}, slotProps = {} } = props;
15
+ const { DownloadButton, PreviewButton, DeleteButton } = slots;
16
+ const { DownloadButton: DownloadButtonProps, PreviewButton: PreviewButtonProps, DeleteButton: DeleteButtonProps } = slotProps;
17
+ if (!files) {
18
+ return null;
19
+ }
20
+ const renderFile = (file, index) => {
21
+ return (_jsxs(DsStack, { direction: 'row', spacing: 'var(--ds-spacing-bitterCold)', sx: {
22
+ alignItems: 'center',
23
+ p: 'var(--ds-spacing-bitterCold)',
24
+ borderRadius: 'var(--ds-radius-glacial)',
25
+ borderWidth: '1px',
26
+ borderStyle: 'solid',
27
+ borderColor: 'var(--ds-colour-strokeDefault)',
28
+ backgroundColor: 'var(--ds-colour-surfacePrimary)',
29
+ cursor: 'pointer',
30
+ '&:hover:not(:has(.delete-icon:hover, .action-icon:hover))': {
31
+ borderColor: 'var(--ds-colour-strokeSecondarySelected)',
32
+ backgroundColor: 'var(--ds-colour-stateSelectedSecondaryHover)'
33
+ }
34
+ }, children: [_jsx(DsFileUploaderPreview, { file: file }), _jsxs(DsBox, { sx: {
35
+ display: 'flex',
36
+ flexGrow: 1,
37
+ minWidth: 0,
38
+ flexDirection: 'column'
39
+ }, children: [_jsx(DsTypography, { component: 'div', variant: 'bodyBoldSmall', noWrap: true, children: file.name }), _jsx(DsTypography, { component: 'div', variant: 'bodyRegularSmall', noWrap: true, sx: {
40
+ color: 'var(--ds-colour-typoTertiary)'
41
+ }, children: humanizeFileSize(file.size) })] }), showDownloadIcon && DownloadButton && (_jsx(DownloadButton, { IconProps: { className: 'ri-download-line' }, onClick: () => onDownload && onDownload(file.name, file), ...DownloadButtonProps })), showPreviewIcon && PreviewButton && (_jsx(PreviewButton, { IconProps: { className: 'ri-eye-line' }, onClick: () => onPreview && onPreview(file.name, file), ...PreviewButtonProps })), showDeleteIcon && DeleteButton && (_jsx(DeleteButton, { IconProps: { className: 'ri-delete-bin-line' }, onClick: () => onDelete && onDelete(file.name, file), ...DeleteButtonProps }))] }, `${file?.name}-${index}`));
42
+ };
43
+ if (!Array.isArray(files)) {
44
+ return renderFile(files, 0);
45
+ }
46
+ return files?.map(renderFile);
47
+ };
48
+ export default FileUploaderFiles;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { IDsFileUploaderActionButtonProps } from '../DsFileUploader.Types';
3
+ /**
4
+ * DsFileUploaderActionButton
5
+ *
6
+ * A reusable icon button component used within the FileUploader for actions like
7
+ * delete, download, preview, etc. It wraps a `DsRemixIcon` inside a `DsIconButton`
8
+ * and accepts customization through props.
9
+ */
10
+ export declare const DsFileUploaderActionButton: React.FC<IDsFileUploaderActionButtonProps>;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { DsIconButton } from '../../DsIconButton';
3
+ import { DsRemixIcon } from '../../DsRemixIcon';
4
+ /**
5
+ * DsFileUploaderActionButton
6
+ *
7
+ * A reusable icon button component used within the FileUploader for actions like
8
+ * delete, download, preview, etc. It wraps a `DsRemixIcon` inside a `DsIconButton`
9
+ * and accepts customization through props.
10
+ */
11
+ export const DsFileUploaderActionButton = ({ onClick, IconProps, ...iconButtonProps }) => {
12
+ return (_jsx(DsIconButton, { onClick: onClick, sx: {
13
+ p: 'var(--ds-spacing-glacial)',
14
+ borderRadius: 'var(--ds-radius-quickFreeze)',
15
+ backgroundColor: 'var(--ds-colour-surfaceSecondary)',
16
+ ...iconButtonProps.sx
17
+ }, ...iconButtonProps, children: _jsx(DsRemixIcon, { fontSize: 'bitterCold', ...IconProps }) }));
18
+ };
@@ -0,0 +1,10 @@
1
+ import { IDsFileUploaderDropZoneProps } from '../DsFileUploader.Types';
2
+ /**
3
+ * DsFileUploaderDropZone
4
+ *
5
+ * This component renders a drop zone area where users can drag & drop files
6
+ * or click to upload using a hidden input field. It supports two layout variants:
7
+ * - 'DEFAULT': Large button with drag and drop space area
8
+ * - 'COMPRESSED': Smaller area and button size to save space
9
+ */
10
+ export declare const DsFileUploaderDropZone: (inProps: IDsFileUploaderDropZoneProps) => (false | import("react/jsx-runtime").JSX.Element | null)[];
@@ -0,0 +1,130 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DsBox } from '../../DsBox';
3
+ import { DsButton } from '../../DsButton';
4
+ import { DsInput } from '../../DsInput';
5
+ import { DsRemixIcon } from '../../DsRemixIcon';
6
+ import { DsStack } from '../../DsStack';
7
+ import { DsTypography } from '../../DsTypography';
8
+ import { DsFileUploaderDropzoneDefaultProps } from '../DsFileUploader.Types';
9
+ /**
10
+ * DsFileUploaderDropZone
11
+ *
12
+ * This component renders a drop zone area where users can drag & drop files
13
+ * or click to upload using a hidden input field. It supports two layout variants:
14
+ * - 'DEFAULT': Large button with drag and drop space area
15
+ * - 'COMPRESSED': Smaller area and button size to save space
16
+ */
17
+ export const DsFileUploaderDropZone = (inProps) => {
18
+ const props = {
19
+ ...DsFileUploaderDropzoneDefaultProps,
20
+ ...inProps
21
+ };
22
+ const { variant, IconProps, title, description, InputProps, disabled, ...restDsStackProps } = props;
23
+ const isCompressed = variant === 'COMPRESSED';
24
+ const renderDescription = () => {
25
+ if (!description)
26
+ return null;
27
+ const isArray = Array.isArray(description);
28
+ const isString = typeof description === 'string';
29
+ const shouldRenderCompressedArray = isArray && isCompressed;
30
+ const shouldRenderString = isString;
31
+ if (!shouldRenderCompressedArray && !shouldRenderString)
32
+ return null;
33
+ const color = `var(--ds-colour-${isCompressed ? 'typoPrimary' : 'typoTertiary'})`;
34
+ const containerProps = {
35
+ direction: isCompressed ? 'row' : undefined,
36
+ justifyContent: isCompressed
37
+ ? isArray && description.length > 1
38
+ ? 'space-between'
39
+ : 'center'
40
+ : 'center',
41
+ alignItems: isCompressed ? 'center' : undefined,
42
+ textAlign: isCompressed ? 'center' : undefined,
43
+ pb: isCompressed ? undefined : 'var(--ds-spacing-bitterCold)',
44
+ sx: isCompressed
45
+ ? {
46
+ py: 'var(--ds-spacing-quickFreeze)',
47
+ px: 'var(--ds-spacing-glacial)',
48
+ backgroundColor: 'var(--ds-colour-surfaceSecondary)',
49
+ borderRadius: 'var(--ds-radius-quickFreeze)'
50
+ }
51
+ : undefined
52
+ };
53
+ const renderTypographies = () => {
54
+ const descList = isString ? [description] : description;
55
+ return descList.map((desc, index) => (_jsx(DsTypography, { variant: 'bodyRegularSmall', align: 'center', color: color, children: desc }, index)));
56
+ };
57
+ return (_jsx(DsStack, { ...containerProps, children: renderTypographies() }, 'dropzone-description'));
58
+ };
59
+ return [
60
+ _jsxs(DsBox, { sx: {
61
+ width: '100%',
62
+ position: 'relative',
63
+ borderRadius: 'var(--ds-radius-glacial)',
64
+ borderWidth: '1px',
65
+ borderStyle: 'dashed',
66
+ borderColor: 'var(--ds-colour-strokeDefault)',
67
+ backgroundColor: disabled
68
+ ? 'var(--ds-colour-stateDisabledSurface)'
69
+ : 'var(--ds-colour-surfacePrimary)',
70
+ cursor: disabled ? 'not-allowed' : 'pointer',
71
+ '&:hover': {
72
+ borderColor: disabled
73
+ ? 'var(--ds-colour-strokeDefault)'
74
+ : 'var(--ds-colour-strokeSecondarySelected)',
75
+ '.dropzone-content-wrapper': {
76
+ backgroundColor: disabled
77
+ ? 'var(--ds-colour-stateDisabledSurface)'
78
+ : isCompressed
79
+ ? 'var(--ds-colour-surfaceSecondary)'
80
+ : 'var(--ds-colour-stateSelectedSecondaryHover)'
81
+ }
82
+ }
83
+ }, children: [_jsxs(DsStack, { className: 'dropzone-content-wrapper', flexDirection: isCompressed ? 'row' : 'column', gap: 'var(--ds-spacing-quickFreeze)', justifyContent: 'center', alignItems: 'center', ...restDsStackProps, sx: {
84
+ borderRadius: isCompressed
85
+ ? 'var(--ds-radius-quickFreeze)'
86
+ : 'var(--ds-radius-glacial)',
87
+ m: isCompressed
88
+ ? 'var(--ds-spacing-frostbite)'
89
+ : 'var(--ds-spacing-zero)',
90
+ px: isCompressed
91
+ ? 'var(--ds-spacing-glacial)'
92
+ : 'var(--ds-spacing-bitterCold)',
93
+ pt: isCompressed
94
+ ? 'var(--ds-spacing-zero)'
95
+ : 'var(--ds-spacing-bitterCold)',
96
+ backgroundColor: disabled
97
+ ? 'var(--ds-colour-stateDisabledSurface)'
98
+ : isCompressed
99
+ ? 'var(--ds-colour-surfaceSecondary)'
100
+ : 'var(--ds-colour-surfacePrimary)',
101
+ '&:hover': {
102
+ backgroundColor: isCompressed
103
+ ? 'var(--ds-colour-surfaceSecondary)'
104
+ : 'var(--ds-colour-stateSelectedSecondaryHover)'
105
+ },
106
+ ...restDsStackProps?.sx
107
+ }, children: [_jsx(DsRemixIcon, { className: isCompressed ? 'ri-upload-line' : 'ri-upload-cloud-2-line', fontSize: isCompressed ? 'frostbite' : 'mild', color: disabled ? 'disabled' : 'secondary', ...IconProps }), _jsx(DsButton, { disabled: disabled, variant: 'text', color: 'secondary', children: title }), !isCompressed && renderDescription()] }), _jsx(DsInput, { type: 'file', slot: 'input', disabled: disabled, sx: {
108
+ position: 'absolute',
109
+ top: 0,
110
+ left: 0,
111
+ height: '100%',
112
+ width: '100%',
113
+ opacity: 0,
114
+ margin: 'var(--ds-spacing-zero) !important'
115
+ }, onChange: InputProps?.onChange, onDrop: InputProps?.onDrop, onDragOver: InputProps?.onDragOver, disableUnderline: true, inputProps: {
116
+ title,
117
+ value: '',
118
+ ...InputProps,
119
+ accept: InputProps?.accept,
120
+ multiple: InputProps?.multiple,
121
+ style: {
122
+ height: '100%',
123
+ width: '100%',
124
+ cursor: disabled ? 'not-allowed' : 'pointer',
125
+ ...InputProps?.style
126
+ }
127
+ } })] }, 'dropzone-box'),
128
+ isCompressed && renderDescription()
129
+ ];
130
+ };
@@ -0,0 +1,10 @@
1
+ import { TDsFileUploaderSlotProps } from '../DsFileUploader.Types';
2
+ /**
3
+ * DsFileUploaderSelectedFilesSegment
4
+ *
5
+ * This component is a wrapper to render a labeled section
6
+ * for displaying selected files in a scrollable container.
7
+ *
8
+ * It supports slot-level customization via props and merges them with default values.
9
+ */
10
+ export declare const DsFileUploaderSelectedFilesSegment: (inProps: TDsFileUploaderSlotProps["SelectedItemSegment"]) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DsStack } from '../../DsStack';
3
+ import { DsTypography } from '../../DsTypography';
4
+ import { DsFileUploaderDefaultProps } from '../DsFileUploader.Types';
5
+ import { cloneElement, isValidElement } from 'react';
6
+ /**
7
+ * DsFileUploaderSelectedFilesSegment
8
+ *
9
+ * This component is a wrapper to render a labeled section
10
+ * for displaying selected files in a scrollable container.
11
+ *
12
+ * It supports slot-level customization via props and merges them with default values.
13
+ */
14
+ export const DsFileUploaderSelectedFilesSegment = (inProps) => {
15
+ const props = {
16
+ ...DsFileUploaderDefaultProps.slotProps?.SelectedItemSegment,
17
+ ...inProps
18
+ };
19
+ const { label, children } = props;
20
+ return (_jsxs(DsStack, { spacing: 'var(--ds-spacing-frostbite)', children: [_jsx(DsTypography, { py: 'var(--ds-spacing-glacial)', variant: 'subheadingSemiboldDefault', color: 'var(--ds-colour-typoSecondary)', children: label }), _jsx(DsStack, { spacing: 'var(--ds-spacing-frostbite)', sx: { maxHeight: '440px', overflowY: 'auto' }, children: isValidElement(children) && cloneElement(children, { ...props }) })] }));
21
+ };
@@ -0,0 +1,10 @@
1
+ import { TDsFileUploaderSlotProps } from '../DsFileUploader.Types';
2
+ /**
3
+ * DsFileUploaderSelectedFilesSegment
4
+ *
5
+ * This component is a wrapper to render a labeled section
6
+ * for displaying uploaded files in a scrollable container.
7
+ *
8
+ * It supports slot-level customization via props and merges them with default values.
9
+ */
10
+ export declare const DsFileUploaderUploadedFilesSegment: (inProps: TDsFileUploaderSlotProps["UploadedItemSegment"]) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DsStack } from '../../DsStack';
3
+ import { DsTypography } from '../../DsTypography';
4
+ import { DsFileUploaderDefaultProps } from '../DsFileUploader.Types';
5
+ import { cloneElement, isValidElement } from 'react';
6
+ /**
7
+ * DsFileUploaderSelectedFilesSegment
8
+ *
9
+ * This component is a wrapper to render a labeled section
10
+ * for displaying uploaded files in a scrollable container.
11
+ *
12
+ * It supports slot-level customization via props and merges them with default values.
13
+ */
14
+ export const DsFileUploaderUploadedFilesSegment = (inProps) => {
15
+ const props = {
16
+ ...DsFileUploaderDefaultProps.slotProps?.UploadedItemSegment,
17
+ ...inProps
18
+ };
19
+ const { label, children } = props;
20
+ return (_jsxs(DsStack, { spacing: 'var(--ds-spacing-frostbite)', children: [_jsx(DsTypography, { py: 'var(--ds-spacing-glacial)', variant: 'subheadingSemiboldDefault', color: 'var(--ds-colour-typoSecondary)', children: label }), _jsx(DsStack, { spacing: 'var(--ds-spacing-frostbite)', sx: { maxHeight: '440px', overflowY: 'auto' }, children: isValidElement(children) && cloneElement(children, { ...props }) })] }));
21
+ };
@@ -0,0 +1,2 @@
1
+ import type { TContentType, TFile } from './DsFileUploader.Types';
2
+ export declare const fileToFileUploader: (file: File, contentType: TContentType) => Promise<TFile<TContentType>>;
@@ -0,0 +1,25 @@
1
+ export const fileToFileUploader = async (file, contentType) => {
2
+ const { name, type, size } = file;
3
+ let content = file;
4
+ if (contentType === 'BASE64') {
5
+ content = await fileToBase64(file);
6
+ if (typeof content === 'string' && content.startsWith('data:')) {
7
+ content = content.substring(content.indexOf(',') + 1);
8
+ }
9
+ }
10
+ const fileUploader = {
11
+ name,
12
+ type,
13
+ size,
14
+ content
15
+ };
16
+ return fileUploader;
17
+ };
18
+ const fileToBase64 = (file) => {
19
+ return new Promise((resolve, reject) => {
20
+ const reader = new FileReader();
21
+ reader.readAsDataURL(file);
22
+ reader.onload = () => resolve(reader.result);
23
+ reader.onerror = error => reject(error);
24
+ });
25
+ };
@@ -0,0 +1,9 @@
1
+ import type { IDsFileUploaderProps, TContentType, TErrorValue, TFile, TMultiple, TFileValue } from './DsFileUploader.Types';
2
+ export declare const mergeProps: <T extends Record<string, any>>(inProps: Partial<T>, defaultProps: T) => T;
3
+ export declare const getFileTypeIcon: <ContentType extends TContentType>(file: TFile<ContentType>) => import("react/jsx-runtime").JSX.Element;
4
+ export declare const humanizeFileSize: (bytes: number, decimals?: number) => string;
5
+ export declare const getDefaultValue: <Multiple extends TMultiple, ContentType extends TContentType>(props: IDsFileUploaderProps<Multiple, ContentType>) => TFileValue<Multiple, ContentType> | null;
6
+ export declare const getValidProcessedFile: <Multiple extends TMultiple, ContentType extends TContentType>(filesToProcess: FileList, files: TFileValue<Multiple, ContentType> | null, accept: string, minSize?: number, maxSize?: number, contentType?: TContentType) => Promise<{
7
+ valid: TFileValue<Multiple, ContentType>;
8
+ invalid: TErrorValue<Multiple, ContentType>;
9
+ }>;
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { DsFileUploaderImagePreview } from './DsFileUploaderPreview/DsFileUploaderImagePreview.Component';
3
+ import { getFileValidator } from './validator';
4
+ import { fileToFileUploader } from './converter';
5
+ import { DsRemixIcon } from '../DsRemixIcon';
6
+ export const mergeProps = (inProps, defaultProps) => {
7
+ return {
8
+ ...defaultProps,
9
+ ...inProps,
10
+ slots: {
11
+ ...defaultProps.slots,
12
+ ...inProps.slots
13
+ },
14
+ slotProps: {
15
+ ...defaultProps.slotProps,
16
+ ...inProps.slotProps,
17
+ SelectedItemSegment: {
18
+ ...defaultProps.slotProps?.SelectedItemSegment,
19
+ ...inProps.slotProps?.SelectedItemSegment
20
+ },
21
+ UploadedItemSegment: {
22
+ ...defaultProps.slotProps?.UploadedItemSegment,
23
+ ...inProps.slotProps?.UploadedItemSegment
24
+ },
25
+ DropZone: {
26
+ ...defaultProps?.slotProps?.DropZone,
27
+ ...inProps.slotProps?.DropZone
28
+ }
29
+ }
30
+ };
31
+ };
32
+ export const getFileTypeIcon = (file) => {
33
+ const { type: mimeType } = file;
34
+ const IMAGE_REGEX = new RegExp('^image/.*');
35
+ const VIDEO_REGEX = new RegExp('^video/.*');
36
+ if (IMAGE_REGEX.test(mimeType)) {
37
+ return _jsx(DsFileUploaderImagePreview, { file: file });
38
+ }
39
+ else if (VIDEO_REGEX.test(mimeType)) {
40
+ return _jsx(DsRemixIcon, { className: "ri-video-line" });
41
+ }
42
+ else {
43
+ return _jsx(DsRemixIcon, { className: "ri-file-list-2-line" });
44
+ }
45
+ };
46
+ export const humanizeFileSize = (bytes, decimals = 2) => {
47
+ if (!+bytes)
48
+ return '0 Bytes';
49
+ const k = 1024;
50
+ const dm = decimals < 0 ? 0 : decimals;
51
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
52
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
53
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
54
+ };
55
+ export const getDefaultValue = (props) => {
56
+ const { value, multiple } = props;
57
+ if (value) {
58
+ return value;
59
+ }
60
+ if (multiple) {
61
+ return [];
62
+ }
63
+ return null;
64
+ };
65
+ export const getValidProcessedFile = async (filesToProcess, files, accept, minSize, maxSize, contentType) => {
66
+ const validator = getFileValidator(accept, minSize, maxSize);
67
+ const isMultiple = Array.isArray(files);
68
+ const validFiles = isMultiple ? [...files] : [];
69
+ const invalidFiles = [];
70
+ const fileList = [...filesToProcess];
71
+ for (const file of fileList) {
72
+ const fileUploader = await fileToFileUploader(file, contentType || 'FILE');
73
+ const errorCode = validator(file);
74
+ if (!errorCode) {
75
+ validFiles.unshift(fileUploader);
76
+ }
77
+ else {
78
+ const temp = {
79
+ file: fileUploader,
80
+ errorCode
81
+ };
82
+ invalidFiles.push(temp);
83
+ }
84
+ }
85
+ const valid = isMultiple ? validFiles : validFiles[0];
86
+ const invalid = (isMultiple ? invalidFiles : invalidFiles[0]);
87
+ return { valid, invalid };
88
+ };
@@ -0,0 +1,2 @@
1
+ import { type TErrorCodes } from './DsFileUploader.Types';
2
+ export declare const getFileValidator: (accept: string, minSize?: number | undefined, maxSize?: number | undefined) => (file: File) => TErrorCodes | undefined;
@@ -0,0 +1,37 @@
1
+ import { ERROR_CODES } from './DsFileUploader.Types';
2
+ export const getFileValidator = (accept, minSize = 0, maxSize = Infinity) => {
3
+ const validateFileType = getFileTypeValidator(accept);
4
+ return (file) => {
5
+ const { size, type } = file;
6
+ if (!validateMinSize(size, minSize)) {
7
+ return ERROR_CODES.FILE_SIZE_BELOW_MIN;
8
+ }
9
+ if (!validateMaxSize(size, maxSize)) {
10
+ return ERROR_CODES.MAX_FILE_SIZE_EXCEEDED;
11
+ }
12
+ if (!validateFileType(type)) {
13
+ return ERROR_CODES.INVALID_FILE_TYPE;
14
+ }
15
+ };
16
+ };
17
+ const validateMinSize = (size, minSize = 0) => {
18
+ return size >= minSize;
19
+ };
20
+ const validateMaxSize = (size, maxSize = Infinity) => {
21
+ return size <= maxSize;
22
+ };
23
+ const getFileTypeValidator = (accept) => {
24
+ // Escape dots and replace wildcards with .* for regex
25
+ const typesPattern = accept
26
+ .split(',')
27
+ .map(type => type.trim())
28
+ .map(type => type.replace(/\./g, '\\.').replace(/\*/g, '.*'))
29
+ .join('|');
30
+ // Create a regex with start and end anchors
31
+ const typesRegex = new RegExp(`^(${typesPattern})$`);
32
+ // Validator function
33
+ const validateFileType = (mimeType) => {
34
+ return typesRegex.test(mimeType);
35
+ };
36
+ return validateFileType;
37
+ };