@timmbr/ui 1.3.0 → 1.4.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 (41) hide show
  1. package/dist/components/NavFooter/NavFooter.d.ts +9 -0
  2. package/dist/components/NavFooter/NavFooter.d.ts.map +1 -0
  3. package/dist/components/NavFooter/NavFooter.helpers.d.ts +27 -0
  4. package/dist/components/NavFooter/NavFooter.helpers.d.ts.map +1 -0
  5. package/dist/components/NavFooter/NavFooter.helpers.js +78 -0
  6. package/dist/components/NavFooter/NavFooter.js +251 -0
  7. package/dist/components/NavFooter/NavFooter.styles.d.ts +37 -0
  8. package/dist/components/NavFooter/NavFooter.styles.d.ts.map +1 -0
  9. package/dist/components/NavFooter/NavFooter.styles.js +54 -0
  10. package/dist/components/NavFooter/NavFooter.types.d.ts +414 -0
  11. package/dist/components/NavFooter/NavFooter.types.d.ts.map +1 -0
  12. package/dist/components/NavFooter/index.d.ts +1 -0
  13. package/dist/components/NavFooter/index.d.ts.map +1 -0
  14. package/dist/components/NavFooter/subcomponents/FooterBrand.d.ts +16 -0
  15. package/dist/components/NavFooter/subcomponents/FooterBrand.d.ts.map +1 -0
  16. package/dist/components/NavFooter/subcomponents/FooterBrand.js +99 -0
  17. package/dist/components/NavFooter/subcomponents/FooterLegalBar.d.ts +10 -0
  18. package/dist/components/NavFooter/subcomponents/FooterLegalBar.d.ts.map +1 -0
  19. package/dist/components/NavFooter/subcomponents/FooterLegalBar.js +60 -0
  20. package/dist/components/NavFooter/subcomponents/FooterNavSection.d.ts +15 -0
  21. package/dist/components/NavFooter/subcomponents/FooterNavSection.d.ts.map +1 -0
  22. package/dist/components/NavFooter/subcomponents/FooterNavSection.js +123 -0
  23. package/dist/components/NavFooter/subcomponents/FooterNewsletter.d.ts +9 -0
  24. package/dist/components/NavFooter/subcomponents/FooterNewsletter.d.ts.map +1 -0
  25. package/dist/components/NavFooter/subcomponents/FooterNewsletter.js +95 -0
  26. package/dist/components/NavFooter/subcomponents/FooterOffice.d.ts +9 -0
  27. package/dist/components/NavFooter/subcomponents/FooterOffice.d.ts.map +1 -0
  28. package/dist/components/NavFooter/subcomponents/FooterOffice.js +63 -0
  29. package/dist/components/NavFooter/subcomponents/FooterSocial.d.ts +12 -0
  30. package/dist/components/NavFooter/subcomponents/FooterSocial.d.ts.map +1 -0
  31. package/dist/components/NavFooter/subcomponents/FooterSocial.js +50 -0
  32. package/dist/components/NavHeader/NavHeader.d.ts.map +1 -1
  33. package/dist/components/NavHeader/NavHeader.js +22 -7
  34. package/dist/components/NavHeader/subcomponents/NavActions.js +1 -1
  35. package/dist/components/NavHeader/subcomponents/NavBrand.d.ts.map +1 -1
  36. package/dist/components/NavHeader/subcomponents/NavBrand.js +5 -7
  37. package/dist/components/NavHeader/subcomponents/NavMegaMenu.js +2 -2
  38. package/dist/components/NavHeader/subcomponents/NavMobileSearchDrawer.js +4 -4
  39. package/dist/test-mocks/next-headers.d.ts +1 -0
  40. package/dist/test-mocks/next-headers.d.ts.map +1 -0
  41. package/package.json +5 -5
