@timmbr/ui 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/components/NavHeader/NavHeader.context.d.ts +20 -0
  2. package/dist/components/NavHeader/NavHeader.context.d.ts.map +1 -0
  3. package/dist/components/NavHeader/NavHeader.context.js +13 -0
  4. package/dist/components/NavHeader/NavHeader.d.ts +8 -0
  5. package/dist/components/NavHeader/NavHeader.d.ts.map +1 -0
  6. package/dist/components/NavHeader/NavHeader.helpers.d.ts +13 -0
  7. package/dist/components/NavHeader/NavHeader.helpers.d.ts.map +1 -0
  8. package/dist/components/NavHeader/NavHeader.helpers.js +14 -0
  9. package/dist/components/NavHeader/NavHeader.js +547 -0
  10. package/dist/components/NavHeader/NavHeader.styles.d.ts +67 -0
  11. package/dist/components/NavHeader/NavHeader.styles.d.ts.map +1 -0
  12. package/dist/components/NavHeader/NavHeader.styles.js +123 -0
  13. package/dist/components/NavHeader/NavHeader.types.d.ts +321 -0
  14. package/dist/components/NavHeader/NavHeader.types.d.ts.map +1 -0
  15. package/dist/components/NavHeader/index.d.ts +11 -0
  16. package/dist/components/NavHeader/index.d.ts.map +1 -0
  17. package/dist/components/NavHeader/subcomponents/NavActionOverflow.d.ts +13 -0
  18. package/dist/components/NavHeader/subcomponents/NavActionOverflow.d.ts.map +1 -0
  19. package/dist/components/NavHeader/subcomponents/NavActionOverflow.js +169 -0
  20. package/dist/components/NavHeader/subcomponents/NavActions.d.ts +22 -0
  21. package/dist/components/NavHeader/subcomponents/NavActions.d.ts.map +1 -0
  22. package/dist/components/NavHeader/subcomponents/NavActions.js +228 -0
  23. package/dist/components/NavHeader/subcomponents/NavBrand.d.ts +12 -0
  24. package/dist/components/NavHeader/subcomponents/NavBrand.d.ts.map +1 -0
  25. package/dist/components/NavHeader/subcomponents/NavBrand.js +94 -0
  26. package/dist/components/NavHeader/subcomponents/NavItems.d.ts +15 -0
  27. package/dist/components/NavHeader/subcomponents/NavItems.d.ts.map +1 -0
  28. package/dist/components/NavHeader/subcomponents/NavItems.js +141 -0
  29. package/dist/components/NavHeader/subcomponents/NavItemsOverflow.d.ts +13 -0
  30. package/dist/components/NavHeader/subcomponents/NavItemsOverflow.d.ts.map +1 -0
  31. package/dist/components/NavHeader/subcomponents/NavItemsOverflow.js +165 -0
  32. package/dist/components/NavHeader/subcomponents/NavMegaMenu.d.ts +12 -0
  33. package/dist/components/NavHeader/subcomponents/NavMegaMenu.d.ts.map +1 -0
  34. package/dist/components/NavHeader/subcomponents/NavMegaMenu.js +73 -0
  35. package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.d.ts +16 -0
  36. package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.d.ts.map +1 -0
  37. package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.js +149 -0
  38. package/dist/components/NavHeader/subcomponents/NavMobileTabContent.d.ts +11 -0
  39. package/dist/components/NavHeader/subcomponents/NavMobileTabContent.d.ts.map +1 -0
  40. package/dist/components/NavHeader/subcomponents/NavMobileTabContent.js +40 -0
  41. package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.d.ts +13 -0
  42. package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.d.ts.map +1 -0
  43. package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.js +111 -0
  44. package/dist/components/NavHeader/subcomponents/NavSearch.d.ts +15 -0
  45. package/dist/components/NavHeader/subcomponents/NavSearch.d.ts.map +1 -0
  46. package/dist/components/NavHeader/subcomponents/NavSearch.js +222 -0
  47. package/dist/components/NavHeader/subcomponents/OfferBanner.d.ts +11 -0
  48. package/dist/components/NavHeader/subcomponents/OfferBanner.d.ts.map +1 -0
  49. package/dist/components/NavHeader/subcomponents/OfferBanner.js +92 -0
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +9 -0
  53. package/package.json +3 -3
