@timmbr/ui 1.1.0 → 1.3.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 (125) hide show
  1. package/dist/components/Accordion/Accordion.d.ts.map +1 -1
  2. package/dist/components/Alert/Alert.types.d.ts +2 -4
  3. package/dist/components/Alert/Alert.types.d.ts.map +1 -1
  4. package/dist/components/AlertDialog/AlertDialog.d.ts +16 -0
  5. package/dist/components/AlertDialog/AlertDialog.d.ts.map +1 -0
  6. package/dist/components/AlertDialog/AlertDialog.js +94 -0
  7. package/dist/components/AlertDialog/AlertDialog.styles.d.ts +8 -0
  8. package/dist/components/AlertDialog/AlertDialog.styles.d.ts.map +1 -0
  9. package/dist/components/AlertDialog/AlertDialog.styles.js +25 -0
  10. package/dist/components/AlertDialog/AlertDialog.types.d.ts +16 -0
  11. package/dist/components/AlertDialog/AlertDialog.types.d.ts.map +1 -0
  12. package/dist/components/AlertDialog/index.d.ts +4 -0
  13. package/dist/components/AlertDialog/index.d.ts.map +1 -0
  14. package/dist/components/Avatar/Avatar.styles.d.ts +1 -1
  15. package/dist/components/Dialog/Dialog.d.ts +2 -1
  16. package/dist/components/Dialog/Dialog.d.ts.map +1 -1
  17. package/dist/components/Dialog/Dialog.js +8 -6
  18. package/dist/components/Dialog/Dialog.styles.d.ts +1 -0
  19. package/dist/components/Dialog/Dialog.styles.d.ts.map +1 -1
  20. package/dist/components/Dialog/Dialog.styles.js +7 -4
  21. package/dist/components/Dialog/Dialog.types.d.ts +4 -6
  22. package/dist/components/Dialog/Dialog.types.d.ts.map +1 -1
  23. package/dist/components/Drawer/Drawer.types.d.ts +3 -6
  24. package/dist/components/Drawer/Drawer.types.d.ts.map +1 -1
  25. package/dist/components/Dropdown/Dropdown.types.d.ts +1 -2
  26. package/dist/components/Dropdown/Dropdown.types.d.ts.map +1 -1
  27. package/dist/components/FormField/FormField.js +3 -3
  28. package/dist/components/ImageUpload/ImageUpload.d.ts +4 -0
  29. package/dist/components/ImageUpload/ImageUpload.d.ts.map +1 -0
  30. package/dist/components/ImageUpload/ImageUpload.helpers.d.ts +30 -0
  31. package/dist/components/ImageUpload/ImageUpload.helpers.d.ts.map +1 -0
  32. package/dist/components/ImageUpload/ImageUpload.helpers.js +95 -0
  33. package/dist/components/ImageUpload/ImageUpload.js +365 -0
  34. package/dist/components/ImageUpload/ImageUpload.styles.d.ts +17 -0
  35. package/dist/components/ImageUpload/ImageUpload.styles.d.ts.map +1 -0
  36. package/dist/components/ImageUpload/ImageUpload.styles.js +72 -0
  37. package/dist/components/ImageUpload/ImageUpload.types.d.ts +112 -0
  38. package/dist/components/ImageUpload/ImageUpload.types.d.ts.map +1 -0
  39. package/dist/components/ImageUpload/index.d.ts +6 -0
  40. package/dist/components/ImageUpload/index.d.ts.map +1 -0
  41. package/dist/components/ImageUpload/subcomponents/ImageUploadDropzone.d.ts +25 -0
  42. package/dist/components/ImageUpload/subcomponents/ImageUploadDropzone.d.ts.map +1 -0
  43. package/dist/components/ImageUpload/subcomponents/ImageUploadDropzone.js +106 -0
  44. package/dist/components/ImageUpload/subcomponents/ImageUploadIcons.d.ts +6 -0
  45. package/dist/components/ImageUpload/subcomponents/ImageUploadIcons.d.ts.map +1 -0
  46. package/dist/components/ImageUpload/subcomponents/ImageUploadIcons.js +61 -0
  47. package/dist/components/ImageUpload/subcomponents/ImageUploadModal.d.ts +12 -0
  48. package/dist/components/ImageUpload/subcomponents/ImageUploadModal.d.ts.map +1 -0
  49. package/dist/components/ImageUpload/subcomponents/ImageUploadModal.js +137 -0
  50. package/dist/components/ImageUpload/subcomponents/ImageUploadSinglePreview.d.ts +14 -0
  51. package/dist/components/ImageUpload/subcomponents/ImageUploadSinglePreview.d.ts.map +1 -0
  52. package/dist/components/ImageUpload/subcomponents/ImageUploadSinglePreview.js +142 -0
  53. package/dist/components/ImageUpload/subcomponents/ImageUploadThumbnailCard.d.ts +23 -0
  54. package/dist/components/ImageUpload/subcomponents/ImageUploadThumbnailCard.d.ts.map +1 -0
  55. package/dist/components/ImageUpload/subcomponents/ImageUploadThumbnailCard.js +139 -0
  56. package/dist/components/ImageUpload/subcomponents/index.d.ts +6 -0
  57. package/dist/components/ImageUpload/subcomponents/index.d.ts.map +1 -0
  58. package/dist/components/Input/Input.js +3 -3
  59. package/dist/components/List/List.types.d.ts +2 -4
  60. package/dist/components/List/List.types.d.ts.map +1 -1
  61. package/dist/components/NavHeader/NavHeader.context.d.ts +20 -0
  62. package/dist/components/NavHeader/NavHeader.context.d.ts.map +1 -0
  63. package/dist/components/NavHeader/NavHeader.context.js +13 -0
  64. package/dist/components/NavHeader/NavHeader.d.ts +8 -0
  65. package/dist/components/NavHeader/NavHeader.d.ts.map +1 -0
  66. package/dist/components/NavHeader/NavHeader.helpers.d.ts +13 -0
  67. package/dist/components/NavHeader/NavHeader.helpers.d.ts.map +1 -0
  68. package/dist/components/NavHeader/NavHeader.helpers.js +14 -0
  69. package/dist/components/NavHeader/NavHeader.js +532 -0
  70. package/dist/components/NavHeader/NavHeader.styles.d.ts +67 -0
  71. package/dist/components/NavHeader/NavHeader.styles.d.ts.map +1 -0
  72. package/dist/components/NavHeader/NavHeader.styles.js +123 -0
  73. package/dist/components/NavHeader/NavHeader.types.d.ts +321 -0
  74. package/dist/components/NavHeader/NavHeader.types.d.ts.map +1 -0
  75. package/dist/components/NavHeader/index.d.ts +11 -0
  76. package/dist/components/NavHeader/index.d.ts.map +1 -0
  77. package/dist/components/NavHeader/subcomponents/NavActionOverflow.d.ts +13 -0
  78. package/dist/components/NavHeader/subcomponents/NavActionOverflow.d.ts.map +1 -0
  79. package/dist/components/NavHeader/subcomponents/NavActionOverflow.js +169 -0
  80. package/dist/components/NavHeader/subcomponents/NavActions.d.ts +22 -0
  81. package/dist/components/NavHeader/subcomponents/NavActions.d.ts.map +1 -0
  82. package/dist/components/NavHeader/subcomponents/NavActions.js +228 -0
  83. package/dist/components/NavHeader/subcomponents/NavBrand.d.ts +12 -0
  84. package/dist/components/NavHeader/subcomponents/NavBrand.d.ts.map +1 -0
  85. package/dist/components/NavHeader/subcomponents/NavBrand.js +96 -0
  86. package/dist/components/NavHeader/subcomponents/NavItems.d.ts +15 -0
  87. package/dist/components/NavHeader/subcomponents/NavItems.d.ts.map +1 -0
  88. package/dist/components/NavHeader/subcomponents/NavItems.js +141 -0
  89. package/dist/components/NavHeader/subcomponents/NavItemsOverflow.d.ts +13 -0
  90. package/dist/components/NavHeader/subcomponents/NavItemsOverflow.d.ts.map +1 -0
  91. package/dist/components/NavHeader/subcomponents/NavItemsOverflow.js +165 -0
  92. package/dist/components/NavHeader/subcomponents/NavMegaMenu.d.ts +12 -0
  93. package/dist/components/NavHeader/subcomponents/NavMegaMenu.d.ts.map +1 -0
  94. package/dist/components/NavHeader/subcomponents/NavMegaMenu.js +73 -0
  95. package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.d.ts +16 -0
  96. package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.d.ts.map +1 -0
  97. package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.js +149 -0
  98. package/dist/components/NavHeader/subcomponents/NavMobileTabContent.d.ts +11 -0
  99. package/dist/components/NavHeader/subcomponents/NavMobileTabContent.d.ts.map +1 -0
  100. package/dist/components/NavHeader/subcomponents/NavMobileTabContent.js +40 -0
  101. package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.d.ts +13 -0
  102. package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.d.ts.map +1 -0
  103. package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.js +111 -0
  104. package/dist/components/NavHeader/subcomponents/NavSearch.d.ts +15 -0
  105. package/dist/components/NavHeader/subcomponents/NavSearch.d.ts.map +1 -0
  106. package/dist/components/NavHeader/subcomponents/NavSearch.js +222 -0
  107. package/dist/components/NavHeader/subcomponents/OfferBanner.d.ts +11 -0
  108. package/dist/components/NavHeader/subcomponents/OfferBanner.d.ts.map +1 -0
  109. package/dist/components/NavHeader/subcomponents/OfferBanner.js +92 -0
  110. package/dist/components/Popover/Popover.types.d.ts +1 -2
  111. package/dist/components/Popover/Popover.types.d.ts.map +1 -1
  112. package/dist/components/Radio/Radio.js +3 -3
  113. package/dist/components/Select/Select.js +3 -3
  114. package/dist/components/Stat/Stat.types.d.ts +3 -6
  115. package/dist/components/Stat/Stat.types.d.ts.map +1 -1
  116. package/dist/components/Table/Table.types.d.ts +7 -14
  117. package/dist/components/Table/Table.types.d.ts.map +1 -1
  118. package/dist/components/Textarea/Textarea.js +3 -3
  119. package/dist/components/Toast/Toast.js +1 -1
  120. package/dist/components/Toast/Toast.types.d.ts +1 -2
  121. package/dist/components/Toast/Toast.types.d.ts.map +1 -1
  122. package/dist/index.d.ts +3 -0
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js +21 -2
  125. package/package.json +5 -4
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { cn } from '@timmbr/utils';
4
+ import { Plus } from '@timmbr/icons';
5
+ import { imageUploadZoneVariants } from '../ImageUpload.styles.js';
6
+ import { UploadCloudIcon, ImageIcon, FilmIcon } from './ImageUploadIcons.js';
7
+ import { Spinner } from '../../Spinner/Spinner.js';
8
+
9
+ const ImageUploadDropzone = ({
10
+ disabled,
11
+ isUploading,
12
+ loadingText,
13
+ isDragging,
14
+ isError,
15
+ aspectRatio,
16
+ size = "default",
17
+ mediaType,
18
+ maxSizeBytes,
19
+ maxFiles,
20
+ multiple: _multiple = false,
21
+ hasItems = false,
22
+ shouldAnimate = true,
23
+ motionClass,
24
+ onClick,
25
+ onDragEnter,
26
+ onDragOver,
27
+ onDragLeave,
28
+ onDrop
29
+ }) => {
30
+ const maxMb = Math.round(maxSizeBytes / (1024 * 1024));
31
+ return /* @__PURE__ */ jsx(
32
+ "div",
33
+ {
34
+ role: "button",
35
+ tabIndex: 0,
36
+ onClick,
37
+ onKeyDown: (e) => {
38
+ if ((e.key === "Enter" || e.key === " ") && !disabled && !isUploading) {
39
+ onClick();
40
+ }
41
+ },
42
+ onDragEnter,
43
+ onDragOver,
44
+ onDragLeave,
45
+ onDrop,
46
+ style: aspectRatio === "square" ? {
47
+ width: "120px",
48
+ height: "120px",
49
+ minWidth: "120px",
50
+ minHeight: "120px",
51
+ maxWidth: "120px",
52
+ maxHeight: "120px"
53
+ } : void 0,
54
+ className: cn(
55
+ imageUploadZoneVariants({
56
+ aspectRatio,
57
+ size,
58
+ isDragging,
59
+ isError,
60
+ isDisabled: disabled
61
+ }),
62
+ !shouldAnimate && "transition-none",
63
+ motionClass
64
+ ),
65
+ children: isUploading ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-2", children: [
66
+ /* @__PURE__ */ jsx(Spinner, { size: "md" }),
67
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: loadingText })
68
+ ] }) : size === "compact" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-1.5 text-center pointer-events-none", children: [
69
+ /* @__PURE__ */ jsx("div", { className: "size-8 rounded-lg bg-white dark:bg-grey-800 border border-grey-200 dark:border-grey-700 shadow-xs flex items-center justify-center text-primary", children: isDragging ? /* @__PURE__ */ jsx(UploadCloudIcon, { className: "size-4 text-primary animate-bounce" }) : /* @__PURE__ */ jsx(Plus, { className: "size-4 text-primary" }) }),
70
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
71
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
72
+ /* @__PURE__ */ jsx("span", { className: "text-primary underline font-semibold", children: hasItems ? "Add more files" : "Click to upload" }),
73
+ " ",
74
+ "or drag & drop"
75
+ ] }),
76
+ /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-muted", children: [
77
+ mediaType === "video" ? `MP4, WEBM, MOV (Max ${maxMb}MB)` : mediaType === "all" ? `Images & Videos (Max ${maxMb}MB each)` : `PNG, JPG, WEBP (Max ${maxMb}MB each)`,
78
+ maxFiles && ` • Up to ${maxFiles} items`
79
+ ] })
80
+ ] })
81
+ ] }) : aspectRatio === "square" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-1.5 text-center pointer-events-none p-1", children: [
82
+ /* @__PURE__ */ jsx("div", { className: "size-8 rounded-lg bg-white dark:bg-grey-800 border border-grey-200 dark:border-grey-700 shadow-xs flex items-center justify-center text-primary", children: isDragging ? /* @__PURE__ */ jsx(UploadCloudIcon, { className: "size-4 text-primary animate-bounce" }) : /* @__PURE__ */ jsx(ImageIcon, { className: "size-4 text-primary" }) }),
83
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
84
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-primary underline", children: "Upload" }),
85
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted", children: [
86
+ "Max ",
87
+ maxMb,
88
+ "MB"
89
+ ] })
90
+ ] })
91
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-2 text-center pointer-events-none", children: [
92
+ /* @__PURE__ */ jsx("div", { className: "size-10 rounded-xl bg-white dark:bg-grey-800 border border-grey-200 dark:border-grey-700 shadow-xs flex items-center justify-center text-primary", children: isDragging ? /* @__PURE__ */ jsx(UploadCloudIcon, { className: "size-5 text-primary animate-bounce" }) : mediaType === "video" ? /* @__PURE__ */ jsx(FilmIcon, { className: "size-5 text-primary" }) : /* @__PURE__ */ jsx(ImageIcon, { className: "size-5 text-primary" }) }),
93
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
94
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground", children: [
95
+ /* @__PURE__ */ jsx("span", { className: "text-primary underline font-semibold", children: "Click to upload" }),
96
+ " or drag and drop"
97
+ ] }),
98
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted", children: mediaType === "video" ? `MP4, WEBM, MOV (Max ${maxMb}MB)` : mediaType === "all" ? `PNG, JPG, WEBP, MP4 (Max ${maxMb}MB)` : `PNG, JPG, or WEBP (Max ${maxMb}MB)` })
99
+ ] })
100
+ ] })
101
+ }
102
+ );
103
+ };
104
+ ImageUploadDropzone.displayName = "ImageUploadDropzone";
105
+
106
+ export { ImageUploadDropzone };
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ export declare const UploadCloudIcon: React.FC<React.SVGProps<SVGSVGElement>>;
3
+ export declare const ImageIcon: React.FC<React.SVGProps<SVGSVGElement>>;
4
+ export declare const PlayIcon: React.FC<React.SVGProps<SVGSVGElement>>;
5
+ export declare const FilmIcon: React.FC<React.SVGProps<SVGSVGElement>>;
6
+ //# sourceMappingURL=ImageUploadIcons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageUploadIcons.d.ts","sourceRoot":"","sources":["../../../../src/components/ImageUpload/subcomponents/ImageUploadIcons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAcnE,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAc7D,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAI5D,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,CAmB5D,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+
3
+ const UploadCloudIcon = (props) => /* @__PURE__ */ jsxs(
4
+ "svg",
5
+ {
6
+ viewBox: "0 0 24 24",
7
+ fill: "none",
8
+ stroke: "currentColor",
9
+ strokeWidth: "2",
10
+ strokeLinecap: "round",
11
+ strokeLinejoin: "round",
12
+ ...props,
13
+ children: [
14
+ /* @__PURE__ */ jsx("path", { d: "M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242" }),
15
+ /* @__PURE__ */ jsx("path", { d: "M12 12v9" }),
16
+ /* @__PURE__ */ jsx("path", { d: "m16 16-4-4-4 4" })
17
+ ]
18
+ }
19
+ );
20
+ const ImageIcon = (props) => /* @__PURE__ */ jsxs(
21
+ "svg",
22
+ {
23
+ viewBox: "0 0 24 24",
24
+ fill: "none",
25
+ stroke: "currentColor",
26
+ strokeWidth: "2",
27
+ strokeLinecap: "round",
28
+ strokeLinejoin: "round",
29
+ ...props,
30
+ children: [
31
+ /* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
32
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "2" }),
33
+ /* @__PURE__ */ jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
34
+ ]
35
+ }
36
+ );
37
+ const PlayIcon = (props) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", stroke: "none", ...props, children: /* @__PURE__ */ jsx("polygon", { points: "6 3 20 12 6 21 6 3" }) });
38
+ const FilmIcon = (props) => /* @__PURE__ */ jsxs(
39
+ "svg",
40
+ {
41
+ viewBox: "0 0 24 24",
42
+ fill: "none",
43
+ stroke: "currentColor",
44
+ strokeWidth: "2",
45
+ strokeLinecap: "round",
46
+ strokeLinejoin: "round",
47
+ ...props,
48
+ children: [
49
+ /* @__PURE__ */ jsx("rect", { width: "20", height: "20", x: "2", y: "2", rx: "2.18", ry: "2.18" }),
50
+ /* @__PURE__ */ jsx("line", { x1: "7", x2: "7", y1: "2", y2: "22" }),
51
+ /* @__PURE__ */ jsx("line", { x1: "17", x2: "17", y1: "2", y2: "22" }),
52
+ /* @__PURE__ */ jsx("line", { x1: "2", x2: "22", y1: "12", y2: "12" }),
53
+ /* @__PURE__ */ jsx("line", { x1: "2", x2: "7", y1: "7", y2: "7" }),
54
+ /* @__PURE__ */ jsx("line", { x1: "2", x2: "7", y1: "17", y2: "17" }),
55
+ /* @__PURE__ */ jsx("line", { x1: "17", x2: "22", y1: "17", y2: "17" }),
56
+ /* @__PURE__ */ jsx("line", { x1: "17", x2: "22", y1: "7", y2: "7" })
57
+ ]
58
+ }
59
+ );
60
+
61
+ export { FilmIcon, ImageIcon, PlayIcon, UploadCloudIcon };
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import type { ImageUploadItem } from '../ImageUpload.types';
3
+ export interface ImageUploadModalProps {
4
+ isOpen: boolean;
5
+ activeIndex: number | null;
6
+ items: ImageUploadItem[];
7
+ shouldAnimate?: boolean;
8
+ onClose: () => void;
9
+ onNavigate: (newIndex: number) => void;
10
+ }
11
+ export declare const ImageUploadModal: React.FC<ImageUploadModalProps>;
12
+ //# sourceMappingURL=ImageUploadModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageUploadModal.d.ts","sourceRoot":"","sources":["../../../../src/components/ImageUpload/subcomponents/ImageUploadModal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAyI5D,CAAC"}
@@ -0,0 +1,137 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { X, ChevronLeft, ChevronRight } from '@timmbr/icons';
6
+ import { AnimatePresence, motion, getTransition } from '@timmbr/motion';
7
+ import { isVideoMedia } from '../ImageUpload.helpers.js';
8
+
9
+ const ImageUploadModal = ({
10
+ isOpen,
11
+ activeIndex,
12
+ items,
13
+ shouldAnimate = true,
14
+ onClose,
15
+ onNavigate
16
+ }) => {
17
+ const [mounted, setMounted] = React.useState(false);
18
+ React.useEffect(() => {
19
+ setMounted(true);
20
+ }, []);
21
+ const activeItem = activeIndex !== null && items[activeIndex] ? items[activeIndex] : null;
22
+ React.useEffect(() => {
23
+ if (!isOpen || activeIndex === null) return;
24
+ const handleKeyDown = (e) => {
25
+ if (e.key === "Escape") {
26
+ onClose();
27
+ } else if (e.key === "ArrowLeft" && items.length > 1) {
28
+ onNavigate(activeIndex > 0 ? activeIndex - 1 : items.length - 1);
29
+ } else if (e.key === "ArrowRight" && items.length > 1) {
30
+ onNavigate(activeIndex < items.length - 1 ? activeIndex + 1 : 0);
31
+ }
32
+ };
33
+ window.addEventListener("keydown", handleKeyDown);
34
+ return () => window.removeEventListener("keydown", handleKeyDown);
35
+ }, [activeIndex, isOpen, items.length, onClose, onNavigate]);
36
+ if (!mounted) return null;
37
+ return createPortal(
38
+ /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && activeItem && activeIndex !== null && /* @__PURE__ */ jsx(
39
+ motion.div,
40
+ {
41
+ initial: shouldAnimate ? { opacity: 0 } : false,
42
+ animate: shouldAnimate ? { opacity: 1 } : void 0,
43
+ exit: shouldAnimate ? { opacity: 0 } : void 0,
44
+ transition: getTransition("fast"),
45
+ className: "fixed inset-0 z-[100] flex flex-col items-center justify-center p-4 sm:p-6 bg-black/60 backdrop-blur-xs",
46
+ onClick: onClose,
47
+ children: /* @__PURE__ */ jsxs(
48
+ motion.div,
49
+ {
50
+ initial: shouldAnimate ? { scale: 0.95, opacity: 0 } : false,
51
+ animate: shouldAnimate ? { scale: 1, opacity: 1 } : void 0,
52
+ exit: shouldAnimate ? { scale: 0.95, opacity: 0 } : void 0,
53
+ transition: getTransition("spring"),
54
+ className: "relative max-w-2xl max-h-[85vh] w-full bg-white dark:bg-grey-950 border border-grey-200 dark:border-grey-800 rounded-2xl overflow-hidden shadow-2xl flex flex-col",
55
+ onClick: (e) => e.stopPropagation(),
56
+ children: [
57
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-3 border-b border-grey-200 dark:border-grey-800 bg-grey-50/80 dark:bg-grey-900/80 backdrop-blur-xs select-none", children: [
58
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
59
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground tracking-wide truncate max-w-xs sm:max-w-md", children: activeItem.name || `Media Item #${activeIndex + 1}` }),
60
+ activeIndex === 0 && /* @__PURE__ */ jsx("span", { className: "bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 border border-emerald-500/20 text-[10px] font-semibold px-2 py-0.5 rounded-full", children: "Cover" }),
61
+ /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-muted", children: [
62
+ "(",
63
+ activeIndex + 1,
64
+ " of ",
65
+ items.length,
66
+ ")"
67
+ ] })
68
+ ] }),
69
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
70
+ "button",
71
+ {
72
+ type: "button",
73
+ onClick: onClose,
74
+ className: "size-7 rounded-full bg-grey-200/70 hover:bg-grey-300 dark:bg-grey-800 dark:hover:bg-grey-700 text-foreground flex items-center justify-center transition-colors cursor-pointer",
75
+ title: "Close preview (Esc)",
76
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
77
+ }
78
+ ) })
79
+ ] }),
80
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 flex items-center justify-center p-4 min-h-[260px] max-h-[70vh] bg-grey-100/50 dark:bg-black/50 overflow-hidden", children: [
81
+ isVideoMedia(activeItem) ? /* @__PURE__ */ jsx(
82
+ "video",
83
+ {
84
+ src: activeItem.url,
85
+ controls: true,
86
+ autoPlay: true,
87
+ playsInline: true,
88
+ className: "max-w-full max-h-[65vh] rounded-lg shadow-md"
89
+ }
90
+ ) : /* @__PURE__ */ jsx(
91
+ "img",
92
+ {
93
+ src: activeItem.url,
94
+ alt: activeItem.name || "Preview full size",
95
+ className: "max-w-full max-h-[65vh] object-contain rounded-lg shadow-md select-none"
96
+ }
97
+ ),
98
+ items.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
99
+ /* @__PURE__ */ jsx(
100
+ "button",
101
+ {
102
+ type: "button",
103
+ onClick: (e) => {
104
+ e.stopPropagation();
105
+ onNavigate(activeIndex > 0 ? activeIndex - 1 : items.length - 1);
106
+ },
107
+ className: "absolute left-3 top-1/2 -translate-y-1/2 size-9 rounded-full bg-white/90 hover:bg-white dark:bg-grey-800/90 dark:hover:bg-grey-800 border border-grey-200 dark:border-grey-700 text-foreground flex items-center justify-center transition-all backdrop-blur-xs cursor-pointer shadow-md",
108
+ title: "Previous (Left Arrow)",
109
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
110
+ }
111
+ ),
112
+ /* @__PURE__ */ jsx(
113
+ "button",
114
+ {
115
+ type: "button",
116
+ onClick: (e) => {
117
+ e.stopPropagation();
118
+ onNavigate(activeIndex < items.length - 1 ? activeIndex + 1 : 0);
119
+ },
120
+ className: "absolute right-3 top-1/2 -translate-y-1/2 size-9 rounded-full bg-white/90 hover:bg-white dark:bg-grey-800/90 dark:hover:bg-grey-800 border border-grey-200 dark:border-grey-700 text-foreground flex items-center justify-center transition-all backdrop-blur-xs cursor-pointer shadow-md",
121
+ title: "Next (Right Arrow)",
122
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
123
+ }
124
+ )
125
+ ] })
126
+ ] })
127
+ ]
128
+ }
129
+ )
130
+ }
131
+ ) }),
132
+ document.body
133
+ );
134
+ };
135
+ ImageUploadModal.displayName = "ImageUploadModal";
136
+
137
+ export { ImageUploadModal };
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ import type { ImageUploadItem, ImageUploadAspectRatio } from '../ImageUpload.types';
3
+ export interface ImageUploadSinglePreviewProps {
4
+ item: ImageUploadItem;
5
+ aspectRatio: ImageUploadAspectRatio;
6
+ disabled?: boolean;
7
+ isUploading?: boolean;
8
+ enablePreviewModal?: boolean;
9
+ onReplace: () => void;
10
+ onRemove: (e: React.MouseEvent) => void;
11
+ onPreview: (e: React.MouseEvent) => void;
12
+ }
13
+ export declare const ImageUploadSinglePreview: React.FC<ImageUploadSinglePreviewProps>;
14
+ //# sourceMappingURL=ImageUploadSinglePreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageUploadSinglePreview.d.ts","sourceRoot":"","sources":["../../../../src/components/ImageUpload/subcomponents/ImageUploadSinglePreview.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACvB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,eAAe,CAAC;IACtB,WAAW,EAAE,sBAAsB,CAAC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACxC,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;CAC1C;AAED,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC,6BAA6B,CAqI5E,CAAC"}
@@ -0,0 +1,142 @@
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { cn } from '@timmbr/utils';
4
+ import { Eye, Trash2 } from '@timmbr/icons';
5
+ import { imagePreviewContainerVariants } from '../ImageUpload.styles.js';
6
+ import { PlayIcon, UploadCloudIcon } from './ImageUploadIcons.js';
7
+ import { isVideoMedia } from '../ImageUpload.helpers.js';
8
+ import { Button } from '../../Button/Button.js';
9
+
10
+ const ImageUploadSinglePreview = ({
11
+ item,
12
+ aspectRatio,
13
+ disabled = false,
14
+ isUploading = false,
15
+ enablePreviewModal = true,
16
+ onReplace,
17
+ onRemove,
18
+ onPreview
19
+ }) => {
20
+ const isVideo = isVideoMedia(item);
21
+ return /* @__PURE__ */ jsxs(
22
+ "div",
23
+ {
24
+ style: aspectRatio === "square" ? {
25
+ width: "120px",
26
+ height: "120px",
27
+ minWidth: "120px",
28
+ minHeight: "120px",
29
+ maxWidth: "120px",
30
+ maxHeight: "120px"
31
+ } : void 0,
32
+ className: cn(imagePreviewContainerVariants({ aspectRatio })),
33
+ children: [
34
+ isVideo ? /* @__PURE__ */ jsx(
35
+ "video",
36
+ {
37
+ src: item.url,
38
+ className: "w-full h-full object-cover",
39
+ muted: true,
40
+ loop: true,
41
+ playsInline: true,
42
+ onMouseEnter: (e) => e.currentTarget.play().catch(() => {
43
+ }),
44
+ onMouseLeave: (e) => {
45
+ const v = e.currentTarget;
46
+ v.pause();
47
+ v.currentTime = 0;
48
+ }
49
+ }
50
+ ) : /* @__PURE__ */ jsx(
51
+ "img",
52
+ {
53
+ src: item.url,
54
+ alt: item.name || "Uploaded preview",
55
+ className: cn(
56
+ "w-full h-full",
57
+ aspectRatio === "square" ? "object-contain p-2" : "object-cover"
58
+ )
59
+ }
60
+ ),
61
+ isVideo && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 left-2 z-10 bg-black/65 backdrop-blur-md text-white text-[10px] font-medium px-2 py-0.5 rounded-full border border-white/20 flex items-center gap-1 shadow-sm pointer-events-none", children: [
62
+ /* @__PURE__ */ jsx(PlayIcon, { className: "size-2.5" }),
63
+ /* @__PURE__ */ jsx("span", { children: "Video" })
64
+ ] }),
65
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center gap-1.5 p-1 backdrop-blur-[2px]", children: aspectRatio === "square" ? /* @__PURE__ */ jsxs(Fragment, { children: [
66
+ enablePreviewModal && /* @__PURE__ */ jsx(
67
+ "button",
68
+ {
69
+ type: "button",
70
+ className: "size-8 rounded-lg bg-black/60 hover:bg-black/85 text-white flex items-center justify-center border border-white/20 transition-all cursor-pointer shadow-xs",
71
+ onClick: onPreview,
72
+ title: "Preview full size",
73
+ children: /* @__PURE__ */ jsx(Eye, { className: "size-3.5" })
74
+ }
75
+ ),
76
+ /* @__PURE__ */ jsx(
77
+ "button",
78
+ {
79
+ type: "button",
80
+ disabled: disabled || isUploading,
81
+ onClick: onReplace,
82
+ className: "size-8 rounded-lg bg-primary hover:bg-primary/90 text-white flex items-center justify-center transition-all cursor-pointer shadow-xs disabled:opacity-50",
83
+ title: "Replace image",
84
+ children: /* @__PURE__ */ jsx(UploadCloudIcon, { className: "size-3.5" })
85
+ }
86
+ ),
87
+ /* @__PURE__ */ jsx(
88
+ "button",
89
+ {
90
+ type: "button",
91
+ disabled: disabled || isUploading,
92
+ onClick: onRemove,
93
+ className: "size-8 rounded-lg bg-destructive hover:bg-destructive/90 text-white flex items-center justify-center transition-all cursor-pointer shadow-xs disabled:opacity-50",
94
+ title: "Remove image",
95
+ children: /* @__PURE__ */ jsx(Trash2, { className: "size-3.5" })
96
+ }
97
+ )
98
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
99
+ enablePreviewModal && /* @__PURE__ */ jsx(
100
+ Button,
101
+ {
102
+ type: "button",
103
+ variant: "outline",
104
+ size: "sm",
105
+ className: "border-white/40 bg-black/40 text-white hover:bg-black/60 hover:text-white backdrop-blur-xs",
106
+ onClick: onPreview,
107
+ leftIcon: /* @__PURE__ */ jsx(Eye, { className: "size-3.5" }),
108
+ children: "Preview"
109
+ }
110
+ ),
111
+ /* @__PURE__ */ jsx(
112
+ Button,
113
+ {
114
+ type: "button",
115
+ variant: "default",
116
+ size: "sm",
117
+ disabled: disabled || isUploading,
118
+ onClick: onReplace,
119
+ children: "Replace"
120
+ }
121
+ ),
122
+ /* @__PURE__ */ jsx(
123
+ Button,
124
+ {
125
+ type: "button",
126
+ variant: "outline",
127
+ size: "sm",
128
+ disabled: disabled || isUploading,
129
+ className: "border-destructive/60 bg-destructive/80 text-white hover:bg-destructive hover:text-white",
130
+ onClick: onRemove,
131
+ leftIcon: /* @__PURE__ */ jsx(Trash2, { className: "size-3.5" }),
132
+ children: "Remove"
133
+ }
134
+ )
135
+ ] }) })
136
+ ]
137
+ }
138
+ );
139
+ };
140
+ ImageUploadSinglePreview.displayName = "ImageUploadSinglePreview";
141
+
142
+ export { ImageUploadSinglePreview };
@@ -0,0 +1,23 @@
1
+ import * as React from 'react';
2
+ import type { ImageUploadItem } from '../ImageUpload.types';
3
+ export interface ImageUploadThumbnailCardProps {
4
+ item: ImageUploadItem;
5
+ index: number;
6
+ layout?: 'grid' | 'scroll';
7
+ disabled?: boolean;
8
+ enablePreviewModal?: boolean;
9
+ shouldAnimate?: boolean;
10
+ onPreview: (e: React.MouseEvent) => void;
11
+ onRemove: (e: React.MouseEvent) => void;
12
+ }
13
+ export declare const ImageUploadThumbnailCard: React.FC<ImageUploadThumbnailCardProps>;
14
+ export interface ImageUploadTaskCardProps {
15
+ id: string;
16
+ name: string;
17
+ previewUrl: string;
18
+ isVideo: boolean;
19
+ layout?: 'grid' | 'scroll';
20
+ shouldAnimate?: boolean;
21
+ }
22
+ export declare const ImageUploadTaskCard: React.FC<ImageUploadTaskCardProps>;
23
+ //# sourceMappingURL=ImageUploadThumbnailCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageUploadThumbnailCard.d.ts","sourceRoot":"","sources":["../../../../src/components/ImageUpload/subcomponents/ImageUploadThumbnailCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;CACzC;AAED,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC,6BAA6B,CAwF5E,CAAC;AAIF,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA4ClE,CAAC"}