@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,165 @@
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { AnimatePresence, motion } from '@timmbr/motion';
5
+ import { MoreHorizontal } from '@timmbr/icons';
6
+ import { cn } from '@timmbr/utils';
7
+ import { navHeaderItemVariants } from '../NavHeader.styles.js';
8
+ import { resolveNavDestination, isCrossZoneNavigation } from '../NavHeader.helpers.js';
9
+ import { useTimmbrConfig } from '../../../providers/TimmbrConfigProvider.js';
10
+
11
+ const NavItemsOverflow = ({
12
+ items,
13
+ activeItemId,
14
+ onItemClick,
15
+ shouldAnimate = true,
16
+ motionClass,
17
+ linkComponent: CustomLink
18
+ }) => {
19
+ const [isOpen, setIsOpen] = React.useState(false);
20
+ const containerRef = React.useRef(null);
21
+ const config = useTimmbrConfig();
22
+ const isChildActive = items.some((item) => item.id === activeItemId);
23
+ React.useEffect(() => {
24
+ if (!isOpen) return;
25
+ const handleClickOutside = (e) => {
26
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
27
+ setIsOpen(false);
28
+ }
29
+ };
30
+ const handleKeyDown = (e) => {
31
+ if (e.key === "Escape") {
32
+ setIsOpen(false);
33
+ }
34
+ };
35
+ document.addEventListener("mousedown", handleClickOutside);
36
+ document.addEventListener("keydown", handleKeyDown);
37
+ return () => {
38
+ document.removeEventListener("mousedown", handleClickOutside);
39
+ document.removeEventListener("keydown", handleKeyDown);
40
+ };
41
+ }, [isOpen]);
42
+ if (!items || items.length === 0) {
43
+ return null;
44
+ }
45
+ return /* @__PURE__ */ jsxs(
46
+ "div",
47
+ {
48
+ ref: containerRef,
49
+ className: "relative flex items-center h-full",
50
+ "data-slot": "nav-items-overflow",
51
+ children: [
52
+ /* @__PURE__ */ jsxs(
53
+ "button",
54
+ {
55
+ type: "button",
56
+ "aria-label": "More navigation categories",
57
+ "aria-expanded": isOpen,
58
+ "aria-haspopup": "menu",
59
+ onClick: () => setIsOpen((prev) => !prev),
60
+ className: cn(
61
+ navHeaderItemVariants({ active: isOpen || isChildActive }),
62
+ "px-2 py-2 flex items-center justify-center rounded-lg hover:bg-black/5 transition-colors",
63
+ !shouldAnimate && "transition-none",
64
+ motionClass
65
+ ),
66
+ "data-slot": "nav-items-overflow-trigger",
67
+ children: [
68
+ /* @__PURE__ */ jsx(MoreHorizontal, { size: 20, className: "stroke-[2]" }),
69
+ (isOpen || isChildActive) && /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-2 right-2 h-[2.5px] bg-[#C0643A] rounded-full" })
70
+ ]
71
+ }
72
+ ),
73
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx(
74
+ motion.div,
75
+ {
76
+ initial: shouldAnimate ? { opacity: 0, y: 6, scale: 0.97 } : false,
77
+ animate: shouldAnimate ? { opacity: 1, y: 0, scale: 1 } : void 0,
78
+ exit: shouldAnimate ? { opacity: 0, y: 6, scale: 0.97 } : void 0,
79
+ transition: { duration: 0.18, ease: [0.25, 1, 0.5, 1] },
80
+ className: cn(
81
+ "absolute top-full left-0 mt-2 w-56 bg-white border border-[#E7DFD3] shadow-2xl rounded-2xl p-2 z-50 text-left overflow-hidden divide-y divide-grey-100",
82
+ !shouldAnimate && "transition-none",
83
+ motionClass
84
+ ),
85
+ role: "menu",
86
+ "aria-orientation": "vertical",
87
+ "data-slot": "nav-items-overflow-popover",
88
+ children: /* @__PURE__ */ jsx("div", { className: "py-1 flex flex-col gap-0.5", children: items.map((item) => {
89
+ const isActive = activeItemId === item.id;
90
+ const resolvedHref = resolveNavDestination(
91
+ item.href,
92
+ item.zone,
93
+ config.zones?.zones
94
+ );
95
+ const requiresCrossZone = isCrossZoneNavigation(
96
+ item.zone,
97
+ config.zones?.currentZone,
98
+ item.crossZone
99
+ );
100
+ const itemRowContent = /* @__PURE__ */ jsxs(Fragment, { children: [
101
+ /* @__PURE__ */ jsx("span", { className: cn("truncate", isActive && "text-[#C0643A] font-bold"), children: item.label }),
102
+ item.badge && /* @__PURE__ */ jsx("span", { className: "shrink-0 px-1.5 py-0.5 rounded-full bg-primary-100 text-primary-800 text-[10px] font-bold", children: item.badge })
103
+ ] });
104
+ const itemClass = cn(
105
+ "w-full flex items-center justify-between gap-3 px-3 py-2 text-xs font-semibold text-[#3C3C3C] hover:text-[#C0643A] hover:bg-[#F7F1E6]/60 rounded-xl transition-colors cursor-pointer text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#C0643A]",
106
+ item.disabled && "opacity-40 cursor-not-allowed pointer-events-none",
107
+ isActive && "bg-[#F7F1E6]/80 text-[#C0643A]",
108
+ item.className
109
+ );
110
+ const handleClick = (e) => {
111
+ if (item.disabled) {
112
+ e.preventDefault();
113
+ return;
114
+ }
115
+ onItemClick(item.id, item);
116
+ setIsOpen(false);
117
+ };
118
+ if (resolvedHref && !item.disabled) {
119
+ if (CustomLink && !requiresCrossZone) {
120
+ return /* @__PURE__ */ jsx(
121
+ CustomLink,
122
+ {
123
+ href: resolvedHref,
124
+ role: "menuitem",
125
+ className: itemClass,
126
+ onClick: handleClick,
127
+ children: itemRowContent
128
+ },
129
+ item.id
130
+ );
131
+ }
132
+ return /* @__PURE__ */ jsx(
133
+ "a",
134
+ {
135
+ href: resolvedHref,
136
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
137
+ role: "menuitem",
138
+ className: itemClass,
139
+ onClick: handleClick,
140
+ children: itemRowContent
141
+ },
142
+ item.id
143
+ );
144
+ }
145
+ return /* @__PURE__ */ jsx(
146
+ "button",
147
+ {
148
+ type: "button",
149
+ role: "menuitem",
150
+ disabled: item.disabled,
151
+ onClick: handleClick,
152
+ className: itemClass,
153
+ children: itemRowContent
154
+ },
155
+ item.id
156
+ );
157
+ }) })
158
+ }
159
+ ) })
160
+ ]
161
+ }
162
+ );
163
+ };
164
+
165
+ export { NavItemsOverflow };
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import type { NavHeaderItem } from '../NavHeader.types';
3
+ export interface NavMegaMenuProps {
4
+ activeItem: NavHeaderItem | null;
5
+ onMouseEnter?: () => void;
6
+ onMouseLeave?: () => void;
7
+ onClose?: () => void;
8
+ shouldAnimate?: boolean;
9
+ motionClass?: string;
10
+ }
11
+ export declare const NavMegaMenu: React.FC<NavMegaMenuProps>;
12
+ //# sourceMappingURL=NavMegaMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavMegaMenu.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/NavMegaMenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAqElD,CAAC"}
@@ -0,0 +1,73 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { AnimatePresence, motion, headerMegaMenuVariants, getTransition } from '@timmbr/motion';
5
+ import { cn } from '@timmbr/utils';
6
+ import { megaMenuCardVariants } from '../NavHeader.styles.js';
7
+
8
+ const NavMegaMenu = ({
9
+ activeItem,
10
+ onMouseEnter,
11
+ onMouseLeave,
12
+ onClose,
13
+ shouldAnimate = true,
14
+ motionClass
15
+ }) => {
16
+ const hasContent = Boolean(activeItem?.content);
17
+ React.useEffect(() => {
18
+ if (!hasContent) return;
19
+ const handleKeyDown = (e) => {
20
+ if (e.key === "Escape") {
21
+ onClose?.();
22
+ }
23
+ };
24
+ window.addEventListener("keydown", handleKeyDown);
25
+ return () => window.removeEventListener("keydown", handleKeyDown);
26
+ }, [hasContent, onClose]);
27
+ const widthStyle = React.useMemo(() => {
28
+ if (typeof activeItem?.popoverWidth === "number") {
29
+ return { maxWidth: `${activeItem.popoverWidth}px` };
30
+ }
31
+ return {};
32
+ }, [activeItem?.popoverWidth]);
33
+ const isFullBleed = activeItem?.popoverWidth === "full";
34
+ return /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: hasContent && activeItem && /* @__PURE__ */ jsx(
35
+ "div",
36
+ {
37
+ "data-slot": "nav-megamenu-wrapper",
38
+ className: "absolute top-full left-0 right-0 z-50 pt-1 pointer-events-none",
39
+ children: /* @__PURE__ */ jsx(
40
+ "div",
41
+ {
42
+ className: "w-full flex justify-center pointer-events-auto",
43
+ onMouseEnter,
44
+ onMouseLeave,
45
+ children: /* @__PURE__ */ jsx(
46
+ motion.div,
47
+ {
48
+ role: "region",
49
+ "aria-label": `${typeof activeItem.label === "string" ? activeItem.label : "Navigation"} Submenu`,
50
+ initial: shouldAnimate ? "hidden" : false,
51
+ animate: shouldAnimate ? "visible" : void 0,
52
+ exit: shouldAnimate ? "exit" : void 0,
53
+ variants: shouldAnimate ? headerMegaMenuVariants : void 0,
54
+ transition: getTransition("fast"),
55
+ style: widthStyle,
56
+ "data-slot": "nav-megamenu-card",
57
+ className: cn(
58
+ megaMenuCardVariants(),
59
+ isFullBleed && "max-w-none rounded-none border-x-0",
60
+ !shouldAnimate && "transition-none",
61
+ motionClass
62
+ ),
63
+ children: activeItem.content
64
+ },
65
+ activeItem.id
66
+ )
67
+ }
68
+ )
69
+ }
70
+ ) });
71
+ };
72
+
73
+ export { NavMegaMenu };
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+ import type { NavSearchConfig } from '../NavHeader.types';
3
+ export interface NavMobileSearchDrawerProps {
4
+ isOpen: boolean;
5
+ onClose: () => void;
6
+ value: string;
7
+ onChange: (value: string) => void;
8
+ onSubmit: (value: string) => void;
9
+ config?: NavSearchConfig | boolean;
10
+ shouldAnimate?: boolean;
11
+ motionClass?: string;
12
+ linkComponent?: React.ComponentType<any>;
13
+ }
14
+ export declare const NavMobileSearchDrawer: React.FC<NavMobileSearchDrawerProps>;
15
+ export default NavMobileSearchDrawer;
16
+ //# sourceMappingURL=NavMobileSearchDrawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavMobileSearchDrawer.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/NavMobileSearchDrawer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;CAC1C;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAoKtE,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,149 @@
1
+ "use client";
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { AnimatePresence, motion } from '@timmbr/motion';
6
+ import { ArrowLeft, Search, X } from '@timmbr/icons';
7
+ import { cn } from '@timmbr/utils';
8
+ import { mobileSearchDrawerVariants } from '../NavHeader.styles.js';
9
+
10
+ const NavMobileSearchDrawer = ({
11
+ isOpen,
12
+ onClose,
13
+ value,
14
+ onChange,
15
+ onSubmit,
16
+ config = {},
17
+ shouldAnimate = true,
18
+ motionClass
19
+ }) => {
20
+ const [mounted, setMounted] = React.useState(false);
21
+ const searchConfig = typeof config === "object" ? config : {};
22
+ const inputRef = React.useRef(null);
23
+ React.useEffect(() => {
24
+ setMounted(true);
25
+ }, []);
26
+ React.useEffect(() => {
27
+ if (!isOpen) return;
28
+ const originalOverflow = document.body.style.overflow;
29
+ document.body.style.overflow = "hidden";
30
+ return () => {
31
+ document.body.style.overflow = originalOverflow;
32
+ };
33
+ }, [isOpen]);
34
+ React.useEffect(() => {
35
+ if (isOpen) {
36
+ const timer = setTimeout(() => {
37
+ inputRef.current?.focus();
38
+ }, 100);
39
+ return () => clearTimeout(timer);
40
+ }
41
+ }, [isOpen]);
42
+ React.useEffect(() => {
43
+ if (!isOpen) return;
44
+ const handleKeyDown = (e) => {
45
+ if (e.key === "Escape") {
46
+ onClose();
47
+ }
48
+ };
49
+ document.addEventListener("keydown", handleKeyDown);
50
+ return () => document.removeEventListener("keydown", handleKeyDown);
51
+ }, [isOpen, onClose]);
52
+ const handleClear = () => {
53
+ onChange("");
54
+ inputRef.current?.focus();
55
+ };
56
+ const handleFormSubmit = (e) => {
57
+ e.preventDefault();
58
+ if (value.trim()) {
59
+ onSubmit(value);
60
+ onClose();
61
+ }
62
+ };
63
+ const handlePopularSearchClick = (tagLabel) => {
64
+ onChange(tagLabel);
65
+ onSubmit(tagLabel);
66
+ onClose();
67
+ };
68
+ const drawerContent = /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsxs(
69
+ motion.div,
70
+ {
71
+ role: "dialog",
72
+ "aria-modal": "true",
73
+ "aria-label": "Search drawer",
74
+ initial: shouldAnimate ? { opacity: 0, y: "-8px" } : false,
75
+ animate: shouldAnimate ? { opacity: 1, y: 0 } : void 0,
76
+ exit: shouldAnimate ? { opacity: 0, y: "-8px" } : void 0,
77
+ transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
78
+ className: cn(mobileSearchDrawerVariants(), motionClass),
79
+ "data-slot": "nav-mobile-search-drawer",
80
+ children: [
81
+ /* @__PURE__ */ jsxs("div", { className: "w-full bg-[var(--color-bg-2,#F7F1E6)] border-b border-[#E7DFD3] px-3 py-3 flex items-center gap-3 shrink-0 shadow-xs", children: [
82
+ /* @__PURE__ */ jsx(
83
+ "button",
84
+ {
85
+ type: "button",
86
+ onClick: onClose,
87
+ "aria-label": "Close search and go back",
88
+ className: "w-10 h-10 rounded-full flex items-center justify-center text-grey-800 hover:text-[#C0643A] hover:bg-black/5 transition-colors cursor-pointer shrink-0",
89
+ "data-slot": "nav-mobile-search-back-btn",
90
+ children: /* @__PURE__ */ jsx(ArrowLeft, { size: 22, className: "stroke-[2]" })
91
+ }
92
+ ),
93
+ /* @__PURE__ */ jsx("form", { onSubmit: handleFormSubmit, className: "flex-1 relative flex items-center", children: /* @__PURE__ */ jsxs("div", { className: "relative w-full flex items-center", children: [
94
+ /* @__PURE__ */ jsx(
95
+ Search,
96
+ {
97
+ size: 18,
98
+ className: "absolute left-3.5 text-grey-500 pointer-events-none stroke-[2]"
99
+ }
100
+ ),
101
+ /* @__PURE__ */ jsx(
102
+ "input",
103
+ {
104
+ ref: inputRef,
105
+ type: "text",
106
+ role: "searchbox",
107
+ "aria-label": "Search store products",
108
+ value,
109
+ onChange: (e) => onChange(e.target.value),
110
+ placeholder: searchConfig.placeholder ?? "Search for furniture, decor, and more...",
111
+ className: "w-full h-11 pl-10 pr-9 rounded-full bg-white border border-[#E7DFD3] text-sm text-grey-900 placeholder:text-grey-400 focus:outline-none focus:border-[#C0643A] focus:ring-2 focus:ring-[#C0643A]/20 transition-all shadow-xs"
112
+ }
113
+ ),
114
+ value.length > 0 && /* @__PURE__ */ jsx(
115
+ "button",
116
+ {
117
+ type: "button",
118
+ onClick: handleClear,
119
+ "aria-label": "Clear search input",
120
+ className: "absolute right-3 w-6 h-6 rounded-full bg-grey-200 hover:bg-grey-300 text-grey-600 flex items-center justify-center transition-colors cursor-pointer",
121
+ children: /* @__PURE__ */ jsx(X, { size: 14, className: "stroke-[2.5]" })
122
+ }
123
+ )
124
+ ] }) })
125
+ ] }),
126
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 sm:p-6 space-y-6 bg-[var(--color-bg-2,#F7F1E6)]", children: [
127
+ searchConfig.popularSearches && searchConfig.popularSearches.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
128
+ /* @__PURE__ */ jsx("h4", { className: "font-title text-xs font-semibold text-grey-500 uppercase tracking-wider", children: "POPULAR SEARCHES" }),
129
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: searchConfig.popularSearches.map((item) => /* @__PURE__ */ jsx(
130
+ "button",
131
+ {
132
+ type: "button",
133
+ onClick: () => handlePopularSearchClick(item.label),
134
+ className: "px-3.5 py-1.5 rounded-full bg-white border border-[#E7DFD3] text-xs font-semibold text-grey-700 hover:border-[#C0643A] hover:text-[#C0643A] hover:bg-white transition-colors cursor-pointer shadow-xs",
135
+ children: item.label
136
+ },
137
+ item.id
138
+ )) })
139
+ ] }),
140
+ searchConfig.suggestions && /* @__PURE__ */ jsx("div", { className: "pt-2", children: searchConfig.suggestions })
141
+ ] })
142
+ ]
143
+ }
144
+ ) });
145
+ if (!mounted) return null;
146
+ return createPortal(drawerContent, document.body);
147
+ };
148
+
149
+ export { NavMobileSearchDrawer };
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import type { NavHeaderItem } from '../NavHeader.types';
3
+ export interface NavMobileTabContentProps {
4
+ activeItem: NavHeaderItem | null;
5
+ onClose?: () => void;
6
+ shouldAnimate?: boolean;
7
+ motionClass?: string;
8
+ }
9
+ export declare const NavMobileTabContent: React.FC<NavMobileTabContentProps>;
10
+ export default NavMobileTabContent;
11
+ //# sourceMappingURL=NavMobileTabContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavMobileTabContent.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/NavMobileTabContent.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAuClE,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { AnimatePresence, motion } from '@timmbr/motion';
5
+ import { cn } from '@timmbr/utils';
6
+ import { mobileTabContentPanelVariants } from '../NavHeader.styles.js';
7
+
8
+ const NavMobileTabContent = ({
9
+ activeItem,
10
+ onClose,
11
+ shouldAnimate = true,
12
+ motionClass
13
+ }) => {
14
+ React.useEffect(() => {
15
+ if (!activeItem || !onClose) return;
16
+ const handleKeyDown = (e) => {
17
+ if (e.key === "Escape") {
18
+ onClose();
19
+ }
20
+ };
21
+ document.addEventListener("keydown", handleKeyDown);
22
+ return () => document.removeEventListener("keydown", handleKeyDown);
23
+ }, [activeItem, onClose]);
24
+ const hasContent = Boolean(activeItem && activeItem.content);
25
+ return /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: hasContent && /* @__PURE__ */ jsx(
26
+ motion.div,
27
+ {
28
+ initial: shouldAnimate ? { height: 0, opacity: 0 } : false,
29
+ animate: shouldAnimate ? { height: "auto", opacity: 1 } : void 0,
30
+ exit: shouldAnimate ? { height: 0, opacity: 0 } : void 0,
31
+ transition: { duration: 0.22, ease: [0.25, 1, 0.5, 1] },
32
+ className: cn(mobileTabContentPanelVariants(), motionClass),
33
+ "data-slot": "nav-mobile-tab-content-panel",
34
+ children: /* @__PURE__ */ jsx("div", { className: "w-full", children: activeItem.content })
35
+ },
36
+ activeItem.id
37
+ ) });
38
+ };
39
+
40
+ export { NavMobileTabContent };
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+ import type { NavHeaderItem } from '../NavHeader.types';
3
+ export interface NavMobileTabSliderProps {
4
+ items: NavHeaderItem[];
5
+ activeItemId: string | null;
6
+ onItemClick: (id: string, item: NavHeaderItem) => void;
7
+ shouldAnimate?: boolean;
8
+ motionClass?: string;
9
+ linkComponent?: React.ComponentType<any>;
10
+ }
11
+ export declare const NavMobileTabSlider: React.FC<NavMobileTabSliderProps>;
12
+ export default NavMobileTabSlider;
13
+ //# sourceMappingURL=NavMobileTabSlider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavMobileTabSlider.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/NavMobileTabSlider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACvD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;CAC1C;AAED,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAuGhE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,111 @@
1
+ "use client";
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { motion } from '@timmbr/motion';
5
+ import { cn } from '@timmbr/utils';
6
+ import { mobileTabItemVariants, mobileTabSliderVariants } from '../NavHeader.styles.js';
7
+ import { resolveNavDestination, isCrossZoneNavigation } from '../NavHeader.helpers.js';
8
+ import { useTimmbrConfig } from '../../../providers/TimmbrConfigProvider.js';
9
+
10
+ const NavMobileTabSlider = ({
11
+ items,
12
+ activeItemId,
13
+ onItemClick,
14
+ shouldAnimate = true,
15
+ motionClass,
16
+ linkComponent: CustomLink
17
+ }) => {
18
+ const config = useTimmbrConfig();
19
+ const activeTabRef = React.useRef(null);
20
+ React.useEffect(() => {
21
+ if (activeTabRef.current && typeof activeTabRef.current.scrollIntoView === "function") {
22
+ activeTabRef.current.scrollIntoView({
23
+ behavior: "smooth",
24
+ block: "nearest",
25
+ inline: "center"
26
+ });
27
+ }
28
+ }, [activeItemId]);
29
+ return /* @__PURE__ */ jsx(
30
+ "nav",
31
+ {
32
+ "aria-label": "Category Navigation",
33
+ className: cn(mobileTabSliderVariants(), motionClass),
34
+ "data-slot": "nav-mobile-tab-slider",
35
+ children: items.map((item) => {
36
+ const isActive = activeItemId === item.id;
37
+ const resolvedHref = resolveNavDestination(item.href, item.zone, config.zones?.zones);
38
+ const requiresCrossZone = isCrossZoneNavigation(item.zone, config.zones?.currentZone, item.crossZone);
39
+ const ariaLabelText = item.ariaLabel ?? (typeof item.label === "string" ? item.label : void 0);
40
+ const tabIndicator = isActive && shouldAnimate ? /* @__PURE__ */ jsx(
41
+ motion.div,
42
+ {
43
+ layoutId: "active-mobile-tab-indicator",
44
+ className: "absolute bottom-0 left-0 right-0 h-[2.5px] bg-[#C0643A] rounded-full",
45
+ transition: { type: "spring", stiffness: 380, damping: 30 }
46
+ }
47
+ ) : isActive ? /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 h-[2.5px] bg-[#C0643A] rounded-full" }) : null;
48
+ if (resolvedHref && !item.content && !item.disabled) {
49
+ if (CustomLink && !requiresCrossZone) {
50
+ return /* @__PURE__ */ jsxs(
51
+ CustomLink,
52
+ {
53
+ ref: isActive ? activeTabRef : void 0,
54
+ href: resolvedHref,
55
+ role: "tab",
56
+ "aria-selected": isActive,
57
+ "aria-label": ariaLabelText,
58
+ className: cn(mobileTabItemVariants({ active: isActive, disabled: item.disabled }), item.className),
59
+ onClick: () => onItemClick(item.id, item),
60
+ children: [
61
+ /* @__PURE__ */ jsx("span", { children: item.label }),
62
+ tabIndicator
63
+ ]
64
+ },
65
+ item.id
66
+ );
67
+ }
68
+ return /* @__PURE__ */ jsxs(
69
+ "a",
70
+ {
71
+ ref: isActive ? activeTabRef : void 0,
72
+ href: resolvedHref,
73
+ role: "tab",
74
+ "aria-selected": isActive,
75
+ "aria-label": ariaLabelText,
76
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
77
+ className: cn(mobileTabItemVariants({ active: isActive, disabled: item.disabled }), item.className),
78
+ onClick: () => onItemClick(item.id, item),
79
+ children: [
80
+ /* @__PURE__ */ jsx("span", { children: item.label }),
81
+ tabIndicator
82
+ ]
83
+ },
84
+ item.id
85
+ );
86
+ }
87
+ return /* @__PURE__ */ jsxs(
88
+ "button",
89
+ {
90
+ ref: isActive ? activeTabRef : void 0,
91
+ type: "button",
92
+ role: "tab",
93
+ "aria-selected": isActive,
94
+ "aria-expanded": isActive,
95
+ "aria-label": ariaLabelText,
96
+ disabled: item.disabled,
97
+ onClick: () => onItemClick(item.id, item),
98
+ className: cn(mobileTabItemVariants({ active: isActive, disabled: item.disabled }), item.className),
99
+ children: [
100
+ /* @__PURE__ */ jsx("span", { children: item.label }),
101
+ tabIndicator
102
+ ]
103
+ },
104
+ item.id
105
+ );
106
+ })
107
+ }
108
+ );
109
+ };
110
+
111
+ export { NavMobileTabSlider };
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ import type { NavSearchConfig } from '../NavHeader.types';
3
+ export interface NavSearchProps {
4
+ config?: NavSearchConfig | boolean;
5
+ isOpen: boolean;
6
+ onOpenChange: (open: boolean) => void;
7
+ value: string;
8
+ onChange: (value: string) => void;
9
+ onSubmit: (query: string) => void;
10
+ shouldAnimate?: boolean;
11
+ motionClass?: string;
12
+ linkComponent?: React.ComponentType<any>;
13
+ }
14
+ export declare const NavSearch: React.FC<NavSearchProps>;
15
+ //# sourceMappingURL=NavSearch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavSearch.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/NavSearch.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,oBAAoB,CAAC;AAG5E,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;CAC1C;AAaD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAmO9C,CAAC"}