@salesmind-ai/design-system 1.7.0 → 1.8.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.
@@ -636,6 +636,24 @@ var CheckIcon = () => /* @__PURE__ */ jsx(
636
636
  children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
637
637
  }
638
638
  );
639
+ var SearchIcon = () => /* @__PURE__ */ jsxs(
640
+ "svg",
641
+ {
642
+ width: "14",
643
+ height: "14",
644
+ viewBox: "0 0 24 24",
645
+ fill: "none",
646
+ stroke: "currentColor",
647
+ strokeWidth: "2",
648
+ strokeLinecap: "round",
649
+ strokeLinejoin: "round",
650
+ "aria-hidden": "true",
651
+ children: [
652
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
653
+ /* @__PURE__ */ jsx("path", { d: "m21 21-4.3-4.3" })
654
+ ]
655
+ }
656
+ );
639
657
  var LocaleDropdown = forwardRef(
640
658
  ({
641
659
  locales,
@@ -645,16 +663,29 @@ var LocaleDropdown = forwardRef(
645
663
  getHref,
646
664
  className,
647
665
  direction = "down",
648
- align = "right"
666
+ align = "right",
667
+ searchable = false,
668
+ searchPlaceholder = "Search languages",
669
+ searchEmptyLabel = "No languages found"
649
670
  }, ref) => {
650
671
  const [isOpen, setIsOpen] = useState(false);
651
672
  const [focusIndex, setFocusIndex] = useState(-1);
673
+ const [query, setQuery] = useState("");
652
674
  const containerRef = useRef(null);
653
675
  const triggerRef = useRef(null);
654
676
  const listRef = useRef(null);
677
+ const searchInputRef = useRef(null);
655
678
  const instanceId = useId();
656
679
  const listboxId = `locale-dropdown-${instanceId}`;
657
680
  const currentLocale = locales.find((l) => l.code === activeLocale) ?? locales[0];
681
+ const filteredLocales = useMemo(() => {
682
+ if (!searchable) return locales;
683
+ const q = query.trim().toLowerCase();
684
+ if (!q) return locales;
685
+ return locales.filter(
686
+ (l) => l.label.toLowerCase().includes(q) || l.code.toLowerCase().includes(q)
687
+ );
688
+ }, [locales, query, searchable]);
658
689
  const handleToggle = useCallback(() => {
659
690
  setIsOpen((prev) => {
660
691
  if (!prev) setFocusIndex(-1);
@@ -664,6 +695,7 @@ var LocaleDropdown = forwardRef(
664
695
  const handleClose = useCallback(() => {
665
696
  setIsOpen(false);
666
697
  setFocusIndex(-1);
698
+ setQuery("");
667
699
  triggerRef.current?.focus();
668
700
  }, []);
669
701
  const handleSelect = useCallback(
@@ -683,6 +715,14 @@ var LocaleDropdown = forwardRef(
683
715
  document.addEventListener("mousedown", handleClickOutside);
684
716
  return () => document.removeEventListener("mousedown", handleClickOutside);
685
717
  }, [isOpen, handleClose]);
718
+ useEffect(() => {
719
+ if (!isOpen || !searchable) return;
720
+ const raf = requestAnimationFrame(() => searchInputRef.current?.focus());
721
+ return () => cancelAnimationFrame(raf);
722
+ }, [isOpen, searchable]);
723
+ useEffect(() => {
724
+ setFocusIndex(-1);
725
+ }, [query]);
686
726
  const handleKeyDown = useCallback(
687
727
  (e) => {
688
728
  if (!isOpen) {
@@ -693,6 +733,7 @@ var LocaleDropdown = forwardRef(
693
733
  }
694
734
  return;
695
735
  }
736
+ const count = filteredLocales.length;
696
737
  switch (e.key) {
697
738
  case "Escape":
698
739
  e.preventDefault();
@@ -700,33 +741,44 @@ var LocaleDropdown = forwardRef(
700
741
  break;
701
742
  case "ArrowDown":
702
743
  e.preventDefault();
703
- setFocusIndex((prev) => (prev + 1) % locales.length);
744
+ if (count > 0) setFocusIndex((prev) => (prev + 1) % count);
704
745
  break;
705
746
  case "ArrowUp":
706
747
  e.preventDefault();
707
- setFocusIndex((prev) => (prev - 1 + locales.length) % locales.length);
748
+ if (count > 0) setFocusIndex((prev) => (prev - 1 + count) % count);
708
749
  break;
709
750
  case "Enter":
710
- case " ":
711
751
  e.preventDefault();
712
- if (focusIndex >= 0 && focusIndex < locales.length) {
713
- handleSelect(locales[focusIndex].code);
752
+ if (focusIndex >= 0 && focusIndex < count) {
753
+ handleSelect(filteredLocales[focusIndex].code);
754
+ }
755
+ break;
756
+ case " ":
757
+ if (!searchable) {
758
+ e.preventDefault();
759
+ if (focusIndex >= 0 && focusIndex < count) {
760
+ handleSelect(filteredLocales[focusIndex].code);
761
+ }
714
762
  }
715
763
  break;
716
764
  case "Home":
717
- e.preventDefault();
718
- setFocusIndex(0);
765
+ if (!searchable) {
766
+ e.preventDefault();
767
+ if (count > 0) setFocusIndex(0);
768
+ }
719
769
  break;
720
770
  case "End":
721
- e.preventDefault();
722
- setFocusIndex(locales.length - 1);
771
+ if (!searchable) {
772
+ e.preventDefault();
773
+ if (count > 0) setFocusIndex(count - 1);
774
+ }
723
775
  break;
724
776
  case "Tab":
725
777
  handleClose();
726
778
  break;
727
779
  }
728
780
  },
729
- [isOpen, focusIndex, locales, handleClose, handleSelect]
781
+ [isOpen, focusIndex, filteredLocales, searchable, handleClose, handleSelect]
730
782
  );
731
783
  useEffect(() => {
732
784
  if (!isOpen || focusIndex < 0 || !listRef.current) return;
@@ -734,6 +786,30 @@ var LocaleDropdown = forwardRef(
734
786
  items[focusIndex]?.scrollIntoView({ block: "nearest" });
735
787
  }, [isOpen, focusIndex]);
736
788
  const activeDescendantId = isOpen && focusIndex >= 0 ? `${listboxId}-option-${focusIndex}` : void 0;
789
+ const renderOption = (locale, index) => {
790
+ const isActive = locale.code === activeLocale;
791
+ const isFocused = index === focusIndex;
792
+ const optionId = `${listboxId}-option-${index}`;
793
+ const optionProps = {
794
+ id: optionId,
795
+ role: "option",
796
+ "aria-selected": isActive,
797
+ className: clsx(
798
+ "ds-locale-dropdown__option",
799
+ isActive && "ds-locale-dropdown__option--active",
800
+ isFocused && "ds-locale-dropdown__option--focused"
801
+ ),
802
+ onClick: () => handleSelect(locale.code),
803
+ ...OptionComponent !== "button" && getHref ? { href: getHref(locale.code) } : { type: "button" }
804
+ };
805
+ return /* @__PURE__ */ jsxs(OptionComponent, { ...optionProps, children: [
806
+ /* @__PURE__ */ jsxs("span", { className: "ds-locale-dropdown__option-content", children: [
807
+ locale.flag && /* @__PURE__ */ jsx("span", { className: "ds-locale-dropdown__option-flag", "aria-hidden": "true", children: locale.flag }),
808
+ /* @__PURE__ */ jsx("span", { className: "ds-locale-dropdown__option-label", children: locale.label })
809
+ ] }),
810
+ isActive && /* @__PURE__ */ jsx("span", { className: "ds-locale-dropdown__option-check", children: /* @__PURE__ */ jsx(CheckIcon, {}) })
811
+ ] }, locale.code);
812
+ };
737
813
  return /* @__PURE__ */ jsx(
738
814
  "div",
739
815
  {
@@ -757,7 +833,7 @@ var LocaleDropdown = forwardRef(
757
833
  "aria-haspopup": "listbox",
758
834
  "aria-expanded": isOpen,
759
835
  "aria-controls": isOpen ? listboxId : void 0,
760
- "aria-activedescendant": activeDescendantId,
836
+ "aria-activedescendant": searchable ? void 0 : activeDescendantId,
761
837
  "aria-label": `Language: ${currentLocale.label}. Change language.`,
762
838
  children: [
763
839
  /* @__PURE__ */ jsx("span", { className: "ds-locale-dropdown__trigger-icon", children: /* @__PURE__ */ jsx(GlobeIcon, {}) }),
@@ -773,7 +849,49 @@ var LocaleDropdown = forwardRef(
773
849
  ]
774
850
  }
775
851
  ),
776
- /* @__PURE__ */ jsx(
852
+ searchable ? /* @__PURE__ */ jsxs(
853
+ "div",
854
+ {
855
+ className: "ds-locale-dropdown__panel ds-locale-dropdown__panel--searchable",
856
+ "data-state": isOpen ? "open" : "closed",
857
+ children: [
858
+ /* @__PURE__ */ jsxs("div", { className: "ds-locale-dropdown__search", children: [
859
+ /* @__PURE__ */ jsx("span", { className: "ds-locale-dropdown__search-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(SearchIcon, {}) }),
860
+ /* @__PURE__ */ jsx(
861
+ "input",
862
+ {
863
+ ref: searchInputRef,
864
+ type: "text",
865
+ className: "ds-locale-dropdown__search-input",
866
+ placeholder: searchPlaceholder,
867
+ value: query,
868
+ onChange: (e) => setQuery(e.target.value),
869
+ onKeyDown: handleKeyDown,
870
+ role: "combobox",
871
+ "aria-expanded": isOpen,
872
+ "aria-controls": listboxId,
873
+ "aria-activedescendant": activeDescendantId,
874
+ "aria-autocomplete": "list",
875
+ "aria-label": searchPlaceholder,
876
+ autoComplete: "off",
877
+ spellCheck: false
878
+ }
879
+ )
880
+ ] }),
881
+ /* @__PURE__ */ jsx(
882
+ "div",
883
+ {
884
+ ref: listRef,
885
+ id: listboxId,
886
+ className: "ds-locale-dropdown__list",
887
+ role: "listbox",
888
+ "aria-label": "Select language",
889
+ children: filteredLocales.length === 0 ? /* @__PURE__ */ jsx("div", { className: "ds-locale-dropdown__empty", role: "status", children: searchEmptyLabel }) : filteredLocales.map(renderOption)
890
+ }
891
+ )
892
+ ]
893
+ }
894
+ ) : /* @__PURE__ */ jsx(
777
895
  "div",
778
896
  {
779
897
  ref: listRef,
@@ -783,30 +901,7 @@ var LocaleDropdown = forwardRef(
783
901
  "aria-label": "Select language",
784
902
  "data-state": isOpen ? "open" : "closed",
785
903
  onKeyDown: handleKeyDown,
786
- children: locales.map((locale, index) => {
787
- const isActive = locale.code === activeLocale;
788
- const isFocused = index === focusIndex;
789
- const optionId = `${listboxId}-option-${index}`;
790
- const optionProps = {
791
- id: optionId,
792
- role: "option",
793
- "aria-selected": isActive,
794
- className: clsx(
795
- "ds-locale-dropdown__option",
796
- isActive && "ds-locale-dropdown__option--active",
797
- isFocused && "ds-locale-dropdown__option--focused"
798
- ),
799
- onClick: () => handleSelect(locale.code),
800
- ...OptionComponent !== "button" && getHref ? { href: getHref(locale.code) } : { type: "button" }
801
- };
802
- return /* @__PURE__ */ jsxs(OptionComponent, { ...optionProps, children: [
803
- /* @__PURE__ */ jsxs("span", { className: "ds-locale-dropdown__option-content", children: [
804
- locale.flag && /* @__PURE__ */ jsx("span", { className: "ds-locale-dropdown__option-flag", "aria-hidden": "true", children: locale.flag }),
805
- /* @__PURE__ */ jsx("span", { className: "ds-locale-dropdown__option-label", children: locale.label })
806
- ] }),
807
- isActive && /* @__PURE__ */ jsx("span", { className: "ds-locale-dropdown__option-check", children: /* @__PURE__ */ jsx(CheckIcon, {}) })
808
- ] }, locale.code);
809
- })
904
+ children: filteredLocales.map(renderOption)
810
905
  }
811
906
  )
812
907
  ] })
@@ -838,4 +933,4 @@ var MDXOutboundLink = ({
838
933
 
839
934
  export { LocaleDropdown, MDXOutboundLink, NavbarV2, NavbarV2Actions, NavbarV2Brand, NavbarV2Tab, NavbarV2TabMenu, NavbarV2Tabs, SiteFooter };
840
935
  //# sourceMappingURL=out.js.map
841
- //# sourceMappingURL=chunk-6BUS7RMS.js.map
936
+ //# sourceMappingURL=chunk-Q3YVBUKQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/NavbarV2/NavbarV2.tsx","../src/components/SiteFooter/SiteFooter.tsx","../src/components/LocaleDropdown/LocaleDropdown.tsx","../src/components/OutboundLink/MDXOutboundLink.tsx"],"names":["focusable","forwardRef","useState","clsx","jsx","jsxs","useRef","useEffect","useCallback","useMemo","useId"],"mappings":"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","sourcesContent":["import React, {\n forwardRef,\n useState,\n useRef,\n useEffect,\n useCallback,\n useContext,\n createContext,\n useMemo,\n useId,\n} from 'react';\nimport clsx from 'clsx';\nimport './NavbarV2.css';\n\n/* ============================================================================\n Icons\n ============================================================================ */\n\nconst ChevronDown = () => (\n <svg width=\"8\" height=\"8\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M4 6L8 10L12 6\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\n/* ============================================================================\n Scroll State Context — lets children (e.g. NavbarV2Tabs) know when\n the outer container has activated its glass surface, so they can\n demote their own glass to avoid glass-on-glass visual noise.\n ============================================================================ */\n\ninterface ScrollContextValue {\n /** Whether the navbar container is in the scrolled (glass) state. */\n scrolled: boolean;\n}\n\nconst ScrollContext = createContext<ScrollContextValue>({ scrolled: false });\n\n/* ============================================================================\n Mutual Exclusion Context — only one mega menu open at a time\n ============================================================================ */\n\ninterface TabsContextValue {\n /** ID of the currently open menu (null = all closed). */\n activeMenuId: string | null;\n /** Request to open a menu. Closes any other open menu. */\n openMenu: (id: string) => void;\n /** Request to close a specific menu. */\n closeMenu: (id: string) => void;\n}\n\nconst TabsContext = createContext<TabsContextValue>({\n activeMenuId: null,\n openMenu: () => {},\n closeMenu: () => {},\n});\n\n/* ============================================================================\n Focus Trap Hook — traps Tab/Shift+Tab inside a container\n ============================================================================ */\n\nfunction useFocusTrap(containerRef: React.RefObject<HTMLElement | null>, active: boolean) {\n useEffect(() => {\n if (!active || !containerRef.current) return;\n\n const container = containerRef.current;\n const FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n\n const focusable = Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE));\n if (focusable.length === 0) {\n e.preventDefault();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n };\n\n // Focus first element on trap activation\n const focusable = container.querySelectorAll<HTMLElement>(FOCUSABLE);\n if (focusable.length > 0) {\n focusable[0].focus();\n }\n\n container.addEventListener('keydown', handleKeyDown);\n return () => container.removeEventListener('keydown', handleKeyDown);\n }, [active, containerRef]);\n}\n\n/* ============================================================================\n NavbarV2 — Floating Glass Pill Tab Navigation\n ============================================================================\n A centered floating glass pill with vertical icon+label tabs,\n flanked by brand (left) and actions (right).\n\n On scroll, an outer container activates a subtle glass surface with\n border, wrapping all content (brand + tabs + actions). The inner\n tabs pill demotes its own glass to avoid double-glass visual noise.\n\n Composition:\n <NavbarV2>\n <NavbarV2Brand />\n <NavbarV2Tabs>\n <NavbarV2Tab /> // simple link/button\n <NavbarV2TabMenu /> // tab with mega menu dropdown\n </NavbarV2Tabs>\n <NavbarV2Actions />\n </NavbarV2>\n ============================================================================ */\n\nexport interface NavbarV2Props extends React.HTMLAttributes<HTMLElement> {\n /** Content rendered inside the mobile menu overlay when open. */\n mobileContent?: React.ReactNode;\n /** Accessible labels for mobile toggle. Override for i18n. */\n mobileToggleLabels?: { open: string; close: string };\n /** Controlled mobile menu open state. */\n isMenuOpen?: boolean;\n /** Callback when mobile menu open state changes. */\n onMenuOpenChange?: (isOpen: boolean) => void;\n /** Enable scroll-aware glass container with border. @default true */\n scrollBorder?: boolean;\n /** Scroll distance (px) before glass activates. @default 32 */\n scrollThreshold?: number;\n}\n\n/** Hysteresis lower-bound ratio — glass disappears when scroll drops below threshold × this */\nconst HYSTERESIS_RATIO = 0.25;\n\nexport const NavbarV2 = forwardRef<HTMLElement, NavbarV2Props>(\n (\n {\n className,\n mobileContent,\n mobileToggleLabels,\n isMenuOpen,\n onMenuOpenChange,\n scrollBorder = true,\n scrollThreshold = 32,\n children,\n ...props\n },\n ref,\n ) => {\n const [internalMenuOpen, setInternalMenuOpen] = useState(false);\n const isMobileMenuOpen = isMenuOpen !== undefined ? isMenuOpen : internalMenuOpen;\n\n const navRef = useRef<HTMLElement>(null);\n const overlayRef = useRef<HTMLDivElement>(null);\n const [navHeight, setNavHeight] = useState(64);\n\n const openLabel = mobileToggleLabels?.open ?? 'Open menu';\n const closeLabel = mobileToggleLabels?.close ?? 'Close menu';\n\n /* ── Scroll detection with hysteresis ──────────────────────────────\n Glass appears at scrollThreshold (32px default).\n Glass disappears only when scrolling back above threshold × 0.25 (8px).\n This prevents flicker when hovering near the threshold.\n Initial state is always false to match SSR — the useEffect below\n corrects it on mount before the browser paints. */\n\n const [scrolled, setScrolled] = useState(false);\n\n useEffect(() => {\n if (!scrollBorder) return;\n\n const lowerBound = scrollThreshold * HYSTERESIS_RATIO;\n\n const handleScroll = () => {\n const y = window.scrollY;\n setScrolled((prev) => {\n if (!prev && y > scrollThreshold) return true;\n if (prev && y < lowerBound) return false;\n return prev;\n });\n };\n\n // SSR flash guard — set correct state on mount\n handleScroll();\n\n window.addEventListener('scroll', handleScroll, { passive: true });\n return () => window.removeEventListener('scroll', handleScroll);\n }, [scrollBorder, scrollThreshold]);\n\n const isScrolled = scrollBorder && scrolled;\n\n const scrollCtx = useMemo<ScrollContextValue>(\n () => ({ scrolled: isScrolled }),\n [isScrolled],\n );\n\n const handleToggle = () => {\n const newState = !isMobileMenuOpen;\n setInternalMenuOpen(newState);\n onMenuOpenChange?.(newState);\n };\n\n /* Measure navbar height for mobile overlay positioning + CSS overlap var */\n useEffect(() => {\n const el = navRef.current;\n if (!el) return;\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const h = entry.contentRect.height;\n setNavHeight(h);\n /* Expose height as CSS custom property for layout overlap tricks */\n el.style.setProperty('--navbar-overlap', `${h}px`);\n }\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n /* Lock body scroll when mobile menu is open */\n useEffect(() => {\n if (isMobileMenuOpen) {\n document.body.style.overflow = 'hidden';\n return () => {\n document.body.style.overflow = '';\n };\n }\n }, [isMobileMenuOpen]);\n\n /* Close on Escape */\n useEffect(() => {\n if (!isMobileMenuOpen) return;\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setInternalMenuOpen(false);\n onMenuOpenChange?.(false);\n }\n };\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [isMobileMenuOpen, onMenuOpenChange]);\n\n /* Focus trap for mobile overlay */\n useFocusTrap(overlayRef, isMobileMenuOpen);\n\n /* Merge refs: forwardRef + internal navRef */\n const mergedRef = useMergedRef(ref, navRef);\n\n return (\n <ScrollContext.Provider value={scrollCtx}>\n <nav\n ref={mergedRef}\n className={clsx(\n 'ds-navbar-v2',\n isScrolled && 'ds-navbar-v2--scrolled',\n className,\n )}\n {...props}\n >\n {/* Inner container — receives glass treatment on scroll */}\n <div className=\"ds-navbar-v2__container\">\n {children}\n\n {/* Mobile Toggle */}\n <button\n className=\"ds-navbar-v2__toggle\"\n onClick={handleToggle}\n aria-label={isMobileMenuOpen ? closeLabel : openLabel}\n aria-expanded={isMobileMenuOpen}\n >\n {isMobileMenuOpen ? (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n ) : (\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\" />\n <line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\" />\n <line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\" />\n </svg>\n )}\n </button>\n </div>\n\n {/* Mobile Menu Overlay — always mounted, CSS transitions handle show/hide */}\n <div\n ref={overlayRef}\n className=\"ds-navbar-v2__mobile-overlay\"\n style={{ top: navHeight }}\n data-state={isMobileMenuOpen ? 'open' : 'closed'}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={closeLabel}\n >\n <div className=\"ds-navbar-v2__mobile-content\">\n {mobileContent}\n </div>\n </div>\n </nav>\n </ScrollContext.Provider>\n );\n },\n);\n\nNavbarV2.displayName = 'NavbarV2';\n\n/* ============================================================================\n NavbarV2Brand — Logo area (left of pill)\n ============================================================================ */\n\nexport interface NavbarV2BrandProps extends React.HTMLAttributes<HTMLElement> {\n /** Render as custom element (e.g. Next.js Link). @default 'a' */\n as?: React.ElementType;\n href?: string;\n}\n\nexport const NavbarV2Brand = forwardRef<HTMLElement, NavbarV2BrandProps>(\n ({ as: Component = 'a', className, children, ...props }, ref) => (\n <Component ref={ref} className={clsx('ds-navbar-v2__brand', className)} {...props}>\n {children}\n </Component>\n ),\n);\n\nNavbarV2Brand.displayName = 'NavbarV2Brand';\n\n/* ============================================================================\n NavbarV2Tabs — The floating glass pill containing tab items\n Provides mutual-exclusion context so only one mega menu is open at a time.\n Reads ScrollContext to demote glass when outer container is active.\n ============================================================================ */\n\nexport const NavbarV2Tabs = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, children, ...props }, ref) => {\n const [activeMenuId, setActiveMenuId] = useState<string | null>(null);\n const { scrolled } = useContext(ScrollContext);\n\n const ctxValue = useMemo<TabsContextValue>(\n () => ({\n activeMenuId,\n openMenu: (id: string) => setActiveMenuId(id),\n closeMenu: (id: string) =>\n setActiveMenuId((prev) => (prev === id ? null : prev)),\n }),\n [activeMenuId],\n );\n\n return (\n <TabsContext.Provider value={ctxValue}>\n {/* No role — parent <nav> already declares navigation landmark */}\n <div\n ref={ref}\n className={clsx(\n 'ds-navbar-v2__tabs',\n scrolled && 'ds-navbar-v2__tabs--demoted',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n </TabsContext.Provider>\n );\n },\n);\n\nNavbarV2Tabs.displayName = 'NavbarV2Tabs';\n\n/* ============================================================================\n NavbarV2Tab — Vertical icon + label tab (simple link or button)\n ============================================================================ */\n\nexport interface NavbarV2TabProps extends React.HTMLAttributes<HTMLElement> {\n /** Render as custom element (e.g. Next.js Link). @default 'button' */\n as?: React.ElementType;\n /** Whether this tab is the current page. */\n active?: boolean;\n /** Icon element (rendered above label). */\n icon?: React.ReactNode;\n /** Tab label text (rendered below icon). */\n label?: string;\n href?: string;\n}\n\nexport const NavbarV2Tab = forwardRef<HTMLElement, NavbarV2TabProps>(\n ({ as: Component = 'button', active, icon, label, className, children, ...props }, ref) => (\n <Component\n ref={ref}\n className={clsx('ds-navbar-v2__tab', active && 'ds-navbar-v2__tab--active', className)}\n aria-current={active ? 'page' : undefined}\n {...(Component === 'button' ? { type: 'button' } : {})}\n {...props}\n >\n {icon && <span className=\"ds-navbar-v2__tab-icon\">{icon}</span>}\n {label && <span className=\"ds-navbar-v2__tab-label\">{label}</span>}\n {children}\n </Component>\n ),\n);\n\nNavbarV2Tab.displayName = 'NavbarV2Tab';\n\n/* ============================================================================\n NavbarV2TabMenu — Vertical tab that triggers a mega menu dropdown\n ============================================================================ */\n\nexport interface NavbarV2TabMenuProps {\n /** Icon for the tab trigger (rendered above label). */\n icon?: React.ReactNode;\n /** Label for the tab trigger (rendered below icon). */\n label: string;\n /** Whether this tab represents the current section. */\n active?: boolean;\n /** Mega menu panel content. */\n children: React.ReactNode;\n /** Optional class name. */\n className?: string;\n /** Controlled open state. */\n isOpen?: boolean;\n /** Controlled open change handler. */\n onOpenChange?: (isOpen: boolean) => void;\n}\n\nexport const NavbarV2TabMenu = forwardRef<HTMLDivElement, NavbarV2TabMenuProps>(\n (\n {\n icon,\n label,\n active,\n children,\n className,\n isOpen: controlledOpen,\n onOpenChange,\n },\n ref,\n ) => {\n /* Stable ID for mutual exclusion + aria-controls */\n const instanceId = useId();\n const panelId = `navbar-v2-panel-${instanceId}`;\n\n /* Mutual exclusion via context */\n const { activeMenuId, openMenu, closeMenu } = useContext(TabsContext);\n\n /* Support both controlled (isOpen prop) and context-driven state */\n const isControlled = controlledOpen !== undefined;\n const isOpen = isControlled ? controlledOpen : activeMenuId === instanceId;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<number | null>(null);\n\n const handleOpen = useCallback(() => {\n if (!isOpen) {\n if (!isControlled) openMenu(instanceId);\n onOpenChange?.(true);\n }\n }, [isOpen, isControlled, openMenu, instanceId, onOpenChange]);\n\n const handleClose = useCallback(() => {\n if (isOpen) {\n if (!isControlled) closeMenu(instanceId);\n onOpenChange?.(false);\n }\n }, [isOpen, isControlled, closeMenu, instanceId, onOpenChange]);\n\n const handleToggle = useCallback(() => {\n if (isOpen) handleClose();\n else handleOpen();\n }, [isOpen, handleClose, handleOpen]);\n\n const handleMouseEnter = useCallback(() => {\n if (timeoutRef.current) {\n window.clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n handleOpen();\n }, [handleOpen]);\n\n const handleMouseLeave = useCallback(() => {\n if (timeoutRef.current) window.clearTimeout(timeoutRef.current);\n timeoutRef.current = window.setTimeout(handleClose, 150);\n }, [handleClose]);\n\n /* Cleanup timeout on unmount */\n useEffect(() => {\n return () => {\n if (timeoutRef.current) window.clearTimeout(timeoutRef.current);\n };\n }, []);\n\n /* Click outside → close */\n useEffect(() => {\n if (!isOpen) return;\n const handleClickOutside = (event: MouseEvent) => {\n if (\n containerRef.current &&\n !containerRef.current.contains(event.target as Node) &&\n triggerRef.current &&\n !triggerRef.current.contains(event.target as Node)\n ) {\n handleClose();\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [isOpen, handleClose]);\n\n /* Escape → close */\n useEffect(() => {\n if (!isOpen) return;\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n handleClose();\n triggerRef.current?.focus();\n }\n };\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [isOpen, handleClose]);\n\n /* Viewport edge clamping — nudge panel so it doesn't overflow */\n useEffect(() => {\n if (!isOpen || !panelRef.current) return;\n\n const clampToViewport = () => {\n const panel = panelRef.current;\n if (!panel) return;\n\n // Reset any previous nudge\n panel.style.setProperty('--panel-nudge', '0px');\n\n const rect = panel.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const EDGE_PADDING = 16;\n\n if (rect.right > viewportWidth - EDGE_PADDING) {\n const overflow = rect.right - (viewportWidth - EDGE_PADDING);\n panel.style.setProperty('--panel-nudge', `${-overflow}px`);\n } else if (rect.left < EDGE_PADDING) {\n const overflow = EDGE_PADDING - rect.left;\n panel.style.setProperty('--panel-nudge', `${overflow}px`);\n }\n };\n\n // Clamp after render + animation start\n requestAnimationFrame(clampToViewport);\n window.addEventListener('resize', clampToViewport);\n return () => window.removeEventListener('resize', clampToViewport);\n }, [isOpen]);\n\n /* Merge refs for panel: containerRef (click-outside) + panelRef (clamping) */\n const panelRefCallback = useCallback((node: HTMLDivElement | null) => {\n (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n (panelRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }, []);\n\n return (\n <div\n ref={ref}\n className={clsx('ds-navbar-v2__tab-menu', className)}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n <button\n ref={triggerRef}\n className={clsx(\n 'ds-navbar-v2__tab',\n (active || isOpen) && 'ds-navbar-v2__tab--active',\n )}\n onClick={handleToggle}\n aria-expanded={isOpen}\n aria-haspopup=\"true\"\n aria-controls={isOpen ? panelId : undefined}\n type=\"button\"\n >\n {icon && <span className=\"ds-navbar-v2__tab-icon\">{icon}</span>}\n <span className=\"ds-navbar-v2__tab-label-row\">\n <span className=\"ds-navbar-v2__tab-label\">{label}</span>\n <span\n className=\"ds-navbar-v2__tab-chevron\"\n data-state={isOpen ? 'open' : 'closed'}\n >\n <ChevronDown />\n </span>\n </span>\n </button>\n\n {/* Mega panel — mount only when open to keep the global DOM budget low. */}\n {isOpen ? (\n <div\n ref={panelRefCallback}\n id={panelId}\n className=\"ds-navbar-v2__mega-panel\"\n data-state=\"open\"\n role=\"region\"\n aria-label={`${label} menu`}\n >\n {children}\n </div>\n ) : null}\n </div>\n );\n },\n);\n\nNavbarV2TabMenu.displayName = 'NavbarV2TabMenu';\n\n/* ============================================================================\n NavbarV2Actions — Right-side actions (right of pill)\n ============================================================================ */\n\nexport const NavbarV2Actions = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, children, ...props }, ref) => (\n <div ref={ref} className={clsx('ds-navbar-v2__actions', className)} {...props}>\n {children}\n </div>\n ),\n);\n\nNavbarV2Actions.displayName = 'NavbarV2Actions';\n\n/* ============================================================================\n Utility — merge multiple refs (forwardRef + internal ref)\n ============================================================================ */\n\nfunction useMergedRef<T>(\n ...refs: (React.Ref<T> | React.MutableRefObject<T | null> | null | undefined)[]\n): React.RefCallback<T> {\n return useCallback(\n (value: T | null) => {\n for (const ref of refs) {\n if (typeof ref === 'function') {\n ref(value);\n } else if (ref && typeof ref === 'object') {\n (ref as React.MutableRefObject<T | null>).current = value;\n }\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n refs,\n );\n}\n","import React, { forwardRef, HTMLAttributes, useState } from 'react';\nimport clsx from 'clsx';\nimport './SiteFooter.css';\nimport './SiteFooter.css';\n\n/* ============================================================================\n Types\n ============================================================================ */\n\nexport interface FooterLink {\n label: string;\n href: string;\n /** Visual \"NEW\" indicator badge. */\n isNew?: boolean;\n}\n\nexport interface FooterSection {\n title: string;\n links: FooterLink[];\n}\n\nexport interface FooterCTA {\n /** Primary headline (e.g., \"Install Your Sales Operating System.\") */\n headline: string;\n /** Supporting subtext below the headline. */\n subtext?: string;\n /** Primary CTA slot (e.g., a <Button> component). */\n primaryCta?: React.ReactNode;\n /** Secondary CTA slot (e.g., a ghost <Button>). */\n secondaryCta?: React.ReactNode;\n /** Micro trust line below the CTA actions. */\n trustLine?: string;\n}\n\nexport interface FooterTrustBadge {\n /** Badge label (e.g., \"GDPR-ready\"). */\n label: string;\n}\n\nexport interface SiteFooterProps extends HTMLAttributes<HTMLElement> {\n /* ── Zone 1: Strategic CTA ─────────────────────────────────────────── */\n\n /** Strategic CTA rendered at the top of the footer. */\n cta?: FooterCTA;\n\n /* ── Zone 2: Structured Navigation Grid ────────────────────────────── */\n\n /** Navigation sections rendered as a 5-column grid on desktop. */\n sections?: FooterSection[];\n /** Quick orientation line above the navigation grid. */\n orientationLine?: React.ReactNode;\n /**\n * Custom element to render for each link (e.g., Next.js locale-aware Link).\n * Must accept `href`, `className`, and `children` props.\n * @default 'a'\n */\n linkAs?: React.ElementType;\n\n /* ── Zone 3: Authority Layer ───────────────────────────────────────── */\n\n /** Brand logo or name slot. */\n brand?: React.ReactNode;\n /** Tagline below the brand. */\n tagline?: React.ReactNode;\n /** Social icons slot. */\n socials?: React.ReactNode;\n /** Trust amplifier badges (e.g., \"Built for B2B Teams\", \"GDPR-ready\"). */\n trustBadges?: FooterTrustBadge[];\n\n /* ── Zone 4: System Controls ───────────────────────────────────────── */\n\n /** System controls slot (language switcher, theme toggle, etc.). */\n controls?: React.ReactNode;\n\n /* ── Zone 5: Legal + Ownership ─────────────────────────────────────── */\n\n /** Legal links rendered in the bottom bar. */\n legalLinks?: FooterLink[];\n /** Copyright text. Falls back to a default with the current year. */\n copyright?: React.ReactNode;\n /** Company registration or legal entity info line. */\n companyInfo?: string;\n /** Extra content rendered below the legal bar (e.g., AI summary links). */\n bottomExtra?: React.ReactNode;\n\n /* ── Deprecated ────────────────────────────────────────────────────── */\n\n /** @deprecated Use `controls` instead. Slot rendered in the bottom bar. */\n bottomActions?: React.ReactNode;\n}\n\n/* ============================================================================\n Internal: Mobile Accordion Item\n ============================================================================ */\n\ninterface FooterAccordionItemProps {\n section: FooterSection;\n LinkComponent: React.ElementType;\n}\n\nfunction FooterAccordionItem({ section, LinkComponent }: FooterAccordionItemProps) {\n const [isOpen, setIsOpen] = useState(false);\n\n return (\n <div\n className={clsx(\n 'ds-footer__accordion-item',\n isOpen && 'ds-footer__accordion-item--open',\n )}\n >\n <button\n type=\"button\"\n className=\"ds-footer__accordion-trigger\"\n aria-expanded={isOpen}\n onClick={() => setIsOpen((prev) => !prev)}\n >\n <span>{section.title}</span>\n <svg\n className=\"ds-footer__accordion-chevron\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n </button>\n <div className=\"ds-footer__accordion-panel\" hidden={!isOpen}>\n <ul className=\"ds-footer__links\">\n {section.links.map((link, idx) => (\n <li key={idx}>\n <LinkComponent href={link.href} className=\"ds-footer__link\">\n {link.label}\n {link.isNew && <span className=\"ds-footer__badge\">NEW</span>}\n </LinkComponent>\n </li>\n ))}\n </ul>\n </div>\n </div>\n );\n}\n\n/* ============================================================================\n Component\n ============================================================================ */\n\nexport const SiteFooter = forwardRef<HTMLElement, SiteFooterProps>(\n (\n {\n cta,\n sections = [],\n orientationLine,\n linkAs,\n brand,\n tagline,\n socials,\n trustBadges,\n controls,\n legalLinks = [],\n copyright,\n companyInfo,\n bottomExtra,\n bottomActions,\n className,\n ...props\n },\n ref,\n ) => {\n const currentYear = new Date().getFullYear();\n const defaultCopyright = copyright || `© ${currentYear}. All rights reserved.`;\n const resolvedControls = controls || bottomActions;\n const LinkComponent = linkAs || 'a';\n\n return (\n <footer ref={ref} className={clsx('ds-footer', className)} {...props}>\n {/* ── Zone 1: Strategic CTA ──────────────────────────────────── */}\n {cta && (\n <div className=\"ds-footer__cta\" data-footer-zone=\"cta\">\n <div style={{maxWidth: \"var(--container-max-width, 1200px)\", marginInline: \"auto\", paddingInline: \"var(--space-4)\", width: \"100%\"}}>\n <div className=\"ds-footer__cta-content\">\n <h2 className=\"ds-footer__cta-headline\">{cta.headline}</h2>\n {cta.subtext && (\n <p className=\"ds-footer__cta-subtext\">{cta.subtext}</p>\n )}\n {(cta.primaryCta || cta.secondaryCta) && (\n <div className=\"ds-footer__cta-actions\">\n {cta.primaryCta}\n {cta.secondaryCta}\n </div>\n )}\n {cta.trustLine && (\n <p className=\"ds-footer__cta-trust\">{cta.trustLine}</p>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* ── Zone 2: Navigation Grid ────────────────────────────────── */}\n {sections.length > 0 && (\n <div className=\"ds-footer__nav\" data-footer-zone=\"navigation\">\n <div style={{maxWidth: \"var(--container-max-width, 1200px)\", marginInline: \"auto\", paddingInline: \"var(--space-4)\", width: \"100%\"}}>\n {orientationLine && (\n <div className=\"ds-footer__orientation\">\n {orientationLine}\n </div>\n )}\n\n {/* Desktop: Grid layout */}\n <nav\n className=\"ds-footer__grid\"\n aria-label=\"Footer navigation\"\n >\n {sections.map((section, idx) => (\n <div\n key={idx}\n className=\"ds-footer__column\"\n data-footer-section={section.title\n .toLowerCase()\n .replace(/\\s+/g, '-')}\n >\n <p className=\"ds-footer__heading\">{section.title}</p>\n <ul className=\"ds-footer__links\">\n {section.links.map((link, linkIdx) => (\n <li key={linkIdx}>\n <LinkComponent\n href={link.href}\n className=\"ds-footer__link\"\n >\n {link.label}\n {link.isNew && (\n <span className=\"ds-footer__badge\">NEW</span>\n )}\n </LinkComponent>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </nav>\n\n {/* Mobile: Accordion layout */}\n <nav\n className=\"ds-footer__mobile-nav\"\n aria-label=\"Footer navigation (mobile)\"\n >\n {sections.map((section, idx) => (\n <FooterAccordionItem\n key={idx}\n section={section}\n LinkComponent={LinkComponent}\n />\n ))}\n </nav>\n </div>\n </div>\n )}\n\n {/* ── Zone 3: Authority Layer ────────────────────────────────── */}\n {(brand || socials || trustBadges) && (\n <div className=\"ds-footer__authority\" data-footer-zone=\"authority\">\n <div style={{maxWidth: \"var(--container-max-width, 1200px)\", marginInline: \"auto\", paddingInline: \"var(--space-4)\", width: \"100%\"}}>\n <div className=\"ds-footer__authority-content\">\n <div className=\"ds-footer__brand\">\n {brand && <div className=\"ds-footer__logo\">{brand}</div>}\n {tagline && (\n <p className=\"ds-footer__tagline\">{tagline}</p>\n )}\n {socials && (\n <div className=\"ds-footer__socials\">{socials}</div>\n )}\n </div>\n {trustBadges && trustBadges.length > 0 && (\n <div className=\"ds-footer__trust-badges\">\n {trustBadges.map((badge, idx) => (\n <span key={idx} className=\"ds-footer__trust-badge\">\n {badge.label}\n </span>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* ── Zone 4 + 5: Controls + Legal ───────────────────────────── */}\n <div className=\"ds-footer__bottom\" data-footer-zone=\"legal\">\n <div style={{maxWidth: \"var(--container-max-width, 1200px)\", marginInline: \"auto\", paddingInline: \"var(--space-4)\", width: \"100%\"}}>\n <div className=\"ds-footer__bottom-content\">\n <div className=\"ds-footer__copyright\">\n {copyright || defaultCopyright}\n </div>\n {resolvedControls && (\n <div className=\"ds-footer__controls\">\n {resolvedControls}\n </div>\n )}\n {legalLinks.length > 0 && (\n <div className=\"ds-footer__legal\">\n {legalLinks.map((link, idx) => (\n <LinkComponent\n key={idx}\n href={link.href}\n className=\"ds-footer__legal-link\"\n >\n {link.label}\n </LinkComponent>\n ))}\n </div>\n )}\n </div>\n {bottomExtra && (\n <div className=\"ds-footer__bottom-extra\">{bottomExtra}</div>\n )}\n {companyInfo && (\n <p className=\"ds-footer__company-info\">{companyInfo}</p>\n )}\n </div>\n </div>\n </footer>\n );\n },\n);\nSiteFooter.displayName = 'SiteFooter';\n","import React, { forwardRef, useState, useRef, useEffect, useCallback, useMemo, useId } from 'react';\nimport clsx from 'clsx';\nimport './LocaleDropdown.css';\n\n/* ============================================================================\n Icons\n ============================================================================ */\n\nconst GlobeIcon = () => (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M2 12h20\" />\n <path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\" />\n </svg>\n);\n\nconst ChevronDownIcon = () => (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M4 6L8 10L12 6\" />\n </svg>\n);\n\nconst CheckIcon = () => (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n);\n\nconst SearchIcon = () => (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n);\n\n/* ============================================================================\n Types\n ============================================================================ */\n\nexport interface LocaleOption {\n /** ISO code, e.g. 'en', 'fr', 'es'. */\n code: string;\n /** Full display name, e.g. 'English', 'Français'. */\n label: string;\n /** Emoji flag or SVG component. Optional. */\n flag?: React.ReactNode;\n /** Short display code, e.g. 'EN', 'FR'. Used in the trigger. */\n shortLabel?: string;\n}\n\nexport interface LocaleDropdownProps {\n /** Available locales. */\n locales: LocaleOption[];\n /** Currently active locale code. */\n activeLocale: string;\n /** Called when user selects a locale. */\n onLocaleChange?: (code: string) => void;\n /**\n * Custom element for dropdown options (e.g. Next.js Link).\n * Receives `href` from `getHref`. @default 'button'\n */\n optionAs?: React.ElementType;\n /** Generate href for each locale option. Used with `optionAs`. */\n getHref?: (code: string) => string;\n /** Additional class name. */\n className?: string;\n /** Direction the dropdown should open. Defaults to 'down'. */\n direction?: 'up' | 'down';\n /** Alignment of the dropdown panel relative to the trigger. Defaults to 'right'. */\n align?: 'left' | 'right' | 'center';\n /**\n * Show a search field to filter the list — useful for long locale lists.\n * @default false\n */\n searchable?: boolean;\n /** Placeholder for the search field. @default 'Search languages' */\n searchPlaceholder?: string;\n /** Message shown when no locale matches the query. @default 'No languages found' */\n searchEmptyLabel?: string;\n}\n\n/* ============================================================================\n LocaleDropdown — Globe + flag trigger with dropdown panel\n ============================================================================\n A compact locale selector showing the current locale with a globe icon\n and flag. Clicking opens a dropdown with all available locales, optionally\n filtered by a search field.\n\n Features:\n - Click outside / Escape to close\n - Keyboard navigation (ArrowUp/Down, Enter, Escape)\n - Optional search field (searchable) with combobox semantics\n - Active locale highlighted with checkmark\n - Animated chevron rotation via CSS\n - Supports polymorphic option elements (buttons or links)\n - Full ARIA: aria-haspopup, aria-expanded, aria-activedescendant\n - prefers-reduced-motion respected via CSS\n - Uses DS tokens only (no hardcoded colors)\n ============================================================================ */\n\nexport const LocaleDropdown = forwardRef<HTMLDivElement, LocaleDropdownProps>(\n (\n {\n locales,\n activeLocale,\n onLocaleChange,\n optionAs: OptionComponent = 'button',\n getHref,\n className,\n direction = 'down',\n align = 'right',\n searchable = false,\n searchPlaceholder = 'Search languages',\n searchEmptyLabel = 'No languages found',\n },\n ref,\n ) => {\n const [isOpen, setIsOpen] = useState(false);\n const [focusIndex, setFocusIndex] = useState(-1);\n const [query, setQuery] = useState('');\n const containerRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const instanceId = useId();\n const listboxId = `locale-dropdown-${instanceId}`;\n\n const currentLocale = locales.find((l) => l.code === activeLocale) ?? locales[0];\n\n /* Filtered list — identical to `locales` unless searching. */\n const filteredLocales = useMemo(() => {\n if (!searchable) return locales;\n const q = query.trim().toLowerCase();\n if (!q) return locales;\n return locales.filter(\n (l) => l.label.toLowerCase().includes(q) || l.code.toLowerCase().includes(q),\n );\n }, [locales, query, searchable]);\n\n const handleToggle = useCallback(() => {\n setIsOpen((prev) => {\n if (!prev) setFocusIndex(-1);\n return !prev;\n });\n }, []);\n\n const handleClose = useCallback(() => {\n setIsOpen(false);\n setFocusIndex(-1);\n setQuery('');\n triggerRef.current?.focus();\n }, []);\n\n const handleSelect = useCallback(\n (code: string) => {\n onLocaleChange?.(code);\n handleClose();\n },\n [onLocaleChange, handleClose],\n );\n\n /* Click outside → close */\n useEffect(() => {\n if (!isOpen) return;\n const handleClickOutside = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n handleClose();\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [isOpen, handleClose]);\n\n /* Focus the search field when opening a searchable dropdown */\n useEffect(() => {\n if (!isOpen || !searchable) return;\n const raf = requestAnimationFrame(() => searchInputRef.current?.focus());\n return () => cancelAnimationFrame(raf);\n }, [isOpen, searchable]);\n\n /* Reset the highlighted option whenever the query changes */\n useEffect(() => {\n setFocusIndex(-1);\n }, [query]);\n\n /* Keyboard navigation */\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!isOpen) {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n setIsOpen(true);\n setFocusIndex(0);\n }\n return;\n }\n\n const count = filteredLocales.length;\n\n switch (e.key) {\n case 'Escape':\n e.preventDefault();\n handleClose();\n break;\n case 'ArrowDown':\n e.preventDefault();\n if (count > 0) setFocusIndex((prev) => (prev + 1) % count);\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (count > 0) setFocusIndex((prev) => (prev - 1 + count) % count);\n break;\n case 'Enter':\n e.preventDefault();\n if (focusIndex >= 0 && focusIndex < count) {\n handleSelect(filteredLocales[focusIndex].code);\n }\n break;\n case ' ':\n /* Space selects only when not typing in the search field. */\n if (!searchable) {\n e.preventDefault();\n if (focusIndex >= 0 && focusIndex < count) {\n handleSelect(filteredLocales[focusIndex].code);\n }\n }\n break;\n case 'Home':\n /* Leave Home/End to the caret when the search field is present. */\n if (!searchable) {\n e.preventDefault();\n if (count > 0) setFocusIndex(0);\n }\n break;\n case 'End':\n if (!searchable) {\n e.preventDefault();\n if (count > 0) setFocusIndex(count - 1);\n }\n break;\n case 'Tab':\n handleClose();\n break;\n }\n },\n [isOpen, focusIndex, filteredLocales, searchable, handleClose, handleSelect],\n );\n\n /* Scroll focused item into view */\n useEffect(() => {\n if (!isOpen || focusIndex < 0 || !listRef.current) return;\n const items = listRef.current.querySelectorAll('[role=\"option\"]');\n items[focusIndex]?.scrollIntoView({ block: 'nearest' });\n }, [isOpen, focusIndex]);\n\n const activeDescendantId =\n isOpen && focusIndex >= 0 ? `${listboxId}-option-${focusIndex}` : undefined;\n\n const renderOption = (locale: LocaleOption, index: number) => {\n const isActive = locale.code === activeLocale;\n const isFocused = index === focusIndex;\n const optionId = `${listboxId}-option-${index}`;\n\n const optionProps = {\n id: optionId,\n role: 'option' as const,\n 'aria-selected': isActive,\n className: clsx(\n 'ds-locale-dropdown__option',\n isActive && 'ds-locale-dropdown__option--active',\n isFocused && 'ds-locale-dropdown__option--focused',\n ),\n onClick: () => handleSelect(locale.code),\n ...(OptionComponent !== 'button' && getHref\n ? { href: getHref(locale.code) }\n : { type: 'button' as const }),\n };\n\n return (\n <OptionComponent key={locale.code} {...optionProps}>\n <span className=\"ds-locale-dropdown__option-content\">\n {locale.flag && (\n <span className=\"ds-locale-dropdown__option-flag\" aria-hidden=\"true\">\n {locale.flag}\n </span>\n )}\n <span className=\"ds-locale-dropdown__option-label\">{locale.label}</span>\n </span>\n {isActive && (\n <span className=\"ds-locale-dropdown__option-check\">\n <CheckIcon />\n </span>\n )}\n </OptionComponent>\n );\n };\n\n return (\n <div\n ref={ref}\n className={clsx(\n 'ds-locale-dropdown',\n direction === 'up' && 'ds-locale-dropdown--up',\n align === 'left' && 'ds-locale-dropdown--align-left',\n align === 'center' && 'ds-locale-dropdown--align-center',\n className,\n )}\n >\n <div ref={containerRef} className=\"ds-locale-dropdown__wrapper\">\n {/* Trigger */}\n <button\n ref={triggerRef}\n type=\"button\"\n className=\"ds-locale-dropdown__trigger\"\n onClick={handleToggle}\n onKeyDown={handleKeyDown}\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? listboxId : undefined}\n aria-activedescendant={searchable ? undefined : activeDescendantId}\n aria-label={`Language: ${currentLocale.label}. Change language.`}\n >\n <span className=\"ds-locale-dropdown__trigger-icon\">\n <GlobeIcon />\n </span>\n {currentLocale.flag && (\n <span className=\"ds-locale-dropdown__trigger-flag\" aria-hidden=\"true\">\n {currentLocale.flag}\n </span>\n )}\n <span\n className=\"ds-locale-dropdown__trigger-chevron\"\n data-state={isOpen ? 'open' : 'closed'}\n >\n <ChevronDownIcon />\n </span>\n </button>\n\n {/* Dropdown Panel — always mounted, CSS transitions handle show/hide */}\n {searchable ? (\n <div\n className=\"ds-locale-dropdown__panel ds-locale-dropdown__panel--searchable\"\n data-state={isOpen ? 'open' : 'closed'}\n >\n <div className=\"ds-locale-dropdown__search\">\n <span className=\"ds-locale-dropdown__search-icon\" aria-hidden=\"true\">\n <SearchIcon />\n </span>\n <input\n ref={searchInputRef}\n type=\"text\"\n className=\"ds-locale-dropdown__search-input\"\n placeholder={searchPlaceholder}\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={handleKeyDown}\n role=\"combobox\"\n aria-expanded={isOpen}\n aria-controls={listboxId}\n aria-activedescendant={activeDescendantId}\n aria-autocomplete=\"list\"\n aria-label={searchPlaceholder}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </div>\n <div\n ref={listRef}\n id={listboxId}\n className=\"ds-locale-dropdown__list\"\n role=\"listbox\"\n aria-label=\"Select language\"\n >\n {filteredLocales.length === 0 ? (\n <div className=\"ds-locale-dropdown__empty\" role=\"status\">\n {searchEmptyLabel}\n </div>\n ) : (\n filteredLocales.map(renderOption)\n )}\n </div>\n </div>\n ) : (\n <div\n ref={listRef}\n id={listboxId}\n className=\"ds-locale-dropdown__panel\"\n role=\"listbox\"\n aria-label=\"Select language\"\n data-state={isOpen ? 'open' : 'closed'}\n onKeyDown={handleKeyDown}\n >\n {filteredLocales.map(renderOption)}\n </div>\n )}\n </div>\n </div>\n );\n },\n);\n\nLocaleDropdown.displayName = 'LocaleDropdown';\n","import React from 'react';\nimport { OutboundLink } from './OutboundLink';\n\n/**\n * A wrapper specifically designed to be passed into MDX component mappings.\n * It provides defaults suitable for markdown content.\n * \n * Usage:\n * <MDXRemote components={{ a: MDXOutboundLink }} />\n */\nexport const MDXOutboundLink = ({ \n href, \n children, \n ...props \n}: React.AnchorHTMLAttributes<HTMLAnchorElement> & { href?: string }) => {\n if (!href) {\n // eslint-disable-next-line no-restricted-syntax\n return <a {...props}>{children}</a>;\n }\n\n return (\n <OutboundLink \n href={href} \n context=\"cms_markdown\" \n preserveExistingUTM={true}\n openInNewTab={true}\n {...props}\n >\n {children}\n </OutboundLink>\n );\n};\n\nexport default MDXOutboundLink;"]}
package/dist/index.cjs CHANGED
@@ -6,7 +6,7 @@ var chunkFVSL5YMB_cjs = require('./chunk-FVSL5YMB.cjs');
6
6
  var chunkLUD52ZJF_cjs = require('./chunk-LUD52ZJF.cjs');
7
7
  var chunkEPD4ZEPY_cjs = require('./chunk-EPD4ZEPY.cjs');
8
8
  var chunkCVLD5RQK_cjs = require('./chunk-CVLD5RQK.cjs');
9
- var chunk3NS6X2R4_cjs = require('./chunk-3NS6X2R4.cjs');
9
+ var chunk5EIBL6DQ_cjs = require('./chunk-5EIBL6DQ.cjs');
10
10
  var chunkNT4LBP7D_cjs = require('./chunk-NT4LBP7D.cjs');
11
11
  var chunkFXYOSA4E_cjs = require('./chunk-FXYOSA4E.cjs');
12
12
  var chunkSR4QRXRR_cjs = require('./chunk-SR4QRXRR.cjs');
@@ -655,39 +655,39 @@ Object.defineProperty(exports, "StatsSection", {
655
655
  });
656
656
  Object.defineProperty(exports, "LocaleDropdown", {
657
657
  enumerable: true,
658
- get: function () { return chunk3NS6X2R4_cjs.LocaleDropdown; }
658
+ get: function () { return chunk5EIBL6DQ_cjs.LocaleDropdown; }
659
659
  });
660
660
  Object.defineProperty(exports, "MDXOutboundLink", {
661
661
  enumerable: true,
662
- get: function () { return chunk3NS6X2R4_cjs.MDXOutboundLink; }
662
+ get: function () { return chunk5EIBL6DQ_cjs.MDXOutboundLink; }
663
663
  });
664
664
  Object.defineProperty(exports, "NavbarV2", {
665
665
  enumerable: true,
666
- get: function () { return chunk3NS6X2R4_cjs.NavbarV2; }
666
+ get: function () { return chunk5EIBL6DQ_cjs.NavbarV2; }
667
667
  });
668
668
  Object.defineProperty(exports, "NavbarV2Actions", {
669
669
  enumerable: true,
670
- get: function () { return chunk3NS6X2R4_cjs.NavbarV2Actions; }
670
+ get: function () { return chunk5EIBL6DQ_cjs.NavbarV2Actions; }
671
671
  });
672
672
  Object.defineProperty(exports, "NavbarV2Brand", {
673
673
  enumerable: true,
674
- get: function () { return chunk3NS6X2R4_cjs.NavbarV2Brand; }
674
+ get: function () { return chunk5EIBL6DQ_cjs.NavbarV2Brand; }
675
675
  });
676
676
  Object.defineProperty(exports, "NavbarV2Tab", {
677
677
  enumerable: true,
678
- get: function () { return chunk3NS6X2R4_cjs.NavbarV2Tab; }
678
+ get: function () { return chunk5EIBL6DQ_cjs.NavbarV2Tab; }
679
679
  });
680
680
  Object.defineProperty(exports, "NavbarV2TabMenu", {
681
681
  enumerable: true,
682
- get: function () { return chunk3NS6X2R4_cjs.NavbarV2TabMenu; }
682
+ get: function () { return chunk5EIBL6DQ_cjs.NavbarV2TabMenu; }
683
683
  });
684
684
  Object.defineProperty(exports, "NavbarV2Tabs", {
685
685
  enumerable: true,
686
- get: function () { return chunk3NS6X2R4_cjs.NavbarV2Tabs; }
686
+ get: function () { return chunk5EIBL6DQ_cjs.NavbarV2Tabs; }
687
687
  });
688
688
  Object.defineProperty(exports, "SiteFooter", {
689
689
  enumerable: true,
690
- get: function () { return chunk3NS6X2R4_cjs.SiteFooter; }
690
+ get: function () { return chunk5EIBL6DQ_cjs.SiteFooter; }
691
691
  });
692
692
  Object.defineProperty(exports, "Breadcrumb", {
693
693
  enumerable: true,
package/dist/index.css CHANGED
@@ -3635,6 +3635,60 @@
3635
3635
  background: var(--fallback-surface);
3636
3636
  }
3637
3637
  }
3638
+ .ds-locale-dropdown__panel--searchable {
3639
+ display: flex;
3640
+ flex-direction: column;
3641
+ min-width: 220px;
3642
+ }
3643
+ .ds-locale-dropdown__search {
3644
+ display: flex;
3645
+ align-items: center;
3646
+ gap: var(--space-2);
3647
+ padding: var(--space-1-5) var(--space-2-5);
3648
+ margin-bottom: var(--space-1);
3649
+ border-radius: var(--radius-button);
3650
+ background: var(--glass-base-hover);
3651
+ }
3652
+ .ds-locale-dropdown__search:focus-within {
3653
+ box-shadow: var(--focus-ring);
3654
+ }
3655
+ .ds-locale-dropdown__search-icon {
3656
+ display: flex;
3657
+ align-items: center;
3658
+ color: var(--text-tertiary);
3659
+ flex-shrink: 0;
3660
+ }
3661
+ .ds-locale-dropdown__search-input {
3662
+ flex: 1;
3663
+ min-width: 0;
3664
+ padding: 0;
3665
+ border: none;
3666
+ background: transparent;
3667
+ color: var(--text-primary);
3668
+ font-family: var(--font-family);
3669
+ font-size: var(--font-size-sm);
3670
+ line-height: 1;
3671
+ }
3672
+ .ds-locale-dropdown__search-input::placeholder {
3673
+ color: var(--text-tertiary);
3674
+ }
3675
+ .ds-locale-dropdown__search-input:focus,
3676
+ .ds-locale-dropdown__search-input:focus-visible {
3677
+ outline: none;
3678
+ }
3679
+ .ds-locale-dropdown__list {
3680
+ display: flex;
3681
+ flex-direction: column;
3682
+ max-height: 280px;
3683
+ overflow-y: auto;
3684
+ }
3685
+ .ds-locale-dropdown__empty {
3686
+ padding: var(--space-3);
3687
+ color: var(--text-tertiary);
3688
+ font-family: var(--font-family);
3689
+ font-size: var(--font-size-sm);
3690
+ text-align: center;
3691
+ }
3638
3692
 
3639
3693
  /* src/components/Shell/Shell.css */
3640
3694
  .ds-shell {