@@ -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"}
@@ -0,0 +1,222 @@
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 { Search, X, TrendingUp } from '@timmbr/icons';
6
+ import { cn } from '@timmbr/utils';
7
+ import { navActionVariants, searchPopoverCardVariants } from '../NavHeader.styles.js';
8
+ import { resolveNavDestination, isCrossZoneNavigation } from '../NavHeader.helpers.js';
9
+ import { useTimmbrConfig } from '../../../providers/TimmbrConfigProvider.js';
10
+
11
+ const defaultPopularSearches = [
12
+ { id: "1", label: "Centre Tables" },
13
+ { id: "2", label: "TV Units" },
14
+ { id: "3", label: "bed" },
15
+ { id: "4", label: "bedsheet" },
16
+ { id: "5", label: "mirror" },
17
+ { id: "6", label: "office chair" },
18
+ { id: "7", label: "shoe rack" },
19
+ { id: "8", label: "sofa cum bed" }
20
+ ];
21
+ const NavSearch = ({
22
+ config,
23
+ isOpen,
24
+ onOpenChange,
25
+ value,
26
+ onChange,
27
+ onSubmit,
28
+ shouldAnimate = true,
29
+ motionClass,
30
+ linkComponent: CustomLink
31
+ }) => {
32
+ const globalConfig = useTimmbrConfig();
33
+ const searchConfig = typeof config === "object" ? config : {};
34
+ const {
35
+ placeholder = "Search for furniture, decor, and more...",
36
+ popoverContent,
37
+ popularSearches = defaultPopularSearches,
38
+ popularSearchesTitle = "Popular Searches",
39
+ onPopularSearchClick,
40
+ ariaLabel = "Search store products"
41
+ } = searchConfig;
42
+ const [showPopover, setShowPopover] = React.useState(false);
43
+ const containerRef = React.useRef(null);
44
+ const inputRef = React.useRef(null);
45
+ React.useEffect(() => {
46
+ if (isOpen) {
47
+ inputRef.current?.focus();
48
+ setShowPopover(true);
49
+ } else {
50
+ setShowPopover(false);
51
+ }
52
+ }, [isOpen]);
53
+ React.useEffect(() => {
54
+ if (!isOpen) return;
55
+ const handleClickOutside = (e) => {
56
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
57
+ setShowPopover(false);
58
+ if (!value.trim()) {
59
+ onOpenChange(false);
60
+ }
61
+ }
62
+ };
63
+ document.addEventListener("mousedown", handleClickOutside);
64
+ return () => document.removeEventListener("mousedown", handleClickOutside);
65
+ }, [isOpen, value, onOpenChange]);
66
+ const handleToggle = () => {
67
+ const nextState = !isOpen;
68
+ onOpenChange(nextState);
69
+ if (nextState) {
70
+ setShowPopover(true);
71
+ }
72
+ };
73
+ const handleKeyDown = (e) => {
74
+ if (e.key === "Enter") {
75
+ e.preventDefault();
76
+ onSubmit(value);
77
+ setShowPopover(false);
78
+ } else if (e.key === "Escape") {
79
+ e.preventDefault();
80
+ setShowPopover(false);
81
+ onOpenChange(false);
82
+ }
83
+ };
84
+ const handleTagClick = (tag) => {
85
+ onChange(tag.label);
86
+ onPopularSearchClick?.(tag);
87
+ tag.onClick?.(tag);
88
+ onSubmit(tag.label);
89
+ setShowPopover(false);
90
+ };
91
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative flex items-center", "data-slot": "nav-search-container", children: [
92
+ !isOpen && /* @__PURE__ */ jsxs(
93
+ "button",
94
+ {
95
+ type: "button",
96
+ onClick: handleToggle,
97
+ "aria-label": "Open search bar",
98
+ "aria-expanded": isOpen,
99
+ className: navActionVariants({ active: isOpen }),
100
+ "data-slot": "nav-search-trigger",
101
+ children: [
102
+ /* @__PURE__ */ jsx(Search, { size: 22, className: "shrink-0 stroke-[1.8]" }),
103
+ /* @__PURE__ */ jsx("span", { className: "font-sans font-bold text-[10px] tracking-[0.0926em] uppercase", children: "SEARCH" })
104
+ ]
105
+ }
106
+ ),
107
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsxs(
108
+ motion.div,
109
+ {
110
+ initial: shouldAnimate ? { width: 0, opacity: 0 } : false,
111
+ animate: shouldAnimate ? { width: 350, opacity: 1 } : void 0,
112
+ exit: shouldAnimate ? { width: 0, opacity: 0 } : void 0,
113
+ transition: { duration: 0.28, ease: [0.25, 1, 0.5, 1] },
114
+ className: cn("relative flex flex-col", !shouldAnimate && "transition-none", motionClass),
115
+ children: [
116
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center w-full h-10 bg-white border border-grey-300 rounded-full shadow-sm focus-within:ring-2 focus-within:ring-[#C0643A]/30 focus-within:border-[#C0643A] overflow-hidden", children: [
117
+ /* @__PURE__ */ jsx(
118
+ Search,
119
+ {
120
+ size: 18,
121
+ className: "absolute left-3.5 top-1/2 -translate-y-1/2 text-grey-500 pointer-events-none shrink-0"
122
+ }
123
+ ),
124
+ /* @__PURE__ */ jsx(
125
+ "input",
126
+ {
127
+ ref: inputRef,
128
+ type: "text",
129
+ role: "searchbox",
130
+ autoComplete: "off",
131
+ value,
132
+ placeholder,
133
+ "aria-label": ariaLabel,
134
+ onChange: (e) => onChange(e.target.value),
135
+ onFocus: () => setShowPopover(true),
136
+ onKeyDown: handleKeyDown,
137
+ className: "w-full h-full pl-10 pr-9 bg-transparent text-sm text-[#1A1A1A] placeholder:text-grey-400 border-none focus:outline-none [&::-webkit-search-cancel-button]:hidden [&::-webkit-search-decoration]:hidden",
138
+ "data-slot": "nav-search-input"
139
+ }
140
+ ),
141
+ /* @__PURE__ */ jsx(
142
+ "button",
143
+ {
144
+ type: "button",
145
+ onClick: () => {
146
+ if (value) {
147
+ onChange("");
148
+ } else {
149
+ onOpenChange(false);
150
+ }
151
+ },
152
+ "aria-label": "Close search",
153
+ className: "absolute right-2.5 top-1/2 -translate-y-1/2 p-1 text-grey-400 hover:text-grey-700 rounded-full hover:bg-grey-100 transition-colors shrink-0",
154
+ children: /* @__PURE__ */ jsx(X, { size: 15 })
155
+ }
156
+ )
157
+ ] }),
158
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: showPopover && /* @__PURE__ */ jsx(
159
+ motion.div,
160
+ {
161
+ initial: shouldAnimate ? { opacity: 0, y: 4 } : false,
162
+ animate: shouldAnimate ? { opacity: 1, y: 0 } : void 0,
163
+ exit: shouldAnimate ? { opacity: 0, y: 4 } : void 0,
164
+ transition: { duration: 0.18, ease: [0.25, 1, 0.5, 1] },
165
+ className: cn(searchPopoverCardVariants(), !shouldAnimate && "transition-none", motionClass),
166
+ "data-slot": "nav-search-popover",
167
+ children: popoverContent ? popoverContent : /* @__PURE__ */ jsxs("div", { children: [
168
+ popularSearchesTitle && /* @__PURE__ */ jsx("h4", { className: "font-sans font-bold text-sm tracking-wide text-[#E11D48] mb-3 uppercase flex items-center gap-1.5", children: /* @__PURE__ */ jsx("span", { children: popularSearchesTitle }) }),
169
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: popularSearches.map((tag, idx) => {
170
+ const resolvedTagHref = resolveNavDestination(tag.href, tag.zone, globalConfig.zones?.zones);
171
+ const requiresCrossZone = isCrossZoneNavigation(tag.zone, globalConfig.zones?.currentZone);
172
+ const tagContent = /* @__PURE__ */ jsxs(Fragment, { children: [
173
+ /* @__PURE__ */ jsx(TrendingUp, { size: 13, className: "text-grey-500 shrink-0" }),
174
+ /* @__PURE__ */ jsx("span", { children: tag.label })
175
+ ] });
176
+ const tagClass = "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-grey-200 bg-white hover:border-grey-400 hover:bg-grey-50 text-xs font-medium text-grey-800 transition-all cursor-pointer";
177
+ if (resolvedTagHref) {
178
+ if (CustomLink && !requiresCrossZone) {
179
+ return /* @__PURE__ */ jsx(
180
+ CustomLink,
181
+ {
182
+ href: resolvedTagHref,
183
+ className: tagClass,
184
+ onClick: () => handleTagClick(tag),
185
+ children: tagContent
186
+ },
187
+ tag.id ?? idx
188
+ );
189
+ }
190
+ return /* @__PURE__ */ jsx(
191
+ "a",
192
+ {
193
+ href: resolvedTagHref,
194
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
195
+ className: tagClass,
196
+ onClick: () => handleTagClick(tag),
197
+ children: tagContent
198
+ },
199
+ tag.id ?? idx
200
+ );
201
+ }
202
+ return /* @__PURE__ */ jsx(
203
+ "button",
204
+ {
205
+ type: "button",
206
+ onClick: () => handleTagClick(tag),
207
+ className: tagClass,
208
+ children: tagContent
209
+ },
210
+ tag.id ?? idx
211
+ );
212
+ }) })
213
+ ] })
214
+ }
215
+ ) })
216
+ ]
217
+ }
218
+ ) })
219
+ ] });
220
+ };
221
+
222
+ export { NavSearch };
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import type { OfferBannerConfig } from '../NavHeader.types';
3
+ export interface OfferBannerProps {
4
+ banner?: OfferBannerConfig | React.ReactNode | boolean;
5
+ show?: boolean;
6
+ shouldAnimate?: boolean;
7
+ motionClass?: string;
8
+ linkComponent?: React.ComponentType<any>;
9
+ }
10
+ export declare const OfferBanner: React.FC<OfferBannerProps>;
11
+ //# sourceMappingURL=OfferBanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OfferBanner.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/OfferBanner.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG5D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,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,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAmGlD,CAAC"}
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { AnimatePresence, motion, collapseVariants, getTransition } from '@timmbr/motion';
5
+ import { X } from '@timmbr/icons';
6
+ import { cn } from '@timmbr/utils';
7
+ import { offerBannerVariants } from '../NavHeader.styles.js';
8
+ import { resolveNavDestination, isCrossZoneNavigation } from '../NavHeader.helpers.js';
9
+ import { useTimmbrConfig } from '../../../providers/TimmbrConfigProvider.js';
10
+
11
+ const OfferBanner = ({
12
+ banner,
13
+ show = true,
14
+ shouldAnimate = true,
15
+ motionClass,
16
+ linkComponent: CustomLink
17
+ }) => {
18
+ const [dismissed, setDismissed] = React.useState(false);
19
+ const config = useTimmbrConfig();
20
+ if (!show || banner === false || banner === null || banner === void 0 || dismissed) {
21
+ return null;
22
+ }
23
+ if (React.isValidElement(banner) || typeof banner === "string" || typeof banner === "number") {
24
+ return /* @__PURE__ */ jsx("div", { className: offerBannerVariants(), "data-slot": "nav-offer-banner", children: banner });
25
+ }
26
+ const bannerConfig = banner;
27
+ const { content, href, zone, crossZone, dismissible = false, onDismiss, action, className } = bannerConfig;
28
+ const resolvedHref = resolveNavDestination(href, zone, config.zones?.zones);
29
+ const requiresCrossZone = isCrossZoneNavigation(zone, config.zones?.currentZone, crossZone);
30
+ const handleDismiss = (e) => {
31
+ e.stopPropagation();
32
+ setDismissed(true);
33
+ onDismiss?.();
34
+ };
35
+ const renderContent = () => {
36
+ if (resolvedHref) {
37
+ if (CustomLink && !requiresCrossZone) {
38
+ return /* @__PURE__ */ jsxs(CustomLink, { href: resolvedHref, className: "hover:underline flex items-center gap-2", children: [
39
+ /* @__PURE__ */ jsx("span", { children: content }),
40
+ action && /* @__PURE__ */ jsx("span", { className: "ml-2", children: action })
41
+ ] });
42
+ }
43
+ return /* @__PURE__ */ jsxs(
44
+ "a",
45
+ {
46
+ href: resolvedHref,
47
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
48
+ className: "hover:underline flex items-center gap-2",
49
+ children: [
50
+ /* @__PURE__ */ jsx("span", { children: content }),
51
+ action && /* @__PURE__ */ jsx("span", { className: "ml-2", children: action })
52
+ ]
53
+ }
54
+ );
55
+ }
56
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
57
+ /* @__PURE__ */ jsx("span", { children: content }),
58
+ action && /* @__PURE__ */ jsx("span", { className: "ml-2", children: action })
59
+ ] });
60
+ };
61
+ return /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: !dismissed && /* @__PURE__ */ jsxs(
62
+ motion.div,
63
+ {
64
+ initial: shouldAnimate ? "visible" : false,
65
+ exit: shouldAnimate ? "exit" : void 0,
66
+ variants: shouldAnimate ? collapseVariants : void 0,
67
+ transition: getTransition("fast"),
68
+ "data-slot": "nav-offer-banner",
69
+ className: cn(
70
+ offerBannerVariants(),
71
+ !shouldAnimate && "transition-none",
72
+ motionClass,
73
+ className
74
+ ),
75
+ children: [
76
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-full px-8 text-center", children: renderContent() }),
77
+ dismissible && /* @__PURE__ */ jsx(
78
+ "button",
79
+ {
80
+ type: "button",
81
+ onClick: handleDismiss,
82
+ "aria-label": "Dismiss offer banner",
83
+ className: "absolute right-3 top-1/2 -translate-y-1/2 p-1 text-[#F7F1E6]/70 hover:text-[#F7F1E6] hover:bg-white/10 rounded transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-white",
84
+ children: /* @__PURE__ */ jsx(X, { size: 14 })
85
+ }
86
+ )
87
+ ]
88
+ }
89
+ ) });
90
+ };
91
+
92
+ export { OfferBanner };
package/dist/index.d.ts CHANGED
@@ -24,6 +24,7 @@ export * from './components/Toast';
24
24
  export * from './components/Tooltip';