@@ -0,0 +1,123 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { motion, footerChevronVariants, AnimatePresence, footerAccordionVariants } from '@timmbr/motion';
4
+ import { ChevronDown } from '@timmbr/icons';
5
+ import { cn } from '@timmbr/utils';
6
+ import { footerSectionTitleVariants, footerNavLinkVariants } from '../NavFooter.styles.js';
7
+ import { resolveNavDestination, isCrossZoneNavigation } from '../NavFooter.helpers.js';
8
+ import { useTimmbrConfig } from '../../../providers/TimmbrConfigProvider.js';
9
+
10
+ const FooterNavSection = ({
11
+ section,
12
+ isMobileAccordion = true,
13
+ isOpen = false,
14
+ onToggle,
15
+ shouldAnimate = true,
16
+ motionClass,
17
+ linkComponent: CustomLink,
18
+ className
19
+ }) => {
20
+ const config = useTimmbrConfig();
21
+ const { id, title, items = [], collapsible = true, className: sectionClass } = section;
22
+ const contentId = `footer-section-content-${id}`;
23
+ const headerId = `footer-section-header-${id}`;
24
+ const renderLink = (item) => {
25
+ if (item.render) {
26
+ return /* @__PURE__ */ jsx("li", { className: "list-none", children: item.render({ item }) }, item.id ?? String(item.label));
27
+ }
28
+ const resolvedHref = resolveNavDestination(item.href, item.zone, config.zones?.zones) ?? "#";
29
+ const requiresCrossZone = isCrossZoneNavigation(item.zone, config.zones?.currentZone, item.crossZone);
30
+ const isExternal = item.external || item.href.startsWith("http://") || item.href.startsWith("https://");
31
+ const linkContent = /* @__PURE__ */ jsxs(Fragment, { children: [
32
+ item.icon && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: item.icon }),
33
+ /* @__PURE__ */ jsx("span", { children: item.label }),
34
+ item.badge && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-1.5 py-0.2 rounded text-[10px] font-bold bg-[#D39375] text-[#1A1714] uppercase tracking-wider", children: item.badge })
35
+ ] });
36
+ const linkProps = {
37
+ href: resolvedHref,
38
+ target: item.target ?? (isExternal ? "_blank" : void 0),
39
+ rel: item.rel ?? (isExternal ? "noopener noreferrer" : void 0),
40
+ onClick: item.onClick,
41
+ className: cn(footerNavLinkVariants(), item.className),
42
+ "data-cross-zone": requiresCrossZone ? "true" : void 0,
43
+ "data-slot": "footer-nav-link"
44
+ };
45
+ return /* @__PURE__ */ jsx("li", { className: "list-none", children: CustomLink && !requiresCrossZone && !isExternal ? /* @__PURE__ */ jsx(CustomLink, { ...linkProps, children: linkContent }) : /* @__PURE__ */ jsx("a", { ...linkProps, children: linkContent }) }, item.id ?? String(item.label));
46
+ };
47
+ const linksList = /* @__PURE__ */ jsx(
48
+ "ul",
49
+ {
50
+ id: contentId,
51
+ role: "region",
52
+ "aria-labelledby": headerId,
53
+ className: "flex flex-col gap-3.5 pt-1 pb-3 md:py-0 list-none p-0 m-0",
54
+ "data-slot": "footer-nav-links-list",
55
+ children: items.map(renderLink)
56
+ }
57
+ );
58
+ return /* @__PURE__ */ jsxs(
59
+ "div",
60
+ {
61
+ className: cn(
62
+ "flex flex-col border-b border-white/10 md:border-b-0 py-3 md:py-0 transition-colors",
63
+ sectionClass,
64
+ className
65
+ ),
66
+ "data-slot": "footer-nav-section",
67
+ "data-section-id": id,
68
+ children: [
69
+ isMobileAccordion && collapsible ? /* @__PURE__ */ jsxs("div", { className: "md:hidden", children: [
70
+ /* @__PURE__ */ jsxs(
71
+ "button",
72
+ {
73
+ id: headerId,
74
+ type: "button",
75
+ "aria-expanded": isOpen,
76
+ "aria-controls": contentId,
77
+ onClick: onToggle,
78
+ className: cn(
79
+ footerSectionTitleVariants(),
80
+ "py-2 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#D39375]/50 rounded"
81
+ ),
82
+ "data-slot": "footer-section-accordion-trigger",
83
+ children: [
84
+ /* @__PURE__ */ jsx("span", { children: title }),
85
+ /* @__PURE__ */ jsx(
86
+ motion.span,
87
+ {
88
+ variants: shouldAnimate ? footerChevronVariants : void 0,
89
+ initial: false,
90
+ animate: shouldAnimate ? isOpen ? "expanded" : "collapsed" : void 0,
91
+ className: cn(
92
+ "shrink-0 text-[#D39375] transition-transform duration-200",
93
+ !shouldAnimate && (isOpen ? "rotate-180" : "rotate-0"),
94
+ motionClass
95
+ ),
96
+ children: /* @__PURE__ */ jsx(ChevronDown, { size: 18, className: "stroke-[2]" })
97
+ }
98
+ )
99
+ ]
100
+ }
101
+ ),
102
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx(
103
+ motion.div,
104
+ {
105
+ initial: shouldAnimate ? "hidden" : false,
106
+ animate: shouldAnimate ? "visible" : void 0,
107
+ exit: shouldAnimate ? "exit" : void 0,
108
+ variants: shouldAnimate ? footerAccordionVariants : void 0,
109
+ className: cn("overflow-hidden", motionClass),
110
+ children: linksList
111
+ }
112
+ ) })
113
+ ] }) : null,
114
+ /* @__PURE__ */ jsxs("div", { className: cn(isMobileAccordion && collapsible ? "hidden md:flex flex-col gap-5" : "flex flex-col gap-5"), children: [
115
+ /* @__PURE__ */ jsx("div", { className: footerSectionTitleVariants(), "data-slot": "footer-section-title", children: /* @__PURE__ */ jsx("span", { children: title }) }),
116
+ linksList
117
+ ] })
118
+ ]
119
+ }
120
+ );
121
+ };
122
+
123
+ export { FooterNavSection };
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ import type { NavFooterNewsletterConfig } from '../NavFooter.types';
3
+ export interface FooterNewsletterProps {
4
+ newsletter?: NavFooterNewsletterConfig;
5
+ className?: string;
6
+ }
7
+ export declare const FooterNewsletter: React.FC<FooterNewsletterProps>;
8
+ export default FooterNewsletter;
9
+ //# sourceMappingURL=FooterNewsletter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FooterNewsletter.d.ts","sourceRoot":"","sources":["../../../../src/components/NavFooter/subcomponents/FooterNewsletter.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAEpE,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAyG5D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,95 @@
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { CheckCircle, Loader2, ArrowRight, AlertCircle } from '@timmbr/icons';
5
+ import { cn } from '@timmbr/utils';
6
+
7
+ const FooterNewsletter = ({
8
+ newsletter,
9
+ className
10
+ }) => {
11
+ const {
12
+ title = "Stay in the loop",
13
+ description = "Sign up for our newsletter to receive updates and exclusive offers.",
14
+ placeholder = "Enter your email...",
15
+ buttonText = "Subscribe",
16
+ onSubmit,
17
+ status: controlledStatus,
18
+ errorMessage = "Something went wrong. Please try again.",
19
+ successMessage = "Thank you for subscribing!",
20
+ className: newsletterClass
21
+ } = newsletter || {};
22
+ const [email, setEmail] = React.useState("");
23
+ const [internalStatus, setInternalStatus] = React.useState("idle");
24
+ const currentStatus = controlledStatus !== void 0 ? controlledStatus : internalStatus;
25
+ const handleSubmit = async (e) => {
26
+ e.preventDefault();
27
+ if (!email || !email.includes("@") || currentStatus === "loading") return;
28
+ if (onSubmit) {
29
+ try {
30
+ setInternalStatus("loading");
31
+ await onSubmit(email);
32
+ setInternalStatus("success");
33
+ setEmail("");
34
+ } catch {
35
+ setInternalStatus("error");
36
+ }
37
+ } else {
38
+ setInternalStatus("success");
39
+ setEmail("");
40
+ }
41
+ };
42
+ return /* @__PURE__ */ jsxs(
43
+ "div",
44
+ {
45
+ className: cn("flex flex-col gap-3 max-w-[420px]", newsletterClass, className),
46
+ "data-slot": "footer-newsletter",
47
+ children: [
48
+ title && /* @__PURE__ */ jsx("div", { className: "font-title text-xs font-semibold text-[#D39375] uppercase tracking-[0.14em]", children: title }),
49
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm font-sans text-[#B7AEA2] m-0 leading-relaxed", children: description }),
50
+ currentStatus === "success" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-3 rounded-lg bg-[#4E8752]/20 border border-[#4E8752]/40 text-[#4E8752] text-sm font-sans", children: [
51
+ /* @__PURE__ */ jsx(CheckCircle, { size: 18, className: "shrink-0" }),
52
+ /* @__PURE__ */ jsx("span", { children: successMessage })
53
+ ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-2", children: [
54
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center rounded-lg bg-black/40 border border-white/15 focus-within:border-[#D39375] focus-within:ring-2 focus-within:ring-[#D39375]/30 transition-all p-1", children: [
55
+ /* @__PURE__ */ jsx(
56
+ "input",
57
+ {
58
+ type: "email",
59
+ value: email,
60
+ onChange: (e) => {
61
+ setEmail(e.target.value);
62
+ if (currentStatus === "error") setInternalStatus("idle");
63
+ },
64
+ placeholder,
65
+ required: true,
66
+ disabled: currentStatus === "loading",
67
+ className: "flex-1 bg-transparent px-3 py-2 text-sm text-[#F7F1E6] placeholder-[#B7AEA2]/60 focus:outline-none disabled:opacity-50",
68
+ "data-slot": "footer-newsletter-input"
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsx(
72
+ "button",
73
+ {
74
+ type: "submit",
75
+ disabled: currentStatus === "loading" || !email,
76
+ className: "inline-flex items-center justify-center gap-1.5 px-4 py-2 rounded-md bg-[#D39375] text-[#1A1714] font-title text-xs font-semibold uppercase tracking-wider hover:bg-[#D39375]/90 transition-all disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer shrink-0",
77
+ "data-slot": "footer-newsletter-button",
78
+ children: currentStatus === "loading" ? /* @__PURE__ */ jsx(Loader2, { size: 16, className: "animate-spin" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
79
+ /* @__PURE__ */ jsx("span", { children: buttonText }),
80
+ /* @__PURE__ */ jsx(ArrowRight, { size: 14, className: "stroke-[2.5]" })
81
+ ] })
82
+ }
83
+ )
84
+ ] }),
85
+ currentStatus === "error" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-red-400", children: [
86
+ /* @__PURE__ */ jsx(AlertCircle, { size: 14, className: "shrink-0" }),
87
+ /* @__PURE__ */ jsx("span", { children: errorMessage })
88
+ ] })
89
+ ] })
90
+ ]
91
+ }
92
+ );
93
+ };
94
+
95
+ export { FooterNewsletter };
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ import type { NavFooterOfficeConfig } from '../NavFooter.types';
3
+ export interface FooterOfficeProps {
4
+ office?: NavFooterOfficeConfig;
5
+ className?: string;
6
+ }
7
+ export declare const FooterOffice: React.FC<FooterOfficeProps>;
8
+ export default FooterOffice;
9
+ //# sourceMappingURL=FooterOffice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FooterOffice.d.ts","sourceRoot":"","sources":["../../../../src/components/NavFooter/subcomponents/FooterOffice.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAGhE,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA+DpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { cn } from '@timmbr/utils';
4
+ import { footerOfficeBlockVariants } from '../NavFooter.styles.js';
5
+
6
+ const FooterOffice = ({ office, className }) => {
7
+ const {
8
+ title = "Registered Office:",
9
+ address = "Timmbr Furnitures SABN Kallappa Layout, Ashwini Extenstion, Chintamani Karnataka 563125",
10
+ email,
11
+ phone,
12
+ extraContent,
13
+ className: officeClass
14
+ } = office || {};
15
+ return /* @__PURE__ */ jsxs(
16
+ "div",
17
+ {
18
+ className: cn("flex flex-col gap-2 max-w-[320px]", officeClass, className),
19
+ "data-slot": "footer-office",
20
+ children: [
21
+ title && /* @__PURE__ */ jsx(
22
+ "div",
23
+ {
24
+ className: "font-bold text-sm text-[#F7F1E6]/90 font-sans select-text",
25
+ "data-slot": "footer-office-title",
26
+ children: title
27
+ }
28
+ ),
29
+ address && /* @__PURE__ */ jsx(
30
+ "div",
31
+ {
32
+ className: cn(footerOfficeBlockVariants(), "select-text text-sm"),
33
+ "data-slot": "footer-office-address",
34
+ children: typeof address === "string" ? /* @__PURE__ */ jsx("p", { className: "m-0 leading-relaxed whitespace-pre-line", children: address }) : address
35
+ }
36
+ ),
37
+ (email || phone) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 pt-1 text-xs text-[#B7AEA2] font-sans", children: [
38
+ email && /* @__PURE__ */ jsx(
39
+ "a",
40
+ {
41
+ href: `mailto:${email}`,
42
+ className: "hover:text-[#D39375] transition-colors inline-block select-text",
43
+ "data-slot": "footer-office-email",
44
+ children: email
45
+ }
46
+ ),
47
+ phone && /* @__PURE__ */ jsx(
48
+ "a",
49
+ {
50
+ href: `tel:${phone.replace(/\s+/g, "")}`,
51
+ className: "hover:text-[#D39375] transition-colors inline-block select-text",
52
+ "data-slot": "footer-office-phone",
53
+ children: phone
54
+ }
55
+ )
56
+ ] }),
57
+ extraContent
58
+ ]
59
+ }
60
+ );
61
+ };
62
+
63
+ export { FooterOffice };
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import type { NavFooterSocialLink } from '../NavFooter.types';
3
+ export interface FooterSocialProps {
4
+ links?: NavFooterSocialLink[];
5
+ title?: React.ReactNode;
6
+ shouldAnimate?: boolean;
7
+ motionClass?: string;
8
+ className?: string;
9
+ }
10
+ export declare const FooterSocial: React.FC<FooterSocialProps>;
11
+ export default FooterSocial;
12
+ //# sourceMappingURL=FooterSocial.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FooterSocial.d.ts","sourceRoot":"","sources":["../../../../src/components/NavFooter/subcomponents/FooterSocial.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAI9D,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAsDpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { motion, footerSocialHoverVariants } from '@timmbr/motion';
4
+ import { cn } from '@timmbr/utils';
5
+ import { footerSocialButtonVariants } from '../NavFooter.styles.js';
6
+ import { DefaultSocialIcons } from '../NavFooter.helpers.js';
7
+
8
+ const FooterSocial = ({
9
+ links = [],
10
+ title,
11
+ shouldAnimate = true,
12
+ motionClass,
13
+ className
14
+ }) => {
15
+ if (!links || links.length === 0) return null;
16
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3", className), "data-slot": "footer-social", children: [
17
+ title && /* @__PURE__ */ jsx("div", { className: "text-xs font-semibold text-[#D39375] uppercase tracking-[0.14em] font-title", children: title }),
18
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 sm:gap-6 flex-wrap", "data-slot": "footer-social-icons", children: links.map((link) => {
19
+ const platformKey = link.name.toLowerCase();
20
+ const iconElement = link.icon ?? DefaultSocialIcons[platformKey] ?? /* @__PURE__ */ jsx("span", { className: "text-sm uppercase font-bold", children: link.name.slice(0, 2) });
21
+ const defaultAria = `Follow us on ${link.name.charAt(0).toUpperCase() + link.name.slice(1)}`;
22
+ return /* @__PURE__ */ jsx(
23
+ motion.a,
24
+ {
25
+ href: link.href,
26
+ target: link.target ?? "_blank",
27
+ rel: link.rel ?? "noopener noreferrer",
28
+ "aria-label": link.ariaLabel ?? defaultAria,
29
+ onClick: link.onClick,
30
+ variants: shouldAnimate ? footerSocialHoverVariants : void 0,
31
+ initial: "initial",
32
+ whileHover: shouldAnimate ? "hover" : void 0,
33
+ whileTap: shouldAnimate ? "tap" : void 0,
34
+ className: cn(
35
+ footerSocialButtonVariants(),
36
+ "relative p-1.5 text-[#D39375] hover:text-[#F7F1E6] transition-colors",
37
+ motionClass,
38
+ link.className
39
+ ),
40
+ "data-slot": "footer-social-link",
41
+ "data-platform": link.name,
42
+ children: iconElement
43
+ },
44
+ `${link.name}-${link.href}`
45
+ );
46
+ }) })
47
+ ] });
48
+ };
49
+
50
+ export { FooterSocial };
@@ -1 +1 @@
1
- {"version":3,"file":"NavHeader.d.ts","sourceRoot":"","sources":["../../../src/components/NavHeader/NavHeader.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,mBAAmB,CAAC;AAavE,eAAO,MAAM,SAAS,oFA4oBrB,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,MAAM,oFAAY,CAAC"}
1
+ {"version":3,"file":"NavHeader.d.ts","sourceRoot":"","sources":["../../../src/components/NavHeader/NavHeader.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EACV,cAAc,EAMf,MAAM,mBAAmB,CAAC;AAa3B,eAAO,MAAM,SAAS,oFAqpBrB,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,MAAM,oFAAY,CAAC"}
@@ -65,16 +65,28 @@ const NavHeader = React.forwardRef(
65
65
  const globalMotion = useGlobalAnimation();
66
66
  const { shouldAnimate, motionClass } = resolveMotion(motionProp, globalMotion);
67
67
  const isOfferBannerVisible = shorthandShowOfferBanner !== void 0 ? shorthandShowOfferBanner : typeof offerBanner === "object" && offerBanner !== null && "show" in offerBanner ? offerBanner.show ?? true : Boolean(offerBanner);
68
- const navigationConfig = Array.isArray(navigation) ? { items: navigation } : navigation ?? {};
69
- const navItems = shorthandItems ?? navigationConfig.items ?? [];
68
+ const navigationConfig = React.useMemo(
69
+ () => Array.isArray(navigation) ? { items: navigation } : navigation ?? {},
70
+ [navigation]
71
+ );
72
+ const navItems = React.useMemo(
73
+ () => shorthandItems ?? navigationConfig.items ?? [],
74
+ [shorthandItems, navigationConfig.items]
75
+ );
70
76
  const isNavItemsVisible = shorthandShowNavItems !== void 0 ? shorthandShowNavItems : navigationConfig.show ?? true;
71
- const maxVisibleNavItems = shorthandMaxVisibleNavItems !== void 0 ? shorthandMaxVisibleNavItems : navigationConfig.maxVisibleItems;
77
+ const maxVisibleNavItems = shorthandMaxVisibleNavItems ?? navigationConfig.maxVisibleItems;
72
78
  const controlledActiveId = shorthandActiveItemId !== void 0 ? shorthandActiveItemId : navigationConfig.activeItemId;
73
79
  const defaultActiveItemId = shorthandDefaultActiveItemId !== void 0 ? shorthandDefaultActiveItemId : navigationConfig.defaultActiveItemId ?? null;
74
80
  const onActiveItemChange = shorthandOnActiveItemChange ?? navigationConfig.onActiveItemChange;
75
- const searchConfig = typeof search === "object" && search !== null ? search : {};
81
+ const searchConfig = React.useMemo(
82
+ () => typeof search === "object" && search !== null ? search : {},
83
+ [search]
84
+ );
76
85
  const isSearchVisible = shorthandShowSearch !== void 0 ? shorthandShowSearch : searchConfig.show ?? Boolean(search);
77
- const actionsConfig = Array.isArray(actions) ? { items: actions } : actions ?? {};
86
+ const actionsConfig = React.useMemo(
87
+ () => Array.isArray(actions) ? { items: actions } : actions ?? {},
88
+ [actions]
89
+ );
78
90
  const actionItems = actionsConfig.items;
79
91
  const isActionsVisible = shorthandShowActions !== void 0 ? shorthandShowActions : actionsConfig.show ?? true;
80
92
  const maxVisibleActions = shorthandMaxVisibleActions !== void 0 ? shorthandMaxVisibleActions : actionsConfig.maxVisible;
@@ -83,7 +95,10 @@ const NavHeader = React.forwardRef(
83
95
  const profileAction = shorthandProfileAction ?? actionsConfig.profile;
84
96
  const wishlistAction = shorthandWishlistAction ?? actionsConfig.wishlist;
85
97
  const cartAction = shorthandCartAction ?? actionsConfig.cart;
86
- const mobileConfig = mobile ?? {};
98
+ const mobileConfig = React.useMemo(
99
+ () => mobile ?? {},
100
+ [mobile]
101
+ );
87
102
  const tabSliderOption = mobileConfig.tabSlider;
88
103
  const tabSliderConfig = typeof tabSliderOption === "object" && tabSliderOption !== null ? tabSliderOption : {};
89
104
  const isMobileTabSliderEnabled = shorthandMobileTabSlider !== void 0 ? shorthandMobileTabSlider : typeof tabSliderOption === "boolean" ? tabSliderOption : tabSliderConfig.show ?? true;
@@ -237,7 +252,7 @@ const NavHeader = React.forwardRef(
237
252
  window.removeEventListener("resize", handleResize);
238
253
  resizeObserver?.disconnect();
239
254
  };
240
- }, [maxVisibleActions, navItems.length, search]);
255
+ }, [maxVisibleActions, navItems.length, search, isSearchVisible]);
241
256
  const [uncontrolledMobileActiveId, setUncontrolledMobileActiveId] = React.useState(
242
257
  defaultMobileActiveItemId
243
258
  );
