{"version":1,"id":"app-store-search","framework":"next","kind":"block","files":[{"path":"src/blocks/gfa/app-store-search.tsx","content":"\"use client\";\n\nimport { useMemo, useRef, useState } from \"react\";\nimport Link from \"next/link\";\nimport {\n  BoxesIcon,\n  FormInputIcon,\n  MessageSquareIcon,\n  SearchIcon,\n  SparklesIcon,\n  WandSparklesIcon,\n  XIcon,\n} from \"lucide-react\";\nimport type { GalleryCategory } from \"@/components/home/gallery-data\";\nimport { cn } from \"@/lib/utils\";\n\nexport type AppStoreItem = {\n  href: string;\n  name: string;\n  subtitle: string;\n  category: Exclude<GalleryCategory, \"all\"> | \"other\";\n};\n\nconst DEMO_ITEMS: AppStoreItem[] = [\n  {\n    href: \"/pricing-card\",\n    name: \"Pricing Card\",\n    subtitle: \"Liquid border · bounce · Pro+\",\n    category: \"motion\",\n  },\n  {\n    href: \"/customer-stories-carousel\",\n    name: \"Customer Stories\",\n    subtitle: \"Drag carousel · progressive blur\",\n    category: \"motion\",\n  },\n  {\n    href: \"/morphing-cursor\",\n    name: \"Morphing Cursor\",\n    subtitle: \"More+ pill on media only\",\n    category: \"motion\",\n  },\n  {\n    href: \"/expanding-feature-card\",\n    name: \"Expanding Feature Card\",\n    subtitle: \"Hover rise panel\",\n    category: \"motion\",\n  },\n  {\n    href: \"/dynamic-island-payment\",\n    name: \"Dynamic Island Payment\",\n    subtitle: \"Pay → Proceed → Success\",\n    category: \"motion\",\n  },\n  {\n    href: \"/bouncy-accordion\",\n    name: \"Bouncy Accordion\",\n    subtitle: \"Weighted spring accordion\",\n    category: \"motion\",\n  },\n  {\n    href: \"/morphing-tabs\",\n    name: \"Morphing Tabs\",\n    subtitle: \"Drag reorder · surface grow\",\n    category: \"motion\",\n  },\n  {\n    href: \"/bounce-sidebar\",\n    name: \"Bounce Sidebar\",\n    subtitle: \"Arc jump between destinations\",\n    category: \"motion\",\n  },\n  {\n    href: \"/pinned-item-list\",\n    name: \"Pinned Item List\",\n    subtitle: \"Pin to top with layout motion\",\n    category: \"motion\",\n  },\n  {\n    href: \"/liquid-metal-avatar\",\n    name: \"Liquid Metal Avatar\",\n    subtitle: \"Paper liquid ring\",\n    category: \"effects\",\n  },\n  {\n    href: \"/liquid-metal-profile-button\",\n    name: \"Liquid Metal Profile\",\n    subtitle: \"Metal border profile pill\",\n    category: \"effects\",\n  },\n  {\n    href: \"/flow-shader-background\",\n    name: \"Flow Shader Background\",\n    subtitle: \"Animated WebGL wash\",\n    category: \"effects\",\n  },\n  {\n    href: \"/credit-card\",\n    name: \"Credit Card\",\n    subtitle: \"Metal surface · PAN scramble\",\n    category: \"effects\",\n  },\n  {\n    href: \"/auth\",\n    name: \"Auth\",\n    subtitle: \"Phone / email · QR login\",\n    category: \"forms\",\n  },\n  {\n    href: \"/composer\",\n    name: \"Composer\",\n    subtitle: \"Tools · files · send\",\n    category: \"forms\",\n  },\n  {\n    href: \"/todo-list\",\n    name: \"Todo List\",\n    subtitle: \"Agent task plan\",\n    category: \"forms\",\n  },\n  {\n    href: \"/chat-conversation\",\n    name: \"Chat Conversation\",\n    subtitle: \"Viewport-enter bubbles\",\n    category: \"chat\",\n  },\n];\n\nconst CAT_META: Record<\n  Exclude<GalleryCategory, \"all\">,\n  { label: string; tint: string; Icon: typeof SparklesIcon }\n> = {\n  motion: {\n    label: \"Motion\",\n    tint: \"from-[#5ac8fa] to-[#007aff]\",\n    Icon: WandSparklesIcon,\n  },\n  effects: {\n    label: \"Effects\",\n    tint: \"from-[#ff9f0a] to-[#ff375f]\",\n    Icon: SparklesIcon,\n  },\n  forms: {\n    label: \"Forms\",\n    tint: \"from-[#30d158] to-[#34c759]\",\n    Icon: FormInputIcon,\n  },\n  chat: {\n    label: \"Chat\",\n    tint: \"from-[#bf5af2] to-[#af52de]\",\n    Icon: MessageSquareIcon,\n  },\n  lab: {\n    label: \"Lab\",\n    tint: \"from-[#8e8e93] to-[#636366]\",\n    Icon: BoxesIcon,\n  },\n};\n\nfunction AppIcon({ category }: { category: AppStoreItem[\"category\"] }) {\n  const meta =\n    category === \"other\"\n      ? {\n          tint: \"from-[#aeaeb2] to-[#8e8e93]\",\n          Icon: BoxesIcon,\n        }\n      : CAT_META[category];\n  const Icon = meta.Icon;\n  return (\n    <span\n      className={cn(\n        \"flex size-[52px] shrink-0 items-center justify-center rounded-[13px]\",\n        \"bg-gradient-to-b shadow-[0_1px_2px_rgba(0,0,0,0.12),inset_0_1px_0_rgba(255,255,255,0.35)]\",\n        meta.tint,\n      )}\n      aria-hidden\n    >\n      <Icon className=\"size-6 text-white drop-shadow-sm\" strokeWidth={1.75} />\n    </span>\n  );\n}\n\nexport type AppStoreSearchProps = {\n  items?: AppStoreItem[];\n  className?: string;\n};\n\nexport function AppStoreSearch({\n  items = DEMO_ITEMS,\n  className,\n}: AppStoreSearchProps) {\n  const inputRef = useRef<HTMLInputElement>(null);\n  const [query, setQuery] = useState(\"\");\n  const [category, setCategory] = useState<GalleryCategory>(\"all\");\n\n  const results = useMemo(() => {\n    const q = query.trim().toLowerCase();\n    return items.filter((item) => {\n      const inCat =\n        category === \"all\" ||\n        (item.category !== \"other\" && item.category === category);\n      if (!inCat) return false;\n      if (!q) return true;\n      return (\n        item.name.toLowerCase().includes(q) ||\n        item.subtitle.toLowerCase().includes(q) ||\n        item.href.toLowerCase().includes(q)\n      );\n    });\n  }, [items, query, category]);\n\n  const suggested = useMemo(() => items.slice(0, 6), [items]);\n  const browsing = !query.trim();\n\n  const cats: GalleryCategory[] = [\n    \"all\",\n    \"motion\",\n    \"effects\",\n    \"forms\",\n    \"chat\",\n  ];\n\n  return (\n    <section\n      data-slot=\"app-store-search\"\n      className={cn(\n        \"mx-auto w-full max-w-[720px]\",\n        \"font-[system-ui,-apple-system,BlinkMacSystemFont,'SF_Pro_Text','SF_Pro_Display',sans-serif]\",\n        className,\n      )}\n    >\n      {/* Mac window chrome */}\n      <div\n        className={cn(\n          \"overflow-hidden rounded-[18px]\",\n          \"bg-[#f5f5f7]/90 shadow-[0_24px_80px_rgba(0,0,0,0.18),0_0_0_0.5px_rgba(0,0,0,0.08)]\",\n          \"backdrop-blur-2xl dark:bg-[#1c1c1e]/92 dark:shadow-[0_24px_80px_rgba(0,0,0,0.55)]\",\n        )}\n      >\n        <div className=\"flex items-center gap-2 border-b border-black/5 px-4 py-3 dark:border-white/10\">\n          <span className=\"flex gap-1.5\" aria-hidden>\n            <span className=\"size-3 rounded-full bg-[#ff5f57]\" />\n            <span className=\"size-3 rounded-full bg-[#febc2e]\" />\n            <span className=\"size-3 rounded-full bg-[#28c840]\" />\n          </span>\n          <p className=\"flex-1 text-center text-[13px] font-medium tracking-tight text-[#6e6e73] dark:text-[#98989d]\">\n            Search\n          </p>\n          <span className=\"w-[42px]\" aria-hidden />\n        </div>\n\n        <div className=\"px-5 pt-5 pb-2\">\n          <label className=\"relative flex items-center\">\n            <SearchIcon\n              className=\"pointer-events-none absolute left-3.5 size-[17px] text-[#8e8e93]\"\n              strokeWidth={2}\n              aria-hidden\n            />\n            <input\n              ref={inputRef}\n              value={query}\n              onChange={(e) => setQuery(e.target.value)}\n              placeholder=\"Games, Apps, Stories, and More\"\n              className={cn(\n                \"h-[38px] w-full rounded-[10px] border-0 bg-[#e8e8ed] pr-10 pl-10\",\n                \"text-[15px] text-[#1d1d1f] outline-none\",\n                \"placeholder:text-[#8e8e93]\",\n                \"dark:bg-[#3a3a3c] dark:text-[#f5f5f7]\",\n                \"focus:ring-2 focus:ring-[#0071e3]/35\",\n              )}\n            />\n            {query ? (\n              <button\n                type=\"button\"\n                onClick={() => {\n                  setQuery(\"\");\n                  inputRef.current?.focus();\n                }}\n                className=\"absolute right-2.5 flex size-5 items-center justify-center rounded-full bg-[#aeaeb2] text-white transition hover:bg-[#8e8e93]\"\n                aria-label=\"Clear\"\n              >\n                <XIcon className=\"size-3\" strokeWidth={3} />\n              </button>\n            ) : null}\n          </label>\n\n          <div className=\"mt-3 flex gap-1.5 overflow-x-auto pb-1\">\n            {cats.map((id) => {\n              const active = category === id;\n              const label =\n                id === \"all\"\n                  ? \"All\"\n                  : CAT_META[id]?.label ?? id;\n              return (\n                <button\n                  key={id}\n                  type=\"button\"\n                  onClick={() => setCategory(id)}\n                  className={cn(\n                    \"shrink-0 rounded-full px-3 py-1 text-[12px] font-medium transition\",\n                    active\n                      ? \"bg-[#1d1d1f] text-white dark:bg-[#f5f5f7] dark:text-[#1d1d1f]\"\n                      : \"bg-transparent text-[#6e6e73] hover:bg-black/5 dark:text-[#98989d] dark:hover:bg-white/5\",\n                  )}\n                >\n                  {label}\n                </button>\n              );\n            })}\n          </div>\n        </div>\n\n        <div className=\"max-h-[min(58vh,420px)] overflow-y-auto px-2 pb-3\">\n          {browsing && category === \"all\" ? (\n            <div className=\"px-3 pt-2\">\n              <p className=\"mb-2 text-[11px] font-semibold tracking-[0.02em] text-[#8e8e93] uppercase\">\n                Suggested\n              </p>\n              <ul>\n                {suggested.map((item) => (\n                  <ResultRow key={item.href} item={item} />\n                ))}\n              </ul>\n            </div>\n          ) : results.length === 0 ? (\n            <div className=\"flex flex-col items-center px-6 py-16 text-center\">\n              <SearchIcon className=\"size-10 text-[#c7c7cc]\" strokeWidth={1.5} />\n              <p className=\"mt-3 text-[15px] font-medium text-[#1d1d1f] dark:text-[#f5f5f7]\">\n                No Results\n              </p>\n              <p className=\"mt-1 max-w-[16rem] text-[13px] leading-snug text-[#8e8e93]\">\n                Try a different name, category, or description.\n              </p>\n            </div>\n          ) : (\n            <div className=\"px-3 pt-2\">\n              <p className=\"mb-2 text-[11px] font-semibold tracking-[0.02em] text-[#8e8e93] uppercase\">\n                {query.trim() ? \"Results\" : CAT_META[category as Exclude<GalleryCategory, \"all\">]?.label ?? \"Results\"}\n              </p>\n              <ul>\n                {results.map((item) => (\n                  <ResultRow key={item.href} item={item} />\n                ))}\n              </ul>\n            </div>\n          )}\n        </div>\n      </div>\n    </section>\n  );\n}\n\nfunction ResultRow({ item }: { item: AppStoreItem }) {\n  const catLabel =\n    item.category === \"other\" ? \"\" : CAT_META[item.category]?.label;\n\n  return (\n    <li>\n      <Link\n        href={item.href}\n        className={cn(\n          \"group flex items-center gap-3.5 rounded-xl px-2 py-2.5 transition\",\n          \"hover:bg-black/[0.04] dark:hover:bg-white/[0.06]\",\n        )}\n      >\n        <AppIcon category={item.category} />\n        <div className=\"min-w-0 flex-1 border-b border-black/5 pb-2.5 group-last:border-0 dark:border-white/10\">\n          <div className=\"flex items-start justify-between gap-3\">\n            <div className=\"min-w-0\">\n              <p className=\"truncate text-[15px] font-semibold tracking-[-0.01em] text-[#1d1d1f] dark:text-[#f5f5f7]\">\n                {item.name}\n              </p>\n              <p className=\"mt-0.5 truncate text-[12px] text-[#8e8e93]\">\n                {catLabel ? `${catLabel} · ` : \"\"}\n                {item.subtitle}\n              </p>\n            </div>\n            <span\n              className={cn(\n                \"mt-0.5 shrink-0 rounded-full bg-[#e8e8ed] px-3.5 py-1\",\n                \"text-[12px] font-semibold text-[#0071e3]\",\n                \"dark:bg-[#3a3a3c] dark:text-[#0a84ff]\",\n              )}\n            >\n              GET\n            </span>\n          </div>\n        </div>\n      </Link>\n    </li>\n  );\n}\n"}],"meta":{"studioName":"GFA Development & Design Studio","siteUrl":"https://ui.gkhn.dev"}}