25
25
  export * from './components/Tabs';
26
26
  export * from './components/SideBarNavigation';
27
+ export * from './components/NavHeader';
27
28
  export * from './components/Button';
28
29
  export * from './components/LinkButton';
29
30
  export * from './components/Accordion';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAG/B,cAAc,aAAa,CAAC;AAG5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AAGzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gCAAgC,CAAC;AAG/C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAG/B,cAAc,aAAa,CAAC;AAG5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AAGzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ export { EmptyState } from './components/EmptyState/EmptyState.js';
22
22
  export { FilmIcon, ImageIcon, PlayIcon, UploadCloudIcon } from './components/ImageUpload/subcomponents/ImageUploadIcons.js';
23
23
  export { FormField } from './components/FormField/FormField.js';
24
24
  export { Grid } from './components/Grid/Grid.js';
25
+ export { Header, NavHeader } from './components/NavHeader/NavHeader.js';
25
26
  export { Heading } from './components/Heading/Heading.js';
26
27
  export { Icon } from './components/Icon/Icon.js';
27
28
  export { ImageUpload } from './components/ImageUpload/ImageUpload.js';
@@ -34,6 +35,12 @@ export { Input } from './components/Input/Input.js';
34
35
  export { Label } from './components/Label/Label.js';
35
36
  export { LinkButton } from './components/LinkButton/LinkButton.js';
