@timmbr/ui 1.0.0-beta → 1.2.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.
- package/README.md +20 -16
- package/dist/components/Accordion/Accordion.d.ts.map +1 -1
- package/dist/components/Alert/Alert.types.d.ts +2 -4
- package/dist/components/Alert/Alert.types.d.ts.map +1 -1
- package/dist/components/AlertDialog/AlertDialog.d.ts +16 -0
- package/dist/components/AlertDialog/AlertDialog.d.ts.map +1 -0
- package/dist/components/AlertDialog/AlertDialog.js +94 -0
- package/dist/components/AlertDialog/AlertDialog.styles.d.ts +8 -0
- package/dist/components/AlertDialog/AlertDialog.styles.d.ts.map +1 -0
- package/dist/components/AlertDialog/AlertDialog.styles.js +25 -0
- package/dist/components/AlertDialog/AlertDialog.types.d.ts +16 -0
- package/dist/components/AlertDialog/AlertDialog.types.d.ts.map +1 -0
- package/dist/components/AlertDialog/index.d.ts +4 -0
- package/dist/components/AlertDialog/index.d.ts.map +1 -0
- package/dist/components/Avatar/Avatar.styles.d.ts +2 -2
- package/dist/components/Badge/Badge.styles.d.ts +1 -1
- package/dist/components/Button/Button.styles.d.ts +2 -2
- package/dist/components/Chip/Chip.styles.d.ts +1 -1
- package/dist/components/CrossZoneLink/CrossZoneLink.d.ts +10 -0
- package/dist/components/CrossZoneLink/CrossZoneLink.d.ts.map +1 -0
- package/dist/components/CrossZoneLink/CrossZoneLink.js +111 -0
- package/dist/components/CrossZoneLink/CrossZoneLink.styles.d.ts +8 -0
- package/dist/components/CrossZoneLink/CrossZoneLink.styles.d.ts.map +1 -0
- package/dist/components/CrossZoneLink/CrossZoneLink.styles.js +33 -0
- package/dist/components/CrossZoneLink/CrossZoneLink.types.d.ts +31 -0
- package/dist/components/CrossZoneLink/CrossZoneLink.types.d.ts.map +1 -0
- package/dist/components/CrossZoneLink/index.d.ts +4 -0
- package/dist/components/CrossZoneLink/index.d.ts.map +1 -0
- package/dist/components/Dialog/Dialog.d.ts +2 -1
- package/dist/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/components/Dialog/Dialog.js +8 -6
- package/dist/components/Dialog/Dialog.styles.d.ts +1 -0
- package/dist/components/Dialog/Dialog.styles.d.ts.map +1 -1
- package/dist/components/Dialog/Dialog.styles.js +7 -4
- package/dist/components/Dialog/Dialog.types.d.ts +4 -6
- package/dist/components/Dialog/Dialog.types.d.ts.map +1 -1
- package/dist/components/Divider/Divider.styles.d.ts +1 -1
- package/dist/components/Drawer/Drawer.types.d.ts +3 -6
- package/dist/components/Drawer/Drawer.types.d.ts.map +1 -1
- package/dist/components/Dropdown/Dropdown.types.d.ts +1 -2
- package/dist/components/Dropdown/Dropdown.types.d.ts.map +1 -1
- package/dist/components/FormField/FormField.js +2 -2
- package/dist/components/Heading/Heading.styles.d.ts +2 -2
- package/dist/components/Icon/Icon.styles.d.ts +1 -1
- package/dist/components/ImageUpload/ImageUpload.d.ts +4 -0
- package/dist/components/ImageUpload/ImageUpload.d.ts.map +1 -0
- package/dist/components/ImageUpload/ImageUpload.helpers.d.ts +30 -0
- package/dist/components/ImageUpload/ImageUpload.helpers.d.ts.map +1 -0
- package/dist/components/ImageUpload/ImageUpload.helpers.js +95 -0
- package/dist/components/ImageUpload/ImageUpload.js +365 -0
- package/dist/components/ImageUpload/ImageUpload.styles.d.ts +17 -0
- package/dist/components/ImageUpload/ImageUpload.styles.d.ts.map +1 -0
- package/dist/components/ImageUpload/ImageUpload.styles.js +72 -0
- package/dist/components/ImageUpload/ImageUpload.types.d.ts +112 -0
- package/dist/components/ImageUpload/ImageUpload.types.d.ts.map +1 -0
- package/dist/components/ImageUpload/index.d.ts +6 -0
- package/dist/components/ImageUpload/index.d.ts.map +1 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadDropzone.d.ts +25 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadDropzone.d.ts.map +1 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadDropzone.js +106 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadIcons.d.ts +6 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadIcons.d.ts.map +1 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadIcons.js +61 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadModal.d.ts +12 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadModal.d.ts.map +1 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadModal.js +137 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadSinglePreview.d.ts +14 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadSinglePreview.d.ts.map +1 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadSinglePreview.js +142 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadThumbnailCard.d.ts +23 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadThumbnailCard.d.ts.map +1 -0
- package/dist/components/ImageUpload/subcomponents/ImageUploadThumbnailCard.js +139 -0
- package/dist/components/ImageUpload/subcomponents/index.d.ts +6 -0
- package/dist/components/ImageUpload/subcomponents/index.d.ts.map +1 -0
- package/dist/components/Inline/Inline.styles.d.ts +2 -2
- package/dist/components/Input/Input.js +2 -2
- package/dist/components/Input/Input.styles.d.ts +1 -1
- package/dist/components/Input/Input.styles.js +1 -1
- package/dist/components/Input/Input.types.d.ts +1 -1
- package/dist/components/Input/Input.types.d.ts.map +1 -1
- package/dist/components/LinkButton/LinkButton.d.ts +13 -0
- package/dist/components/LinkButton/LinkButton.d.ts.map +1 -0
- package/dist/components/LinkButton/LinkButton.js +179 -0
- package/dist/components/LinkButton/LinkButton.styles.d.ts +8 -0
- package/dist/components/LinkButton/LinkButton.styles.d.ts.map +1 -0
- package/dist/components/LinkButton/LinkButton.styles.js +61 -0
- package/dist/components/LinkButton/LinkButton.types.d.ts +60 -0
- package/dist/components/LinkButton/LinkButton.types.d.ts.map +1 -0
- package/dist/components/LinkButton/index.d.ts +4 -0
- package/dist/components/LinkButton/index.d.ts.map +1 -0
- package/dist/components/List/List.types.d.ts +2 -4
- package/dist/components/List/List.types.d.ts.map +1 -1
- package/dist/components/Popover/Popover.types.d.ts +1 -2
- package/dist/components/Popover/Popover.types.d.ts.map +1 -1
- package/dist/components/Progress/Progress.styles.d.ts +1 -1
- package/dist/components/Radio/Radio.js +2 -2
- package/dist/components/Select/Select.js +2 -2
- package/dist/components/Select/Select.styles.d.ts +1 -1
- package/dist/components/SideBarNavigation/SideBarNavigation.context.d.ts +15 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.context.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.context.js +10 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.d.ts +4 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.helpers.d.ts +19 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.helpers.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.helpers.js +48 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.js +132 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.styles.d.ts +18 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.styles.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.styles.js +85 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.types.d.ts +69 -0
- package/dist/components/SideBarNavigation/SideBarNavigation.types.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/index.d.ts +13 -0
- package/dist/components/SideBarNavigation/index.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarBranding.d.ts +13 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarBranding.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarBranding.js +58 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarFooter.d.ts +10 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarFooter.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarFooter.js +27 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarNavItem.d.ts +14 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarNavItem.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarNavItem.js +147 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarPopoverMenu.d.ts +13 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarPopoverMenu.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarPopoverMenu.js +106 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarProfile.d.ts +13 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarProfile.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarProfile.js +118 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarSubItem.d.ts +13 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarSubItem.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarSubItem.js +34 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarToggle.d.ts +8 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarToggle.d.ts.map +1 -0
- package/dist/components/SideBarNavigation/subcomponents/SidebarToggle.js +40 -0
- package/dist/components/Stack/Stack.styles.d.ts +2 -2
- package/dist/components/Stat/Stat.types.d.ts +3 -6
- package/dist/components/Stat/Stat.types.d.ts.map +1 -1
- package/dist/components/Table/Table.types.d.ts +7 -14
- package/dist/components/Table/Table.types.d.ts.map +1 -1
- package/dist/components/Tabs/Tabs.styles.d.ts +2 -2
- package/dist/components/Text/Text.styles.d.ts +2 -2
- package/dist/components/Textarea/Textarea.js +2 -2
- package/dist/components/Textarea/Textarea.styles.d.ts +1 -1
- package/dist/components/Toast/Toast.d.ts.map +1 -1
- package/dist/components/Toast/Toast.js +13 -4
- package/dist/components/Toast/Toast.types.d.ts +35 -2
- package/dist/components/Toast/Toast.types.d.ts.map +1 -1
- package/dist/components/Toast/ToastContainer.d.ts +7 -0
- package/dist/components/Toast/ToastContainer.d.ts.map +1 -0
- package/dist/components/Toast/ToastContainer.js +104 -0
- package/dist/components/Toast/index.d.ts +2 -0
- package/dist/components/Toast/index.d.ts.map +1 -1
- package/dist/components/Toast/toastStore.d.ts +31 -0
- package/dist/components/Toast/toastStore.d.ts.map +1 -0
- package/dist/components/Toast/toastStore.js +104 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -3
- package/dist/providers/TimmbrConfigProvider.d.ts +20 -2
- package/dist/providers/TimmbrConfigProvider.d.ts.map +1 -1
- package/dist/providers/TimmbrConfigProvider.js +38 -3
- package/package.json +15 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.styles.d.ts","sourceRoot":"","sources":["../../../src/components/Dialog/Dialog.styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,oFAEjC,CAAC;AAEF,eAAO,MAAM,qBAAqB,oFAEjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,oFAEhC,CAAC;AAEF,eAAO,MAAM,oBAAoB,oFAEhC,CAAC;AAEF,eAAO,MAAM,mBAAmB,oFAE/B,CAAC;AAEF,eAAO,MAAM,yBAAyB,oFAErC,CAAC"}
|
|
1
|
+
{"version":3,"file":"Dialog.styles.d.ts","sourceRoot":"","sources":["../../../src/components/Dialog/Dialog.styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,oFAEjC,CAAC;AAEF,eAAO,MAAM,qBAAqB,oFAEjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,oFAEhC,CAAC;AAEF,eAAO,MAAM,kBAAkB,oFAE9B,CAAC;AAEF,eAAO,MAAM,oBAAoB,oFAEhC,CAAC;AAEF,eAAO,MAAM,mBAAmB,oFAE/B,CAAC;AAEF,eAAO,MAAM,yBAAyB,oFAErC,CAAC"}
|
|
@@ -4,13 +4,16 @@ const dialogOverlayVariants = cva(
|
|
|
4
4
|
"fixed inset-0 z-50 bg-black/60 backdrop-blur-xs transition-opacity duration-200 data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out"
|
|
5
5
|
);
|
|
6
6
|
const dialogContentVariants = cva(
|
|
7
|
-
"relative pointer-events-auto
|
|
7
|
+
"relative pointer-events-auto flex flex-col w-full max-w-lg max-h-[calc(100vh-2rem)] sm:max-h-[calc(100vh-4rem)] rounded-xl border border-grey-200 dark:border-grey-800 bg-white dark:bg-grey-900 shadow-xl font-sans text-foreground overflow-hidden data-[state=open]:animate-dialog-scale-in data-[state=closed]:animate-dialog-scale-out"
|
|
8
8
|
);
|
|
9
9
|
const dialogHeaderVariants = cva(
|
|
10
|
-
"flex flex-col space-y-1.5 text-left"
|
|
10
|
+
"flex flex-col space-y-1.5 text-left p-6 pb-4 border-b border-grey-100 dark:border-grey-800/60 shrink-0 bg-white dark:bg-grey-900 pr-12"
|
|
11
|
+
);
|
|
12
|
+
const dialogBodyVariants = cva(
|
|
13
|
+
"flex-1 overflow-y-auto p-6 min-h-0"
|
|
11
14
|
);
|
|
12
15
|
const dialogFooterVariants = cva(
|
|
13
|
-
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 gap-2
|
|
16
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 gap-2 p-6 pt-4 border-t border-grey-100 dark:border-grey-800/60 shrink-0 bg-white dark:bg-grey-900"
|
|
14
17
|
);
|
|
15
18
|
const dialogTitleVariants = cva(
|
|
16
19
|
"text-xl font-display font-semibold leading-none tracking-tight text-foreground"
|
|
@@ -19,4 +22,4 @@ const dialogDescriptionVariants = cva(
|
|
|
19
22
|
"text-sm text-muted-foreground font-sans leading-relaxed"
|
|
20
23
|
);
|
|
21
24
|
|
|
22
|
-
export { dialogContentVariants, dialogDescriptionVariants, dialogFooterVariants, dialogHeaderVariants, dialogOverlayVariants, dialogTitleVariants };
|
|
25
|
+
export { dialogBodyVariants, dialogContentVariants, dialogDescriptionVariants, dialogFooterVariants, dialogHeaderVariants, dialogOverlayVariants, dialogTitleVariants };
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type * as React from 'react';
|
|
2
2
|
import type * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
3
3
|
import type { MotionProp } from '../../types/motion';
|
|
4
|
-
export
|
|
5
|
-
}
|
|
4
|
+
export type DialogProps = DialogPrimitive.DialogProps;
|
|
6
5
|
export interface DialogContentProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
|
|
7
6
|
/**
|
|
8
7
|
* Motion transition control.
|
|
9
8
|
*/
|
|
10
9
|
motion?: MotionProp;
|
|
11
10
|
}
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
}
|
|
11
|
+
export type DialogHeaderProps = React.HTMLAttributes<HTMLDivElement>;
|
|
12
|
+
export type DialogBodyProps = React.HTMLAttributes<HTMLDivElement>;
|
|
13
|
+
export type DialogFooterProps = React.HTMLAttributes<HTMLDivElement>;
|
|
16
14
|
//# sourceMappingURL=Dialog.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.types.d.ts","sourceRoot":"","sources":["../../../src/components/Dialog/Dialog.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"Dialog.types.d.ts","sourceRoot":"","sources":["../../../src/components/Dialog/Dialog.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC;AAEtD,MAAM,WAAW,kBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC;IACtE;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;AACrE,MAAM,MAAM,eAAe,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC"}
|
|
@@ -3,7 +3,7 @@ export declare const dividerVariants: (props?: ({
|
|
|
3
3
|
orientation?: "horizontal" | "vertical" | null | undefined;
|
|
4
4
|
variant?: "dashed" | "dotted" | "solid" | null | undefined;
|
|
5
5
|
thickness?: "default" | "thick" | "thin" | null | undefined;
|
|
6
|
-
color?: "default" | "primary" | "
|
|
6
|
+
color?: "default" | "primary" | "muted" | "brand" | null | undefined;
|
|
7
7
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
8
|
export type DividerVariants = VariantProps<typeof dividerVariants>;
|
|
9
9
|
//# sourceMappingURL=Divider.styles.d.ts.map
|
|
@@ -2,16 +2,13 @@ import type * as React from 'react';
|
|
|
2
2
|
import type * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
3
3
|
import type { DrawerContentVariants } from './Drawer.styles';
|
|
4
4
|
import type { MotionProp } from '../../types/motion';
|
|
5
|
-
export
|
|
6
|
-
}
|
|
5
|
+
export type DrawerProps = DialogPrimitive.DialogProps;
|
|
7
6
|
export interface DrawerContentProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, DrawerContentVariants {
|
|
8
7
|
/**
|
|
9
8
|
* Motion transition control.
|
|
10
9
|
*/
|
|
11
10
|
motion?: MotionProp;
|
|
12
11
|
}
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
export interface DrawerFooterProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
16
|
-
}
|
|
12
|
+
export type DrawerHeaderProps = React.HTMLAttributes<HTMLDivElement>;
|
|
13
|
+
export type DrawerFooterProps = React.HTMLAttributes<HTMLDivElement>;
|
|
17
14
|
//# sourceMappingURL=Drawer.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.types.d.ts","sourceRoot":"","sources":["../../../src/components/Drawer/Drawer.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"Drawer.types.d.ts","sourceRoot":"","sources":["../../../src/components/Drawer/Drawer.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC;AAEtD,MAAM,WAAW,kBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,EACpE,qBAAqB;IACvB;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type * as React from 'react';
|
|
2
2
|
import type * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
3
|
import type { MotionProp } from '../../types/motion';
|
|
4
|
-
export
|
|
5
|
-
}
|
|
4
|
+
export type DropdownMenuProps = DropdownMenuPrimitive.DropdownMenuProps;
|
|
6
5
|
export interface DropdownMenuContentProps extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {
|
|
7
6
|
/**
|
|
8
7
|
* Motion transition control.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropdown.types.d.ts","sourceRoot":"","sources":["../../../src/components/Dropdown/Dropdown.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,qBAAqB,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,
|
|
1
|
+
{"version":3,"file":"Dropdown.types.d.ts","sourceRoot":"","sources":["../../../src/components/Dropdown/Dropdown.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,KAAK,qBAAqB,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,CAAC,iBAAiB,CAAC;AAExE,MAAM,WAAW,wBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,qBAAqB,CAAC,OAAO,CAAC;IAC5E;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,qBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,qBAAqB,CAAC,IAAI,CAAC;IACzE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB"}
|
|
@@ -39,12 +39,12 @@ const FormField = React.forwardRef(
|
|
|
39
39
|
{
|
|
40
40
|
id: `${id}-desc`,
|
|
41
41
|
className: cn(
|
|
42
|
-
"text-xs mt-
|
|
42
|
+
"text-xs mt-1 flex items-center gap-1.5",
|
|
43
43
|
isError ? "text-destructive font-medium" : "text-muted"
|
|
44
44
|
),
|
|
45
45
|
children: [
|
|
46
46
|
isError ? /* @__PURE__ */ jsx(AlertCircle, { className: "w-3.5 h-3.5 shrink-0", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Info, { className: "w-3.5 h-3.5 shrink-0", "aria-hidden": "true" }),
|
|
47
|
-
/* @__PURE__ */ jsx("span", { children: errorMessage || helperText })
|
|
47
|
+
/* @__PURE__ */ jsx("span", { className: "leading-normal", children: errorMessage || helperText })
|
|
48
48
|
]
|
|
49
49
|
}
|
|
50
50
|
)
|
|
@@ -2,8 +2,8 @@ import { type VariantProps } from 'class-variance-authority';
|
|
|
2
2
|
export declare const headingVariants: (props?: ({
|
|
3
3
|
level?: 1 | 2 | 3 | 4 | 5 | 6 | null | undefined;
|
|
4
4
|
font?: "title" | "display" | null | undefined;
|
|
5
|
-
foreground?: "default" | "inherit" | "white" | "subtle" | "primary" | "secondary" | "
|
|
6
|
-
weight?: "bold" | "
|
|
5
|
+
foreground?: "default" | "destructive" | "inherit" | "white" | "subtle" | "primary" | "secondary" | "muted" | null | undefined;
|
|
6
|
+
weight?: "bold" | "normal" | "medium" | "light" | "semibold" | null | undefined;
|
|
7
7
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
8
|
export type HeadingVariants = VariantProps<typeof headingVariants>;
|
|
9
9
|
//# sourceMappingURL=Heading.styles.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type VariantProps } from 'class-variance-authority';
|
|
2
2
|
export declare const iconVariants: (props?: ({
|
|
3
3
|
size?: "sm" | "lg" | "md" | "xl" | "xs" | null | undefined;
|
|
4
|
-
foreground?: "default" | "inherit" | "white" | "subtle" | "primary" | "secondary" | "
|
|
4
|
+
foreground?: "default" | "destructive" | "inherit" | "white" | "subtle" | "primary" | "secondary" | "muted" | null | undefined;
|
|
5
5
|
clickable?: boolean | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export type IconVariants = VariantProps<typeof iconVariants>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageUpload.d.ts","sourceRoot":"","sources":["../../../src/components/ImageUpload/ImageUpload.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAwB/B,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,qBAAqB,CAAC;AAS7B,eAAO,MAAM,WAAW,yFAoZvB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ImageUploadItem, ImageUploadValue } from './ImageUpload.types';
|
|
2
|
+
export declare const DEFAULT_IMAGE_ACCEPT = "image/png, image/jpeg, image/webp, image/gif";
|
|
3
|
+
export declare const DEFAULT_VIDEO_ACCEPT = "video/mp4, video/webm, video/quicktime, video/ogg";
|
|
4
|
+
export declare const DEFAULT_ALL_ACCEPT = "image/png, image/jpeg, image/webp, image/gif, video/mp4, video/webm, video/quicktime, video/ogg";
|
|
5
|
+
export declare const DEFAULT_IMAGE_MAX_SIZE: number;
|
|
6
|
+
export declare const DEFAULT_VIDEO_MAX_SIZE: number;
|
|
7
|
+
export declare const VIDEO_EXTENSIONS: RegExp;
|
|
8
|
+
export declare const GRID_COLS_MAP: Record<number, string>;
|
|
9
|
+
/**
|
|
10
|
+
* Checks whether an item, URL, or filename corresponds to a video file.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isVideoMedia(item?: ImageUploadItem | string | null): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Normalizes single or array value props into an array of ImageUploadItem.
|
|
15
|
+
*/
|
|
16
|
+
export declare function normalizeUploadItems(value?: ImageUploadValue | ImageUploadValue[] | null): ImageUploadItem[];
|
|
17
|
+
/**
|
|
18
|
+
* Validates files against acceptance criteria (MIME/ext, max files, and size).
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateUploadFiles({ files, accept, maxSizeBytes, maxFiles, currentCount, }: {
|
|
21
|
+
files: File[];
|
|
22
|
+
accept: string;
|
|
23
|
+
maxSizeBytes: number;
|
|
24
|
+
maxFiles?: number;
|
|
25
|
+
currentCount: number;
|
|
26
|
+
}): {
|
|
27
|
+
validFiles: File[];
|
|
28
|
+
errorMessage: string | null;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=ImageUpload.helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageUpload.helpers.d.ts","sourceRoot":"","sources":["../../../src/components/ImageUpload/ImageUpload.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,oBAAoB,iDAAiD,CAAC;AACnF,eAAO,MAAM,oBAAoB,sDAAsD,CAAC;AACxF,eAAO,MAAM,kBAAkB,oGAAqD,CAAC;AAErF,eAAO,MAAM,sBAAsB,QAAkB,CAAC;AACtD,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAEvD,eAAO,MAAM,gBAAgB,QAA2C,CAAC;AAEzE,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKhD,CAAC;AAEF;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CAS5E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,GAAG,IAAI,GACnD,eAAe,EAAE,CAenB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,MAAM,EACN,YAAY,EACZ,QAAQ,EACR,YAAY,GACb,EAAE;IACD,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG;IAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA0DtD"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const DEFAULT_IMAGE_ACCEPT = "image/png, image/jpeg, image/webp, image/gif";
|
|
2
|
+
const DEFAULT_VIDEO_ACCEPT = "video/mp4, video/webm, video/quicktime, video/ogg";
|
|
3
|
+
const DEFAULT_ALL_ACCEPT = `${DEFAULT_IMAGE_ACCEPT}, ${DEFAULT_VIDEO_ACCEPT}`;
|
|
4
|
+
const DEFAULT_IMAGE_MAX_SIZE = 5 * 1024 * 1024;
|
|
5
|
+
const DEFAULT_VIDEO_MAX_SIZE = 50 * 1024 * 1024;
|
|
6
|
+
const VIDEO_EXTENSIONS = /\.(mp4|webm|mov|m4v|ogg|ogv|avi|mkv)$/i;
|
|
7
|
+
const GRID_COLS_MAP = {
|
|
8
|
+
2: "grid-cols-2",
|
|
9
|
+
3: "grid-cols-2 sm:grid-cols-3",
|
|
10
|
+
4: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4",
|
|
11
|
+
5: "grid-cols-2 sm:grid-cols-3 md:grid-cols-5"
|
|
12
|
+
};
|
|
13
|
+
function isVideoMedia(item) {
|
|
14
|
+
if (!item) return false;
|
|
15
|
+
if (typeof item === "string") {
|
|
16
|
+
return VIDEO_EXTENSIONS.test(item) || item.startsWith("data:video/");
|
|
17
|
+
}
|
|
18
|
+
if (item.type === "video" || item.type && item.type.startsWith("video/")) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return VIDEO_EXTENSIONS.test(item.url || "") || VIDEO_EXTENSIONS.test(item.name || "");
|
|
22
|
+
}
|
|
23
|
+
function normalizeUploadItems(value) {
|
|
24
|
+
if (!value) return [];
|
|
25
|
+
const rawArray = Array.isArray(value) ? value : [value];
|
|
26
|
+
return rawArray.map((v) => {
|
|
27
|
+
if (typeof v === "string") {
|
|
28
|
+
return {
|
|
29
|
+
url: v,
|
|
30
|
+
type: isVideoMedia(v) ? "video" : "image"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...v,
|
|
35
|
+
type: v.type || (isVideoMedia(v) ? "video" : "image")
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function validateUploadFiles({
|
|
40
|
+
files,
|
|
41
|
+
accept,
|
|
42
|
+
maxSizeBytes,
|
|
43
|
+
maxFiles,
|
|
44
|
+
currentCount
|
|
45
|
+
}) {
|
|
46
|
+
if (files.length === 0) {
|
|
47
|
+
return { validFiles: [], errorMessage: null };
|
|
48
|
+
}
|
|
49
|
+
if (maxFiles) {
|
|
50
|
+
const availableSlots = Math.max(0, maxFiles - currentCount);
|
|
51
|
+
if (availableSlots <= 0) {
|
|
52
|
+
return {
|
|
53
|
+
validFiles: [],
|
|
54
|
+
errorMessage: `Maximum limit of ${maxFiles} item${maxFiles > 1 ? "s" : ""} already reached.`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (files.length > availableSlots) {
|
|
58
|
+
return {
|
|
59
|
+
validFiles: [],
|
|
60
|
+
errorMessage: `You can only upload up to ${maxFiles} items. Selected ${files.length} files (${availableSlots} slot${availableSlots > 1 ? "s" : ""} available).`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const acceptedTypes = accept.split(",").map((t) => t.trim().toLowerCase());
|
|
65
|
+
const validFiles = [];
|
|
66
|
+
for (const file of files) {
|
|
67
|
+
const fileExt = `.${file.name.split(".").pop()?.toLowerCase()}`;
|
|
68
|
+
const isValidType = acceptedTypes.some((type) => {
|
|
69
|
+
if (type.startsWith(".")) {
|
|
70
|
+
return fileExt === type;
|
|
71
|
+
}
|
|
72
|
+
if (type.endsWith("/*")) {
|
|
73
|
+
return file.type.startsWith(type.replace("/*", ""));
|
|
74
|
+
}
|
|
75
|
+
return file.type.toLowerCase() === type;
|
|
76
|
+
});
|
|
77
|
+
if (!isValidType) {
|
|
78
|
+
return {
|
|
79
|
+
validFiles: [],
|
|
80
|
+
errorMessage: `File "${file.name}" has an unsupported format. Accepted: ${accept}.`
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (file.size > maxSizeBytes) {
|
|
84
|
+
const maxMb = Math.round(maxSizeBytes / (1024 * 1024));
|
|
85
|
+
return {
|
|
86
|
+
validFiles: [],
|
|
87
|
+
errorMessage: `File "${file.name}" exceeds the ${maxMb}MB size limit.`
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
validFiles.push(file);
|
|
91
|
+
}
|
|
92
|
+
return { validFiles, errorMessage: null };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { DEFAULT_ALL_ACCEPT, DEFAULT_IMAGE_ACCEPT, DEFAULT_IMAGE_MAX_SIZE, DEFAULT_VIDEO_ACCEPT, DEFAULT_VIDEO_MAX_SIZE, GRID_COLS_MAP, VIDEO_EXTENSIONS, isVideoMedia, normalizeUploadItems, validateUploadFiles };
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '@timmbr/utils';
|
|
5
|
+
import { AlertCircle } from '@timmbr/icons';
|
|
6
|
+
import { LayoutGroup, AnimatePresence } from '@timmbr/motion';
|
|
7
|
+
import { resolveMotion } from '../../types/motion.js';
|
|
8
|
+
import { DEFAULT_VIDEO_ACCEPT, DEFAULT_ALL_ACCEPT, DEFAULT_IMAGE_ACCEPT, DEFAULT_VIDEO_MAX_SIZE, DEFAULT_IMAGE_MAX_SIZE, normalizeUploadItems, validateUploadFiles, isVideoMedia, GRID_COLS_MAP } from './ImageUpload.helpers.js';
|
|
9
|
+
import { useGlobalAnimation } from '../../providers/AnimationProvider.js';
|
|
10
|
+
import { ImageUploadSinglePreview } from './subcomponents/ImageUploadSinglePreview.js';
|
|
11
|
+
import { ImageUploadDropzone } from './subcomponents/ImageUploadDropzone.js';
|
|
12
|
+
import { ImageUploadThumbnailCard, ImageUploadTaskCard } from './subcomponents/ImageUploadThumbnailCard.js';
|
|
13
|
+
import { ImageUploadModal } from './subcomponents/ImageUploadModal.js';
|
|
14
|
+
|
|
15
|
+
const ImageUpload = React.forwardRef(
|
|
16
|
+
({
|
|
17
|
+
className,
|
|
18
|
+
style,
|
|
19
|
+
value,
|
|
20
|
+
multiple = false,
|
|
21
|
+
mediaType = "image",
|
|
22
|
+
minFiles,
|
|
23
|
+
maxFiles,
|
|
24
|
+
onChange,
|
|
25
|
+
onUpload,
|
|
26
|
+
onRemove,
|
|
27
|
+
disabled = false,
|
|
28
|
+
loading = false,
|
|
29
|
+
loadingText,
|
|
30
|
+
accept: customAccept,
|
|
31
|
+
maxSizeBytes: customMaxSize,
|
|
32
|
+
label,
|
|
33
|
+
helperText,
|
|
34
|
+
error: externalError,
|
|
35
|
+
aspectRatio = "auto",
|
|
36
|
+
gridCols = 4,
|
|
37
|
+
layout = "grid",
|
|
38
|
+
enablePreviewModal = true,
|
|
39
|
+
motion: motionProp,
|
|
40
|
+
...props
|
|
41
|
+
}, ref) => {
|
|
42
|
+
const fileInputRef = React.useRef(null);
|
|
43
|
+
const dragCounterRef = React.useRef(0);
|
|
44
|
+
const globalMotion = useGlobalAnimation();
|
|
45
|
+
const { shouldAnimate, motionClass, dataAttributes } = resolveMotion(
|
|
46
|
+
motionProp,
|
|
47
|
+
globalMotion
|
|
48
|
+
);
|
|
49
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
50
|
+
const [uploadingTasks, setUploadingTasks] = React.useState([]);
|
|
51
|
+
const [localError, setLocalError] = React.useState(null);
|
|
52
|
+
const [previewModalIndex, setPreviewModalIndex] = React.useState(null);
|
|
53
|
+
const accept = React.useMemo(() => {
|
|
54
|
+
if (customAccept) return customAccept;
|
|
55
|
+
if (mediaType === "video") return DEFAULT_VIDEO_ACCEPT;
|
|
56
|
+
if (mediaType === "all") return DEFAULT_ALL_ACCEPT;
|
|
57
|
+
return DEFAULT_IMAGE_ACCEPT;
|
|
58
|
+
}, [customAccept, mediaType]);
|
|
59
|
+
const maxSizeBytes = React.useMemo(() => {
|
|
60
|
+
if (customMaxSize) return customMaxSize;
|
|
61
|
+
return mediaType === "video" || mediaType === "all" ? DEFAULT_VIDEO_MAX_SIZE : DEFAULT_IMAGE_MAX_SIZE;
|
|
62
|
+
}, [customMaxSize, mediaType]);
|
|
63
|
+
const defaultLoadingText = loadingText || (mediaType === "video" ? "Uploading video..." : "Uploading image...");
|
|
64
|
+
const items = React.useMemo(
|
|
65
|
+
() => normalizeUploadItems(value),
|
|
66
|
+
[value]
|
|
67
|
+
);
|
|
68
|
+
const isGlobalUploading = loading || uploadingTasks.length > 0;
|
|
69
|
+
const currentCount = items.length + uploadingTasks.length;
|
|
70
|
+
const isMaxReached = Boolean(maxFiles && currentCount >= maxFiles);
|
|
71
|
+
const minFilesWarning = minFiles && items.length > 0 && items.length < minFiles ? `At least ${minFiles} items are required (currently ${items.length}).` : null;
|
|
72
|
+
const error = externalError || localError;
|
|
73
|
+
const isError = Boolean(error);
|
|
74
|
+
React.useEffect(() => {
|
|
75
|
+
return () => {
|
|
76
|
+
uploadingTasks.forEach((task) => {
|
|
77
|
+
if (task.previewUrl.startsWith("blob:")) {
|
|
78
|
+
URL.revokeObjectURL(task.previewUrl);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
}, [uploadingTasks]);
|
|
83
|
+
const handleFiles = React.useCallback(
|
|
84
|
+
async (rawFiles) => {
|
|
85
|
+
setLocalError(null);
|
|
86
|
+
const filesToProcess = multiple ? rawFiles : rawFiles.slice(0, 1);
|
|
87
|
+
const { validFiles, errorMessage } = validateUploadFiles({
|
|
88
|
+
files: filesToProcess,
|
|
89
|
+
accept,
|
|
90
|
+
maxSizeBytes,
|
|
91
|
+
maxFiles,
|
|
92
|
+
currentCount
|
|
93
|
+
});
|
|
94
|
+
if (errorMessage) {
|
|
95
|
+
setLocalError(errorMessage);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (validFiles.length === 0) return;
|
|
99
|
+
onChange?.(multiple ? validFiles : validFiles[0]);
|
|
100
|
+
if (onUpload) {
|
|
101
|
+
const newTasks = validFiles.map((file) => ({
|
|
102
|
+
id: `${file.name}-${Date.now()}-${Math.random()}`,
|
|
103
|
+
name: file.name,
|
|
104
|
+
previewUrl: URL.createObjectURL(file),
|
|
105
|
+
isVideo: file.type.startsWith("video/") || isVideoMedia(file.name)
|
|
106
|
+
}));
|
|
107
|
+
setUploadingTasks((prev) => [...prev, ...newTasks]);
|
|
108
|
+
await Promise.allSettled(
|
|
109
|
+
validFiles.map(async (file, idx) => {
|
|
110
|
+
const taskId = newTasks[idx].id;
|
|
111
|
+
try {
|
|
112
|
+
await onUpload(file);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
const msg = err instanceof Error ? err.message : `Failed to upload "${file.name}".`;
|
|
115
|
+
setLocalError(msg);
|
|
116
|
+
} finally {
|
|
117
|
+
setUploadingTasks((prev) => {
|
|
118
|
+
const task = prev.find((t) => t.id === taskId);
|
|
119
|
+
if (task?.previewUrl.startsWith("blob:")) {
|
|
120
|
+
URL.revokeObjectURL(task.previewUrl);
|
|
121
|
+
}
|
|
122
|
+
return prev.filter((t) => t.id !== taskId);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
[accept, currentCount, maxFiles, maxSizeBytes, multiple, onChange, onUpload]
|
|
130
|
+
);
|
|
131
|
+
const handleDragEnter = (e) => {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
e.stopPropagation();
|
|
134
|
+
dragCounterRef.current += 1;
|
|
135
|
+
if (!disabled && !isGlobalUploading && !isMaxReached) {
|
|
136
|
+
setIsDragging(true);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const handleDragOver = (e) => {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
e.stopPropagation();
|
|
142
|
+
};
|
|
143
|
+
const handleDragLeave = (e) => {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
e.stopPropagation();
|
|
146
|
+
dragCounterRef.current -= 1;
|
|
147
|
+
if (dragCounterRef.current <= 0) {
|
|
148
|
+
dragCounterRef.current = 0;
|
|
149
|
+
setIsDragging(false);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const handleDrop = (e) => {
|
|
153
|
+
e.preventDefault();
|
|
154
|
+
e.stopPropagation();
|
|
155
|
+
dragCounterRef.current = 0;
|
|
156
|
+
setIsDragging(false);
|
|
157
|
+
if (disabled || isGlobalUploading || isMaxReached) return;
|
|
158
|
+
const droppedFiles = Array.from(e.dataTransfer.files || []);
|
|
159
|
+
if (droppedFiles.length > 0) {
|
|
160
|
+
handleFiles(droppedFiles);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const handleInputChange = (e) => {
|
|
164
|
+
const selectedFiles = Array.from(e.target.files || []);
|
|
165
|
+
if (selectedFiles.length > 0) {
|
|
166
|
+
handleFiles(selectedFiles);
|
|
167
|
+
}
|
|
168
|
+
if (fileInputRef.current) {
|
|
169
|
+
fileInputRef.current.value = "";
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const handleSingleRemove = (e) => {
|
|
173
|
+
e.stopPropagation();
|
|
174
|
+
setLocalError(null);
|
|
175
|
+
onRemove?.(0, items[0]);
|
|
176
|
+
};
|
|
177
|
+
const handleItemRemove = (e, index, item) => {
|
|
178
|
+
e.stopPropagation();
|
|
179
|
+
setLocalError(null);
|
|
180
|
+
if (previewModalIndex === index) {
|
|
181
|
+
setPreviewModalIndex(null);
|
|
182
|
+
}
|
|
183
|
+
onRemove?.(index, item);
|
|
184
|
+
};
|
|
185
|
+
const openPreviewModal = (e, index) => {
|
|
186
|
+
e.stopPropagation();
|
|
187
|
+
if (enablePreviewModal) {
|
|
188
|
+
setPreviewModalIndex(index);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const gridClass = GRID_COLS_MAP[gridCols] || GRID_COLS_MAP[4];
|
|
192
|
+
return /* @__PURE__ */ jsxs(LayoutGroup, { id: "image-upload-group", children: [
|
|
193
|
+
/* @__PURE__ */ jsxs(
|
|
194
|
+
"div",
|
|
195
|
+
{
|
|
196
|
+
ref,
|
|
197
|
+
"data-slot": "image-upload",
|
|
198
|
+
className: cn("flex flex-col gap-2 w-full font-sans", className),
|
|
199
|
+
style,
|
|
200
|
+
...dataAttributes,
|
|
201
|
+
...props,
|
|
202
|
+
children: [
|
|
203
|
+
(label || maxFiles || minFiles) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 select-none", children: [
|
|
204
|
+
label && /* @__PURE__ */ jsxs("label", { className: "text-xs font-semibold text-foreground tracking-wide flex items-center gap-1.5", children: [
|
|
205
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
206
|
+
minFiles && minFiles > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-muted font-normal", children: [
|
|
207
|
+
"(Min ",
|
|
208
|
+
minFiles,
|
|
209
|
+
")"
|
|
210
|
+
] })
|
|
211
|
+
] }),
|
|
212
|
+
multiple && /* @__PURE__ */ jsxs("span", { className: "text-[11px] font-medium text-muted", children: [
|
|
213
|
+
items.length,
|
|
214
|
+
maxFiles ? ` / ${maxFiles}` : "",
|
|
215
|
+
" uploaded"
|
|
216
|
+
] })
|
|
217
|
+
] }),
|
|
218
|
+
/* @__PURE__ */ jsx(
|
|
219
|
+
"input",
|
|
220
|
+
{
|
|
221
|
+
ref: fileInputRef,
|
|
222
|
+
type: "file",
|
|
223
|
+
accept,
|
|
224
|
+
multiple,
|
|
225
|
+
className: "hidden",
|
|
226
|
+
disabled: disabled || isGlobalUploading || isMaxReached,
|
|
227
|
+
onChange: handleInputChange
|
|
228
|
+
}
|
|
229
|
+
),
|
|
230
|
+
!multiple ? items.length > 0 ? /* @__PURE__ */ jsx(
|
|
231
|
+
ImageUploadSinglePreview,
|
|
232
|
+
{
|
|
233
|
+
item: items[0],
|
|
234
|
+
aspectRatio,
|
|
235
|
+
disabled,
|
|
236
|
+
isUploading: isGlobalUploading,
|
|
237
|
+
enablePreviewModal,
|
|
238
|
+
onReplace: () => fileInputRef.current?.click(),
|
|
239
|
+
onRemove: handleSingleRemove,
|
|
240
|
+
onPreview: (e) => openPreviewModal(e, 0)
|
|
241
|
+
}
|
|
242
|
+
) : /* @__PURE__ */ jsx(
|
|
243
|
+
ImageUploadDropzone,
|
|
244
|
+
{
|
|
245
|
+
disabled,
|
|
246
|
+
isUploading: isGlobalUploading,
|
|
247
|
+
loadingText: defaultLoadingText,
|
|
248
|
+
isDragging,
|
|
249
|
+
isError,
|
|
250
|
+
aspectRatio,
|
|
251
|
+
mediaType,
|
|
252
|
+
maxSizeBytes,
|
|
253
|
+
shouldAnimate,
|
|
254
|
+
motionClass,
|
|
255
|
+
onClick: () => !disabled && !isGlobalUploading && fileInputRef.current?.click(),
|
|
256
|
+
onDragEnter: handleDragEnter,
|
|
257
|
+
onDragOver: handleDragOver,
|
|
258
|
+
onDragLeave: handleDragLeave,
|
|
259
|
+
onDrop: handleDrop
|
|
260
|
+
}
|
|
261
|
+
) : (
|
|
262
|
+
/* MULTIPLE MODE */
|
|
263
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 w-full", children: [
|
|
264
|
+
!isMaxReached ? /* @__PURE__ */ jsx(
|
|
265
|
+
ImageUploadDropzone,
|
|
266
|
+
{
|
|
267
|
+
disabled,
|
|
268
|
+
isUploading: isGlobalUploading,
|
|
269
|
+
loadingText: defaultLoadingText,
|
|
270
|
+
isDragging,
|
|
271
|
+
isError,
|
|
272
|
+
aspectRatio: items.length > 0 ? "auto" : aspectRatio,
|
|
273
|
+
size: items.length > 0 ? "compact" : "default",
|
|
274
|
+
mediaType,
|
|
275
|
+
maxSizeBytes,
|
|
276
|
+
maxFiles,
|
|
277
|
+
multiple: true,
|
|
278
|
+
hasItems: items.length > 0,
|
|
279
|
+
shouldAnimate,
|
|
280
|
+
motionClass,
|
|
281
|
+
onClick: () => !disabled && !isGlobalUploading && fileInputRef.current?.click(),
|
|
282
|
+
onDragEnter: handleDragEnter,
|
|
283
|
+
onDragOver: handleDragOver,
|
|
284
|
+
onDragLeave: handleDragLeave,
|
|
285
|
+
onDrop: handleDrop
|
|
286
|
+
}
|
|
287
|
+
) : /* @__PURE__ */ jsxs("div", { className: "w-full rounded-xl border border-dashed border-grey-300 dark:border-grey-700 bg-grey-50/50 dark:bg-grey-900/30 p-3 text-center text-xs text-muted", children: [
|
|
288
|
+
"Maximum upload limit reached (",
|
|
289
|
+
maxFiles,
|
|
290
|
+
"/",
|
|
291
|
+
maxFiles,
|
|
292
|
+
" items). Remove an item to add another."
|
|
293
|
+
] }),
|
|
294
|
+
(items.length > 0 || uploadingTasks.length > 0) && /* @__PURE__ */ jsx(
|
|
295
|
+
"div",
|
|
296
|
+
{
|
|
297
|
+
className: cn(
|
|
298
|
+
layout === "scroll" ? "flex gap-3 overflow-x-auto pb-2.5 pt-0.5 scrollbar-thin snap-x snap-mandatory scroll-smooth w-full" : "grid gap-3 w-full",
|
|
299
|
+
layout === "grid" && gridClass
|
|
300
|
+
),
|
|
301
|
+
children: /* @__PURE__ */ jsxs(AnimatePresence, { initial: false, children: [
|
|
302
|
+
items.map((item, index) => /* @__PURE__ */ jsx(
|
|
303
|
+
ImageUploadThumbnailCard,
|
|
304
|
+
{
|
|
305
|
+
item,
|
|
306
|
+
index,
|
|
307
|
+
layout,
|
|
308
|
+
disabled,
|
|
309
|
+
enablePreviewModal,
|
|
310
|
+
shouldAnimate,
|
|
311
|
+
onPreview: (e) => openPreviewModal(e, index),
|
|
312
|
+
onRemove: (e) => handleItemRemove(e, index, item)
|
|
313
|
+
},
|
|
314
|
+
item.key || item.url || index
|
|
315
|
+
)),
|
|
316
|
+
uploadingTasks.map((task) => /* @__PURE__ */ jsx(
|
|
317
|
+
ImageUploadTaskCard,
|
|
318
|
+
{
|
|
319
|
+
id: task.id,
|
|
320
|
+
name: task.name,
|
|
321
|
+
previewUrl: task.previewUrl,
|
|
322
|
+
isVideo: task.isVideo,
|
|
323
|
+
layout,
|
|
324
|
+
shouldAnimate
|
|
325
|
+
},
|
|
326
|
+
task.id
|
|
327
|
+
))
|
|
328
|
+
] })
|
|
329
|
+
}
|
|
330
|
+
)
|
|
331
|
+
] })
|
|
332
|
+
),
|
|
333
|
+
(error || minFilesWarning || helperText) && /* @__PURE__ */ jsxs(
|
|
334
|
+
"p",
|
|
335
|
+
{
|
|
336
|
+
className: cn(
|
|
337
|
+
"text-xs mt-1 flex items-center gap-1.5 leading-normal",
|
|
338
|
+
isError ? "text-destructive font-medium" : minFilesWarning ? "text-amber-600 dark:text-amber-400 font-medium" : "text-muted"
|
|
339
|
+
),
|
|
340
|
+
children: [
|
|
341
|
+
(isError || minFilesWarning) && /* @__PURE__ */ jsx(AlertCircle, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
|
|
342
|
+
/* @__PURE__ */ jsx("span", { children: error || minFilesWarning || helperText })
|
|
343
|
+
]
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
),
|
|
349
|
+
/* @__PURE__ */ jsx(
|
|
350
|
+
ImageUploadModal,
|
|
351
|
+
{
|
|
352
|
+
isOpen: enablePreviewModal && previewModalIndex !== null,
|
|
353
|
+
activeIndex: previewModalIndex,
|
|
354
|
+
items,
|
|
355
|
+
shouldAnimate,
|
|
356
|
+
onClose: () => setPreviewModalIndex(null),
|
|
357
|
+
onNavigate: (newIndex) => setPreviewModalIndex(newIndex)
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
] });
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
ImageUpload.displayName = "ImageUpload";
|
|
364
|
+
|
|
365
|
+
export { ImageUpload };
|