@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.
- package/dist/components/NavHeader/NavHeader.context.d.ts +20 -0
- package/dist/components/NavHeader/NavHeader.context.d.ts.map +1 -0
- package/dist/components/NavHeader/NavHeader.context.js +13 -0
- package/dist/components/NavHeader/NavHeader.d.ts +8 -0
- package/dist/components/NavHeader/NavHeader.d.ts.map +1 -0
- package/dist/components/NavHeader/NavHeader.helpers.d.ts +13 -0
- package/dist/components/NavHeader/NavHeader.helpers.d.ts.map +1 -0
- package/dist/components/NavHeader/NavHeader.helpers.js +14 -0
- package/dist/components/NavHeader/NavHeader.js +547 -0
- package/dist/components/NavHeader/NavHeader.styles.d.ts +67 -0
- package/dist/components/NavHeader/NavHeader.styles.d.ts.map +1 -0
- package/dist/components/NavHeader/NavHeader.styles.js +123 -0
- package/dist/components/NavHeader/NavHeader.types.d.ts +321 -0
- package/dist/components/NavHeader/NavHeader.types.d.ts.map +1 -0
- package/dist/components/NavHeader/index.d.ts +11 -0
- package/dist/components/NavHeader/index.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavActionOverflow.d.ts +13 -0
- package/dist/components/NavHeader/subcomponents/NavActionOverflow.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavActionOverflow.js +169 -0
- package/dist/components/NavHeader/subcomponents/NavActions.d.ts +22 -0
- package/dist/components/NavHeader/subcomponents/NavActions.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavActions.js +228 -0
- package/dist/components/NavHeader/subcomponents/NavBrand.d.ts +12 -0
- package/dist/components/NavHeader/subcomponents/NavBrand.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavBrand.js +94 -0
- package/dist/components/NavHeader/subcomponents/NavItems.d.ts +15 -0
- package/dist/components/NavHeader/subcomponents/NavItems.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavItems.js +141 -0
- package/dist/components/NavHeader/subcomponents/NavItemsOverflow.d.ts +13 -0
- package/dist/components/NavHeader/subcomponents/NavItemsOverflow.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavItemsOverflow.js +165 -0
- package/dist/components/NavHeader/subcomponents/NavMegaMenu.d.ts +12 -0
- package/dist/components/NavHeader/subcomponents/NavMegaMenu.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavMegaMenu.js +73 -0
- package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.d.ts +16 -0
- package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.js +149 -0
- package/dist/components/NavHeader/subcomponents/NavMobileTabContent.d.ts +11 -0
- package/dist/components/NavHeader/subcomponents/NavMobileTabContent.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavMobileTabContent.js +40 -0
- package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.d.ts +13 -0
- package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavMobileTabSlider.js +111 -0
- package/dist/components/NavHeader/subcomponents/NavSearch.d.ts +15 -0
- package/dist/components/NavHeader/subcomponents/NavSearch.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/NavSearch.js +222 -0
- package/dist/components/NavHeader/subcomponents/OfferBanner.d.ts +11 -0
- package/dist/components/NavHeader/subcomponents/OfferBanner.d.ts.map +1 -0
- package/dist/components/NavHeader/subcomponents/OfferBanner.js +92 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- 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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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.
|
|
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/
|
|
48
|
-
"@timmbr/
|
|
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",
|