{"version":1,"id":"apple-style-header","framework":"next","kind":"block","files":[{"path":"src/blocks/gfa/apple-style-header.tsx","content":"\"use client\";\n\nimport {\n  useCallback,\n  useEffect,\n  useId,\n  useRef,\n  useState,\n  type KeyboardEvent,\n} from \"react\";\nimport {\n  APPLE_STYLE_HEADER_NAV_ITEMS,\n  APPLE_STYLE_HEADER_SEARCH_QUICK_LINKS,\n} from \"@/blocks/gfa/apple-style-header-data\";\nimport \"./apple-style-header.css\";\n\nconst SEARCH_ID = \"search\";\n\nfunction SearchIcon() {\n  return (\n    <svg\n      className=\"agn__icon\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"15\"\n      height=\"44\"\n      viewBox=\"0 0 15 44\"\n      aria-hidden=\"true\"\n    >\n      <path d=\"M14.298,27.202l-3.87-3.87c0.701-0.929,1.122-2.081,1.122-3.332c0-3.06-2.489-5.55-5.55-5.55c-3.06,0-5.55,2.49-5.55,5.55 c0,3.061,2.49,5.55,5.55,5.55c1.251,0,2.403-0.421,3.332-1.122l3.87,3.87c0.151,0.151,0.35,0.228,0.548,0.228 s0.396-0.076,0.548-0.228C14.601,27.995,14.601,27.505,14.298,27.202z M1.55,20c0-2.454,1.997-4.45,4.45-4.45 c2.454,0,4.45,1.997,4.45,4.45S8.454,24.45,6,24.45C3.546,24.45,1.55,22.454,1.55,20z\" />\n    </svg>\n  );\n}\n\nfunction BagIcon() {\n  return (\n    <svg\n      className=\"agn__icon\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"14\"\n      height=\"44\"\n      viewBox=\"0 0 14 44\"\n      aria-hidden=\"true\"\n    >\n      <path d=\"m11.3535 16.0283h-1.0205a3.4229 3.4229 0 0 0 -3.333-2.9648 3.4229 3.4229 0 0 0 -3.333 2.9648h-1.02a2.1184 2.1184 0 0 0 -2.117 2.1162v7.7155a2.1186 2.1186 0 0 0 2.1162 2.1167h8.707a2.1186 2.1186 0 0 0 2.1168-2.1167v-7.7155a2.1184 2.1184 0 0 0 -2.1165-2.1162zm-4.3535-1.8652a2.3169 2.3169 0 0 1 2.2222 1.8652h-4.4444a2.3169 2.3169 0 0 1 2.2222-1.8652zm5.37 11.6969a1.0182 1.0182 0 0 1 -1.0166 1.0171h-8.7069a1.0182 1.0182 0 0 1 -1.0165-1.0171v-7.7155a1.0178 1.0178 0 0 1 1.0166-1.0166h8.707a1.0178 1.0178 0 0 1 1.0164 1.0166z\" />\n    </svg>\n  );\n}\n\n/** Apple-style global header + mega menu (Acme demo brand). */\nexport function AppleStyleHeader() {\n  const rootId = useId();\n  const measureRef = useRef<HTMLDivElement>(null);\n  const searchInputRef = useRef<HTMLInputElement>(null);\n  const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const openIdRef = useRef<string | null>(null);\n  const [openId, setOpenId] = useState<string | null>(null);\n  const [panelItemId, setPanelItemId] = useState<string | null>(null);\n  const [flyoutH, setFlyoutH] = useState(0);\n  const [query, setQuery] = useState(\"\");\n\n  openIdRef.current = openId;\n\n  const isSearch = (openId ?? panelItemId) === SEARCH_ID;\n  const active = isSearch\n    ? null\n    : (APPLE_STYLE_HEADER_NAV_ITEMS.find(\n        (item) => item.id === (openId ?? panelItemId),\n      ) ?? null);\n\n  const clearCloseTimer = useCallback(() => {\n    if (closeTimer.current) {\n      clearTimeout(closeTimer.current);\n      closeTimer.current = null;\n    }\n  }, []);\n\n  const open = useCallback(\n    (id: string) => {\n      clearCloseTimer();\n      setOpenId(id);\n      setPanelItemId(id);\n      if (id !== SEARCH_ID) setQuery(\"\");\n    },\n    [clearCloseTimer],\n  );\n\n  const scheduleClose = useCallback(() => {\n    clearCloseTimer();\n    closeTimer.current = setTimeout(() => {\n      if (openIdRef.current === SEARCH_ID) return;\n      setOpenId(null);\n      setFlyoutH(0);\n      window.setTimeout(() => setPanelItemId(null), 320);\n    }, 120);\n  }, [clearCloseTimer]);\n\n  const close = useCallback(() => {\n    clearCloseTimer();\n    setOpenId(null);\n    setFlyoutH(0);\n    setQuery(\"\");\n    window.setTimeout(() => setPanelItemId(null), 320);\n  }, [clearCloseTimer]);\n\n  const toggleSearch = useCallback(() => {\n    if (openId === SEARCH_ID) close();\n    else open(SEARCH_ID);\n  }, [openId, open, close]);\n\n  useEffect(() => {\n    if (!openId || !measureRef.current) return;\n    setFlyoutH(measureRef.current.scrollHeight);\n  }, [openId, panelItemId, isSearch]);\n\n  useEffect(() => {\n    if (openId !== SEARCH_ID) return;\n    const id = window.requestAnimationFrame(() => {\n      searchInputRef.current?.focus();\n    });\n    return () => window.cancelAnimationFrame(id);\n  }, [openId]);\n\n  useEffect(() => {\n    if (!openId) return;\n    const onKey = (e: globalThis.KeyboardEvent) => {\n      if (e.key === \"Escape\") close();\n    };\n    window.addEventListener(\"keydown\", onKey);\n    return () => window.removeEventListener(\"keydown\", onKey);\n  }, [openId, close]);\n\n  useEffect(() => () => clearCloseTimer(), [clearCloseTimer]);\n\n  const onItemKeyDown = (e: KeyboardEvent<HTMLButtonElement>, id: string) => {\n    if (e.key === \"Enter\" || e.key === \" \") {\n      e.preventDefault();\n      if (openId === id) close();\n      else open(id);\n    }\n    if (e.key === \"Escape\") close();\n  };\n\n  const searchControlsId = `${rootId}-search-panel`;\n  const searchOpen = openId === SEARCH_ID;\n\n  return (\n    <div className=\"agn-shell\" data-gfa-apple-style-header=\"\">\n      <nav\n        className=\"agn\"\n        data-open={openId ? \"true\" : \"false\"}\n        data-mode={isSearch ? \"search\" : \"menu\"}\n        style={{ [\"--agn-flyout-h\" as string]: `${flyoutH}px` }}\n        aria-label=\"Global\"\n        onMouseLeave={scheduleClose}\n        onMouseEnter={clearCloseTimer}\n      >\n        <div className=\"agn__bar\">\n          <div className=\"agn__content\">\n            <ul className=\"agn__list\" role=\"list\">\n              <li className=\"agn__item\">\n                <a className=\"agn__link agn__link--brand\" href=\"#acme\" aria-label=\"Acme\">\n                  Acme\n                </a>\n              </li>\n\n              {APPLE_STYLE_HEADER_NAV_ITEMS.map((item) => {\n                const controlsId = `${rootId}-${item.id}-panel`;\n                const isActive = openId === item.id;\n                return (\n                  <li\n                    key={item.id}\n                    className=\"agn__item\"\n                    data-active={isActive ? \"true\" : \"false\"}\n                    onMouseEnter={() => open(item.id)}\n                  >\n                    <button\n                      type=\"button\"\n                      className=\"agn__link\"\n                      aria-expanded={isActive}\n                      aria-controls={controlsId}\n                      onClick={() => open(item.id)}\n                      onFocus={() => open(item.id)}\n                      onKeyDown={(e) => onItemKeyDown(e, item.id)}\n                    >\n                      {item.label}\n                    </button>\n                  </li>\n                );\n              })}\n\n              <li className=\"agn__item\" data-active={searchOpen ? \"true\" : \"false\"}>\n                <button\n                  type=\"button\"\n                  className=\"agn__link agn__link--icon\"\n                  aria-label=\"Search acme.com\"\n                  aria-expanded={searchOpen}\n                  aria-controls={searchControlsId}\n                  onClick={toggleSearch}\n                >\n                  <SearchIcon />\n                </button>\n              </li>\n              <li className=\"agn__item\">\n                <a className=\"agn__link agn__link--icon\" href=\"#bag\" aria-label=\"Bag\">\n                  <BagIcon />\n                </a>\n              </li>\n            </ul>\n          </div>\n        </div>\n\n        <div\n          className=\"agn__curtain\"\n          aria-hidden=\"true\"\n          onClick={close}\n          onMouseEnter={scheduleClose}\n        />\n\n        <div className=\"agn__flyout\" aria-hidden={!openId}>\n          <div\n            ref={measureRef}\n            className={\n              isSearch ? \"agn__flyout-inner agn__flyout-inner--search\" : \"agn__flyout-inner\"\n            }\n            id={\n              openId\n                ? openId === SEARCH_ID\n                  ? searchControlsId\n                  : `${rootId}-${openId}-panel`\n                : undefined\n            }\n            role=\"region\"\n            aria-label={\n              isSearch ? \"Search\" : active ? `${active.label} menu` : undefined\n            }\n          >\n            {isSearch ? (\n              <div className=\"agn__search\">\n                <div className=\"agn__search-field\">\n                  <svg\n                    className=\"agn__search-glyph\"\n                    xmlns=\"http://www.w3.org/2000/svg\"\n                    width=\"16\"\n                    height=\"16\"\n                    viewBox=\"0 0 16 16\"\n                    aria-hidden=\"true\"\n                  >\n                    <path\n                      fill=\"currentColor\"\n                      d=\"M11.5 10.4l3.1 3.1a.75.75 0 11-1.06 1.06l-3.1-3.1a5.5 5.5 0 111.06-1.06zM6.75 11a4.25 4.25 0 100-8.5 4.25 4.25 0 000 8.5z\"\n                    />\n                  </svg>\n                  <input\n                    ref={searchInputRef}\n                    className=\"agn__search-input\"\n                    type=\"search\"\n                    value={query}\n                    onChange={(e) => setQuery(e.target.value)}\n                    placeholder=\"Search acme.com\"\n                    aria-label=\"Search acme.com\"\n                    autoComplete=\"off\"\n                  />\n                </div>\n                <div className=\"agn__search-quick\">\n                  <h2 className=\"agn__header\">Quick Links</h2>\n                  <ul className=\"agn__links\">\n                    {APPLE_STYLE_HEADER_SEARCH_QUICK_LINKS.map((label) => (\n                      <li key={label}>\n                        <a\n                          href={`#${label.toLowerCase().replace(/\\s+/g, \"-\")}`}\n                          className=\"agn__sublink agn__sublink--compact agn__sublink--search\"\n                        >\n                          {label}\n                        </a>\n                      </li>\n                    ))}\n                  </ul>\n                </div>\n              </div>\n            ) : (\n              active?.groups.map((group) => (\n                <div\n                  key={group.header}\n                  className={\n                    group.elevated\n                      ? \"agn__group agn__group--elevated\"\n                      : \"agn__group agn__group--secondary\"\n                  }\n                >\n                  <h2 className=\"agn__header\">{group.header}</h2>\n                  <ul className=\"agn__links\">\n                    {group.links.map((link) => (\n                      <li key={link.label}>\n                        <a\n                          href={`#${link.label.toLowerCase().replace(/\\s+/g, \"-\")}`}\n                          className={\n                            link.elevated\n                              ? \"agn__sublink agn__sublink--elevated\"\n                              : \"agn__sublink agn__sublink--compact\"\n                          }\n                        >\n                          {link.label}\n                        </a>\n                      </li>\n                    ))}\n                  </ul>\n                </div>\n              ))\n            )}\n          </div>\n        </div>\n      </nav>\n    </div>\n  );\n}\n"}],"meta":{"studioName":"GFA Development & Design Studio","siteUrl":"https://ui.gkhn.dev"}}