36
37
  export { List, ListItem, ListItemAction, ListItemIcon, ListItemText } from './components/List/List.js';
38
+ export { NavActions } from './components/NavHeader/subcomponents/NavActions.js';
39
+ export { NavBrand } from './components/NavHeader/subcomponents/NavBrand.js';
40
+ export { NavItems } from './components/NavHeader/subcomponents/NavItems.js';
41
+ export { NavMegaMenu } from './components/NavHeader/subcomponents/NavMegaMenu.js';
42
+ export { NavSearch } from './components/NavHeader/subcomponents/NavSearch.js';
43
+ export { OfferBanner } from './components/NavHeader/subcomponents/OfferBanner.js';
37
44
  export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from './components/Pagination/Pagination.js';
38
45
  export { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger } from './components/Popover/Popover.js';
39
46
  export { Progress } from './components/Progress/Progress.js';
@@ -90,6 +97,7 @@ export { inputFieldVariants, inputWrapperVariants } from './components/Input/Inp
90
97
  export { labelVariants } from './components/Label/Label.styles.js';
91
98
  export { linkButtonVariants } from './components/LinkButton/LinkButton.styles.js';
92
99
  export { listItemActionVariants, listItemIconVariants, listItemTextVariants, listItemVariants, listVariants } from './components/List/List.styles.js';
