@timmbr/ui 1.2.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 (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 +532 -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 +96 -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 +4 -4
@@ -0,0 +1,169 @@
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 { navActionVariants, actionOverflowItemVariants, actionOverflowPopoverVariants } from '../NavHeader.styles.js';
8
+ import { resolveNavDestination, isCrossZoneNavigation } from '../NavHeader.helpers.js';
9
+ import { useTimmbrConfig } from '../../../providers/TimmbrConfigProvider.js';
10
+
11
+ const NavActionOverflow = ({
12
+ items,
13
+ label = "MORE",
14
+ icon,
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
+ React.useEffect(() => {
23
+ if (!isOpen) return;
24
+ const handleClickOutside = (e) => {
25
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
26
+ setIsOpen(false);
27
+ }
28
+ };
29
+ const handleKeyDown = (e) => {
30
+ if (e.key === "Escape") {
31
+ setIsOpen(false);
32
+ }
33
+ };
34
+ document.addEventListener("mousedown", handleClickOutside);
35
+ document.addEventListener("keydown", handleKeyDown);
36
+ return () => {
37
+ document.removeEventListener("mousedown", handleClickOutside);
38
+ document.removeEventListener("keydown", handleKeyDown);
39
+ };
40
+ }, [isOpen]);
41
+ if (!items || items.length === 0) {
42
+ return null;
43
+ }
44
+ const totalBadges = items.reduce((acc, curr) => {
45
+ if (typeof curr.badge === "number") return acc + curr.badge;
46
+ if (typeof curr.badge === "string") {
47
+ const parsed = parseInt(curr.badge, 10);
48
+ return acc + (isNaN(parsed) ? 1 : parsed);
49
+ }
50
+ if (curr.badge) return acc + 1;
51
+ return acc;
52
+ }, 0);
53
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-flex items-center", "data-slot": "nav-action-overflow", children: [
54
+ /* @__PURE__ */ jsxs(
55
+ "button",
56
+ {
57
+ type: "button",
58
+ "aria-label": "More navigation actions",
59
+ "aria-expanded": isOpen,
60
+ "aria-haspopup": "menu",
61
+ onClick: () => setIsOpen((prev) => !prev),
62
+ className: cn(
63
+ navActionVariants({ active: isOpen }),
64
+ !shouldAnimate && "transition-none",
65
+ motionClass
66
+ ),
67
+ "data-slot": "nav-action-overflow-trigger",
68
+ children: [
69
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
70
+ icon ?? /* @__PURE__ */ jsx(MoreHorizontal, { size: 22, className: "shrink-0 stroke-[1.8]" }),
71
+ totalBadges > 0 && /* @__PURE__ */ jsx(
72
+ "span",
73
+ {
74
+ className: "absolute -top-1 -right-2 min-w-[16px] h-4 px-1 rounded-full bg-[#C0643A] text-white text-[10px] font-bold flex items-center justify-center",
75
+ "aria-label": `${totalBadges} items in overflow menu`,
76
+ children: totalBadges
77
+ }
78
+ )
79
+ ] }),
80
+ /* @__PURE__ */ jsx("span", { className: "hidden lg:inline font-sans font-bold text-[10px] tracking-[0.0926em] uppercase whitespace-nowrap", children: label })
81
+ ]
82
+ }
83
+ ),
84
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx(
85
+ motion.div,
86
+ {
87
+ initial: shouldAnimate ? { opacity: 0, y: 6, scale: 0.97 } : false,
88
+ animate: shouldAnimate ? { opacity: 1, y: 0, scale: 1 } : void 0,
89
+ exit: shouldAnimate ? { opacity: 0, y: 6, scale: 0.97 } : void 0,
90
+ transition: { duration: 0.18, ease: [0.25, 1, 0.5, 1] },
91
+ className: cn(
92
+ actionOverflowPopoverVariants(),
93
+ !shouldAnimate && "transition-none",
94
+ motionClass
95
+ ),
96
+ role: "menu",
97
+ "aria-orientation": "vertical",
98
+ "data-slot": "nav-action-overflow-popover",
99
+ children: /* @__PURE__ */ jsx("div", { className: "py-1 flex flex-col gap-0.5", children: items.map((item) => {
100
+ if (item.render) {
101
+ return /* @__PURE__ */ jsx("div", { role: "menuitem", children: item.render({ item, isOverflow: true }) }, item.id);
102
+ }
103
+ const resolvedHref = resolveNavDestination(item.href, item.zone, config.zones?.zones);
104
+ const requiresCrossZone = isCrossZoneNavigation(item.zone, config.zones?.currentZone, item.crossZone);
105
+ const itemRowContent = /* @__PURE__ */ jsxs(Fragment, { children: [
106
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 truncate", children: [
107
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-grey-500 group-hover:text-[#C0643A] transition-colors", children: item.icon }),
108
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: item.label })
109
+ ] }),
110
+ typeof item.badge !== "undefined" && item.badge !== null && /* @__PURE__ */ jsx("span", { className: "shrink-0 px-1.5 py-0.5 rounded-full bg-[#C0643A] text-white text-[10px] font-bold", children: item.badge })
111
+ ] });
112
+ const itemClass = cn(
113
+ actionOverflowItemVariants({ disabled: item.disabled }),
114
+ "group",
115
+ item.className
116
+ );
117
+ const handleClick = (e) => {
118
+ item.onClick?.(e);
119
+ setIsOpen(false);
120
+ };
121
+ if (resolvedHref && !item.disabled) {
122
+ if (CustomLink && !requiresCrossZone) {
123
+ return /* @__PURE__ */ jsx(
124
+ CustomLink,
125
+ {
126
+ href: resolvedHref,
127
+ "aria-label": item.ariaLabel,
128
+ role: "menuitem",
129
+ className: itemClass,
130
+ onClick: handleClick,
131
+ children: itemRowContent
132
+ },
133
+ item.id
134
+ );
135
+ }
136
+ return /* @__PURE__ */ jsx(
137
+ "a",
138
+ {
139
+ href: resolvedHref,
140
+ "aria-label": item.ariaLabel,
141
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
142
+ role: "menuitem",
143
+ className: itemClass,
144
+ onClick: handleClick,
145
+ children: itemRowContent
146
+ },
147
+ item.id
148
+ );
149
+ }
150
+ return /* @__PURE__ */ jsx(
151
+ "button",
152
+ {
153
+ type: "button",
154
+ role: "menuitem",
155
+ disabled: item.disabled,
156
+ "aria-label": item.ariaLabel,
157
+ onClick: handleClick,
158
+ className: itemClass,
159
+ children: itemRowContent
160
+ },
161
+ item.id
162
+ );
163
+ }) })
164
+ }
165
+ ) })
166
+ ] });
167
+ };
168
+
169
+ export { NavActionOverflow };
@@ -0,0 +1,22 @@
1
+ import * as React from 'react';
2
+ import type { NavActionItem } from '../NavHeader.types';
3
+ export interface NavActionsProps {
4
+ actions?: NavActionItem[];
5
+ profileAction?: Partial<NavActionItem>;
6
+ wishlistAction?: Partial<NavActionItem> & {
7
+ count?: number;
8
+ };
9
+ cartAction?: Partial<NavActionItem> & {
10
+ count?: number;
11
+ };
12
+ maxVisibleActions?: number | 'auto';
13
+ overflowActionLabel?: React.ReactNode;
14
+ overflowActionIcon?: React.ReactNode;
15
+ rightContent?: React.ReactNode;
16
+ shouldAnimate?: boolean;
17
+ motionClass?: string;
18
+ linkComponent?: React.ComponentType<any>;
19
+ }
20
+ export declare const NavActions: React.FC<NavActionsProps>;
21
+ export default NavActions;
22
+ //# sourceMappingURL=NavActions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavActions.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/NavActions.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACvC,cAAc,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,UAAU,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACtC,kBAAkB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACrC,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,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,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAsRhD,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,228 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { User, Heart, ShoppingCart } from '@timmbr/icons';
5
+ import { cn } from '@timmbr/utils';
6
+ import { navActionVariants } from '../NavHeader.styles.js';
7
+ import { resolveNavDestination, isCrossZoneNavigation } from '../NavHeader.helpers.js';
8
+ import { NavActionOverflow } from './NavActionOverflow.js';
9
+ import { useTimmbrConfig } from '../../../providers/TimmbrConfigProvider.js';
10
+
11
+ const NavActions = ({
12
+ actions,
13
+ profileAction,
14
+ wishlistAction,
15
+ cartAction,
16
+ maxVisibleActions = "auto",
17
+ overflowActionLabel,
18
+ overflowActionIcon,
19
+ rightContent,
20
+ shouldAnimate = true,
21
+ motionClass,
22
+ linkComponent: CustomLink
23
+ }) => {
24
+ const config = useTimmbrConfig();
25
+ const containerRef = React.useRef(null);
26
+ const [measuredMaxVisible, setMeasuredMaxVisible] = React.useState(null);
27
+ const items = React.useMemo(() => {
28
+ if (actions && actions.length > 0 && !profileAction && !wishlistAction && !cartAction) {
29
+ return actions;
30
+ }
31
+ const defaultItems = [];
32
+ const customItems = actions ?? [];
33
+ defaultItems.push({
34
+ id: "profile",
35
+ label: profileAction?.label ?? "PROFILE",
36
+ icon: profileAction?.icon ?? /* @__PURE__ */ jsx(User, { size: 22, className: "stroke-[1.8]" }),
37
+ href: profileAction?.href ?? "/account",
38
+ zone: profileAction?.zone,
39
+ crossZone: profileAction?.crossZone,
40
+ onClick: profileAction?.onClick,
41
+ ariaLabel: profileAction?.ariaLabel ?? "User Account"
42
+ });
43
+ const wishCount = wishlistAction?.count;
44
+ defaultItems.push({
45
+ id: "wishlist",
46
+ label: wishlistAction?.label ?? "WISH LIST",
47
+ icon: wishlistAction?.icon ?? /* @__PURE__ */ jsx(Heart, { size: 22, className: "stroke-[1.8]" }),
48
+ href: wishlistAction?.href ?? "/wishlist",
49
+ zone: wishlistAction?.zone,
50
+ crossZone: wishlistAction?.crossZone,
51
+ badge: wishlistAction?.badge ?? (wishCount && wishCount > 0 ? wishCount : void 0),
52
+ onClick: wishlistAction?.onClick,
53
+ ariaLabel: wishlistAction?.ariaLabel ?? (wishCount ? `Wishlist with ${wishCount} items` : "Wishlist")
54
+ });
55
+ const cartCount = cartAction?.count;
56
+ defaultItems.push({
57
+ id: "cart",
58
+ label: cartAction?.label ?? "CART",
59
+ icon: cartAction?.icon ?? /* @__PURE__ */ jsx(ShoppingCart, { size: 22, className: "stroke-[1.8]" }),
60
+ href: cartAction?.href ?? "/cart",
61
+ zone: cartAction?.zone,
62
+ crossZone: cartAction?.crossZone,
63
+ badge: cartAction?.badge ?? (cartCount && cartCount > 0 ? cartCount : void 0),
64
+ onClick: cartAction?.onClick,
65
+ ariaLabel: cartAction?.ariaLabel ?? (cartCount ? `Shopping cart with ${cartCount} items` : "Shopping cart"),
66
+ priority: "high"
67
+ });
68
+ return [...customItems, ...defaultItems];
69
+ }, [actions, profileAction, wishlistAction, cartAction]);
70
+ React.useEffect(() => {
71
+ if (maxVisibleActions !== "auto") {
72
+ setMeasuredMaxVisible(null);
73
+ return;
74
+ }
75
+ const container = containerRef.current;
76
+ if (!container || typeof ResizeObserver === "undefined") return;
77
+ const calculateFit = () => {
78
+ const parent = container.parentElement;
79
+ if (!parent) return;
80
+ const parentWidth = parent.clientWidth;
81
+ if (parentWidth <= 0) return;
82
+ let siblingsWidth = 0;
83
+ Array.from(parent.children).forEach((child) => {
84
+ if (child !== container) {
85
+ siblingsWidth += child.offsetWidth || 0;
86
+ }
87
+ });
88
+ const availableWidth = Math.max(0, parentWidth - siblingsWidth);
89
+ const itemWidth = 68;
90
+ const moreWidth = 68;
91
+ if (availableWidth <= moreWidth) {
92
+ setMeasuredMaxVisible(1);
93
+ return;
94
+ }
95
+ const maxSlots = Math.floor(availableWidth / itemWidth);
96
+ setMeasuredMaxVisible(Math.max(1, maxSlots));
97
+ };
98
+ calculateFit();
99
+ const observer = new ResizeObserver(() => {
100
+ calculateFit();
101
+ });
102
+ if (container.parentElement) {
103
+ observer.observe(container.parentElement);
104
+ }
105
+ return () => {
106
+ observer.disconnect();
107
+ };
108
+ }, [maxVisibleActions, items.length]);
109
+ const { visibleActions, overflowActions } = React.useMemo(() => {
110
+ const limit = typeof maxVisibleActions === "number" ? maxVisibleActions : measuredMaxVisible ?? items.length;
111
+ if (items.length <= limit) {
112
+ return { visibleActions: items, overflowActions: [] };
113
+ }
114
+ const countToKeep = typeof maxVisibleActions === "number" ? maxVisibleActions : Math.max(1, limit - 1);
115
+ const hasPriorities = items.some((i) => i.priority);
116
+ if (!hasPriorities) {
117
+ return {
118
+ visibleActions: items.slice(0, countToKeep),
119
+ overflowActions: items.slice(countToKeep)
120
+ };
121
+ }
122
+ const priorityScore = (item) => {
123
+ if (item.priority === "high") return 3;
124
+ if (item.priority === "low") return 1;
125
+ return 2;
126
+ };
127
+ const indexed = items.map((item, index) => ({ item, index, score: priorityScore(item) }));
128
+ const sorted = [...indexed].sort((a, b) => b.score - a.score || a.index - b.index);
129
+ const visibleIndices = new Set(sorted.slice(0, countToKeep).map((x) => x.index));
130
+ const visible = [];
131
+ const overflow = [];
132
+ items.forEach((item, index) => {
133
+ if (visibleIndices.has(index)) {
134
+ visible.push(item);
135
+ } else {
136
+ overflow.push(item);
137
+ }
138
+ });
139
+ return { visibleActions: visible, overflowActions: overflow };
140
+ }, [items, maxVisibleActions, measuredMaxVisible]);
141
+ return /* @__PURE__ */ jsxs(
142
+ "div",
143
+ {
144
+ ref: containerRef,
145
+ className: "flex items-center gap-1.5 sm:gap-2.5 lg:gap-[18px]",
146
+ "data-slot": "nav-actions-container",
147
+ children: [
148
+ visibleActions.map((item) => {
149
+ if (item.render) {
150
+ return /* @__PURE__ */ jsx("div", { className: "relative inline-flex items-center", children: item.render({ item, isOverflow: false }) }, item.id);
151
+ }
152
+ const resolvedHref = resolveNavDestination(item.href, item.zone, config.zones?.zones);
153
+ const requiresCrossZone = isCrossZoneNavigation(item.zone, config.zones?.currentZone, item.crossZone);
154
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
155
+ /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
156
+ item.icon,
157
+ typeof item.badge !== "undefined" && item.badge !== null && /* @__PURE__ */ jsx("span", { className: "absolute -top-1 -right-2 min-w-[16px] h-4 px-1 rounded-full bg-[#C0643A] text-white text-[10px] font-bold flex items-center justify-center", children: item.badge })
158
+ ] }),
159
+ /* @__PURE__ */ jsx("span", { className: "hidden lg:inline font-sans font-bold text-[10px] tracking-[0.0926em] uppercase whitespace-nowrap", children: item.label })
160
+ ] });
161
+ const actionClass = cn(
162
+ navActionVariants(),
163
+ item.disabled && "opacity-40 cursor-not-allowed pointer-events-none",
164
+ !shouldAnimate && "transition-none",
165
+ motionClass,
166
+ item.className
167
+ );
168
+ if (resolvedHref && !item.disabled) {
169
+ if (CustomLink && !requiresCrossZone) {
170
+ return /* @__PURE__ */ jsx(
171
+ CustomLink,
172
+ {
173
+ href: resolvedHref,
174
+ "aria-label": item.ariaLabel,
175
+ className: actionClass,
176
+ onClick: item.onClick,
177
+ "data-slot": "nav-action-link",
178
+ children: content
179
+ },
180
+ item.id
181
+ );
182
+ }
183
+ return /* @__PURE__ */ jsx(
184
+ "a",
185
+ {
186
+ href: resolvedHref,
187
+ "aria-label": item.ariaLabel,
188
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
189
+ className: actionClass,
190
+ onClick: item.onClick,
191
+ "data-slot": "nav-action-link",
192
+ children: content
193
+ },
194
+ item.id
195
+ );
196
+ }
197
+ return /* @__PURE__ */ jsx(
198
+ "button",
199
+ {
200
+ type: "button",
201
+ disabled: item.disabled,
202
+ "aria-label": item.ariaLabel,
203
+ onClick: item.onClick,
204
+ className: actionClass,
205
+ "data-slot": "nav-action-button",
206
+ children: content
207
+ },
208
+ item.id
209
+ );
210
+ }),
211
+ overflowActions.length > 0 && /* @__PURE__ */ jsx(
212
+ NavActionOverflow,
213
+ {
214
+ items: overflowActions,
215
+ label: overflowActionLabel,
216
+ icon: overflowActionIcon,
217
+ shouldAnimate,
218
+ motionClass,
219
+ linkComponent: CustomLink
220
+ }
221
+ ),
222
+ rightContent && /* @__PURE__ */ jsx("div", { className: "ml-2 flex items-center", children: rightContent })
223
+ ]
224
+ }
225
+ );
226
+ };
227
+
228
+ export { NavActions };
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import type { NavHeaderBranding } from '../NavHeader.types';
3
+ export interface NavBrandProps {
4
+ branding: NavHeaderBranding;
5
+ isSearchOpen?: boolean;
6
+ shouldAnimate?: boolean;
7
+ motionClass?: string;
8
+ linkComponent?: React.ComponentType<any>;
9
+ }
10
+ export declare const NavBrand: React.FC<NavBrandProps>;
11
+ export default NavBrand;
12
+ //# sourceMappingURL=NavBrand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavBrand.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/NavBrand.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG5D,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,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,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAmG5C,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,96 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { motion } from '@timmbr/motion';
4
+ import { cn } from '@timmbr/utils';
5
+ import { resolveNavDestination, isCrossZoneNavigation } from '../NavHeader.helpers.js';
6
+ import { useTimmbrConfig } from '../../../providers/TimmbrConfigProvider.js';
7
+
8
+ const NavBrand = ({
9
+ branding,
10
+ isSearchOpen = false,
11
+ shouldAnimate = true,
12
+ motionClass,
13
+ linkComponent: CustomLink
14
+ }) => {
15
+ const config = useTimmbrConfig();
16
+ const { logo, miniLogo, href = "/", zone, alt = "Brand Home", className, onClick } = branding;
17
+ const resolvedHref = resolveNavDestination(href, zone, config.zones?.zones) ?? "/";
18
+ const requiresCrossZone = isCrossZoneNavigation(zone, config.zones?.currentZone);
19
+ const showMini = Boolean(isSearchOpen && miniLogo);
20
+ const logoNode = /* @__PURE__ */ jsxs(
21
+ "div",
22
+ {
23
+ className: "relative flex items-center justify-center shrink-0 min-w-[32px] h-8",
24
+ "data-slot": "nav-brand-logo",
25
+ children: [
26
+ /* @__PURE__ */ jsx(
27
+ motion.div,
28
+ {
29
+ initial: false,
30
+ animate: shouldAnimate ? {
31
+ opacity: showMini ? 0 : 1,
32
+ scale: showMini ? 0.92 : 1
33
+ } : void 0,
34
+ transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
35
+ className: cn(
36
+ "flex items-center justify-center shrink-0 transition-opacity",
37
+ showMini ? "pointer-events-none" : "pointer-events-auto",
38
+ !shouldAnimate && (showMini ? "opacity-0" : "opacity-100"),
39
+ motionClass
40
+ ),
41
+ children: logo
42
+ }
43
+ ),
44
+ miniLogo && /* @__PURE__ */ jsx(
45
+ motion.div,
46
+ {
47
+ initial: false,
48
+ animate: shouldAnimate ? {
49
+ opacity: showMini ? 1 : 0,
50
+ scale: showMini ? 1 : 0.92
51
+ } : void 0,
52
+ transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
53
+ className: cn(
54
+ "absolute inset-0 flex items-center justify-center shrink-0",
55
+ showMini ? "pointer-events-auto" : "pointer-events-none",
56
+ !shouldAnimate && (showMini ? "opacity-100" : "opacity-0"),
57
+ motionClass
58
+ ),
59
+ children: miniLogo
60
+ }
61
+ )
62
+ ]
63
+ }
64
+ );
65
+ const wrapperClass = cn(
66
+ "inline-flex items-center justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 rounded-md transition-transform",
67
+ className
68
+ );
69
+ if (CustomLink && !requiresCrossZone) {
70
+ return /* @__PURE__ */ jsx(
71
+ CustomLink,
72
+ {
73
+ href: resolvedHref,
74
+ "aria-label": alt,
75
+ className: wrapperClass,
76
+ onClick,
77
+ "data-slot": "nav-brand-link",
78
+ children: logoNode
79
+ }
80
+ );
81
+ }
82
+ return /* @__PURE__ */ jsx(
83
+ "a",
84
+ {
85
+ href: resolvedHref,
86
+ "aria-label": alt,
87
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
88
+ "data-slot": "nav-brand-link",
89
+ className: wrapperClass,
90
+ onClick,
91
+ children: logoNode
92
+ }
93
+ );
94
+ };
95
+
96
+ export { NavBrand };
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ import type { NavHeaderItem } from '../NavHeader.types';
3
+ export interface NavItemsProps {
4
+ items: NavHeaderItem[];
5
+ activeItemId: string | null;
6
+ onItemMouseEnter: (id: string) => void;
7
+ onItemMouseLeave: () => void;
8
+ onItemClick: (id: string, item: NavHeaderItem) => void;
9
+ maxVisibleItems?: number;
10
+ shouldAnimate?: boolean;
11
+ motionClass?: string;
12
+ linkComponent?: React.ComponentType<any>;
13
+ }
14
+ export declare const NavItems: React.FC<NavItemsProps>;
15
+ //# sourceMappingURL=NavItems.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavItems.d.ts","sourceRoot":"","sources":["../../../../src/components/NavHeader/subcomponents/NavItems.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,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,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAiJ5C,CAAC"}