@@ -111,7 +111,7 @@ const NavActions = ({
111
111
  if (items.length <= limit) {
112
112
  return { visibleActions: items, overflowActions: [] };
113
113
  }
114
- const countToKeep = typeof maxVisibleActions === "number" ? maxVisibleActions : Math.max(1, limit - 1);
114
+ const countToKeep = Math.max(1, limit - 1);
115
115
  const hasPriorities = items.some((i) => i.priority);
116
116
  if (!hasPriorities) {
117
117
  return {
@@ -1 +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"}
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,CAiG5C,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -41,19 +41,17 @@ const NavBrand = ({
41
41
  children: logo
42
42
  }
43
43
  ),
44
- miniLogo && /* @__PURE__ */ jsx(
44
+ showMini && /* @__PURE__ */ jsx(
45
45
  motion.div,
46
46
  {
47
- initial: false,
47
+ initial: shouldAnimate ? { opacity: 0, scale: 0.92 } : false,
48
48
  animate: shouldAnimate ? {
49
- opacity: showMini ? 1 : 0,
50
- scale: showMini ? 1 : 0.92
49
+ opacity: 1,
50
+ scale: 1
51
51
  } : void 0,
52
52
  transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
53
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"),
54
+ "absolute inset-0 flex items-center justify-center shrink-0 pointer-events-auto",
57
55
  motionClass
58
56
  ),
59
57
  children: miniLogo
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import * as React from 'react';
4
- import { AnimatePresence, motion, headerMegaMenuVariants, getTransition } from '@timmbr/motion';
4
+ import { AnimatePresence, motion, fadeSlideDownVariants, getTransition } from '@timmbr/motion';
5
5
  import { cn } from '@timmbr/utils';
6
6
  import { megaMenuCardVariants } from '../NavHeader.styles.js';
7
7
 
@@ -50,7 +50,7 @@ const NavMegaMenu = ({
50
50
  initial: shouldAnimate ? "hidden" : false,
51
51
  animate: shouldAnimate ? "visible" : void 0,
52
52
  exit: shouldAnimate ? "exit" : void 0,
53
- variants: shouldAnimate ? headerMegaMenuVariants : void 0,
53
+ variants: shouldAnimate ? fadeSlideDownVariants : void 0,
54
54
  transition: getTransition("fast"),
55
55
  style: widthStyle,
56
56
  "data-slot": "nav-megamenu-card",
@@ -71,10 +71,10 @@ const NavMobileSearchDrawer = ({
71
71
  role: "dialog",
72
72
  "aria-modal": "true",
73
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] },
74
+ initial: shouldAnimate ? { opacity: 0, y: "100%" } : false,
75
+ animate: shouldAnimate ? { opacity: 1, y: "0%" } : void 0,
76
+ exit: shouldAnimate ? { opacity: 0, y: "100%" } : void 0,
77
+ transition: { duration: 0.28, ease: [0.32, 0.72, 0, 1] },
78
78
  className: cn(mobileSearchDrawerVariants(), motionClass),
79
79
  "data-slot": "nav-mobile-search-drawer",
80
80
  children: [
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=next-headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next-headers.d.ts","sourceRoot":"","sources":["../../src/test-mocks/next-headers.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmbr/ui",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
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",
@@ -41,11 +41,11 @@
41
41
  "@radix-ui/react-toast": "^1.2.23",
42
42
  "@radix-ui/react-tooltip": "^1.2.16",
43
43
  "class-variance-authority": "^0.7.1",
44
+ "@timmbr/motion": "1.2.0",
45
+ "@timmbr/icons": "1.2.1",
44
46
  "@timmbr/hooks": "1.0.0-beta",
45
- "@timmbr/theme": "1.1.0",
46
- "@timmbr/utils": "1.1.0",
47
- "@timmbr/icons": "1.2.0",
48
- "@timmbr/motion": "1.1.0"
47
+ "@timmbr/theme": "1.2.0",
48
+ "@timmbr/utils": "1.2.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "next": ">=13.0.0 || >=14.0.0 || >=15.0.0 || >=16.0.0",