100
+ export { megaMenuCardVariants, navActionVariants, navHeaderContainerVariants, navHeaderItemVariants, navHeaderVariants, offerBannerVariants, searchPopoverCardVariants } from './components/NavHeader/NavHeader.styles.js';
93
101
  export { navBadgeVariants, navItemVariants, sidebarContainerVariants, sidebarToggleVariants, subItemVariants } from './components/SideBarNavigation/SideBarNavigation.styles.js';
94
102
  export { paginationLinkVariants } from './components/Pagination/Pagination.styles.js';
95
103
  export { popoverContentVariants } from './components/Popover/Popover.styles.js';
@@ -109,3 +117,4 @@ export { textareaVariants } from './components/Textarea/Textarea.styles.js';
109
117
  export { toast, toastStore, useToast } from './components/Toast/toastStore.js';
110
118
  export { toastVariants } from './components/Toast/Toast.styles.js';
111
119
  export { tooltipContentVariants } from './components/Tooltip/Tooltip.styles.js';
120
+ export { useNavHeaderContext } from './components/NavHeader/NavHeader.context.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmbr/ui",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "High-performance, Next.js App Router-compatible UI design system components for Timmbr",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,8 +44,8 @@
44
44
  "@timmbr/hooks": "1.0.0-beta",
45
45
  "@timmbr/motion": "1.1.0",
46
46
  "@timmbr/utils": "1.1.0",
47
- "@timmbr/theme": "1.1.0",
48
- "@timmbr/icons": "1.2.0"
47
+ "@timmbr/icons": "1.2.0",
48
+ "@timmbr/theme": "1.1.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "next": ">=13.0.0 || >=14.0.0 || >=15.0.0 || >=16.0.0",