@photon-ai/pho-ui 2.1.1 → 2.2.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.
- package/dist/chunks/IconAlertCircleFilled-VxKU8TYK.js.map +1 -1
- package/dist/chunks/IconArrowUpRight-DGgKzQwV.js.map +1 -1
- package/dist/chunks/IconCheck-D2WuculQ.js.map +1 -1
- package/dist/chunks/IconChevronLeft-CSWMKQ4C.js.map +1 -1
- package/dist/chunks/IconInfoCircle-L0zfhg7R.js.map +1 -1
- package/dist/chunks/IconX-pZ9vrtSr.js.map +1 -1
- package/dist/chunks/animated-height-C2eQstGy.js.map +1 -1
- package/dist/chunks/{avatar-CiAvJNNs.js → avatar-BkmGDIqf.js} +3 -3
- package/dist/chunks/{avatar-CiAvJNNs.js.map → avatar-BkmGDIqf.js.map} +1 -1
- package/dist/chunks/basic-page-CB36yrpL.js +643 -0
- package/dist/chunks/basic-page-CB36yrpL.js.map +1 -0
- package/dist/chunks/{button-X5wsWvRa.js → button-BmhyLgy5.js} +3 -3
- package/dist/chunks/{button-X5wsWvRa.js.map → button-BmhyLgy5.js.map} +1 -1
- package/dist/chunks/{card-9cGnv-VD.js → card-DSW3Uhag.js} +2 -2
- package/dist/chunks/{card-9cGnv-VD.js.map → card-DSW3Uhag.js.map} +1 -1
- package/dist/chunks/{close-button-xadxp0j8.js → close-button-GvqWbLl0.js} +2 -2
- package/dist/chunks/{close-button-xadxp0j8.js.map → close-button-GvqWbLl0.js.map} +1 -1
- package/dist/chunks/{cn-DTiB6ek_.js → cn-ChIgwEl3.js} +99 -96
- package/dist/chunks/{cn-DTiB6ek_.js.map → cn-ChIgwEl3.js.map} +1 -1
- package/dist/chunks/{combobox-rLdS6fl-.js → combobox-Dj5m_dDd.js} +2 -2
- package/dist/chunks/{combobox-rLdS6fl-.js.map → combobox-Dj5m_dDd.js.map} +1 -1
- package/dist/chunks/createReactComponent-BqCmnqfX.js.map +1 -1
- package/dist/chunks/{dialog-BC23YRUq.js → dialog-DtFO7oy0.js} +5 -5
- package/dist/chunks/{dialog-BC23YRUq.js.map → dialog-DtFO7oy0.js.map} +1 -1
- package/dist/chunks/{error-DR6G2LmG.js → error-Da7P5WZi.js} +11 -10
- package/dist/chunks/{error-DR6G2LmG.js.map → error-Da7P5WZi.js.map} +1 -1
- package/dist/chunks/{input-D3XQcC4J.js → input-QSAUO30c.js} +2 -2
- package/dist/chunks/{input-D3XQcC4J.js.map → input-QSAUO30c.js.map} +1 -1
- package/dist/chunks/{input-group-D6dlma8k.js → input-group-5AtpIhC_.js} +2 -2
- package/dist/chunks/{input-group-D6dlma8k.js.map → input-group-5AtpIhC_.js.map} +1 -1
- package/dist/chunks/{menu-BC-9IP29.js → menu-Cgj3lq9z.js} +3 -3
- package/dist/chunks/{menu-BC-9IP29.js.map → menu-Cgj3lq9z.js.map} +1 -1
- package/dist/chunks/{popover-EL1SE1kj.js → popover-BhBJO346.js} +2 -2
- package/dist/chunks/{popover-EL1SE1kj.js.map → popover-BhBJO346.js.map} +1 -1
- package/dist/chunks/{scroll-area-CNZVwe7v.js → scroll-area-Cw4JFqNG.js} +2 -2
- package/dist/chunks/{scroll-area-CNZVwe7v.js.map → scroll-area-Cw4JFqNG.js.map} +1 -1
- package/dist/chunks/{sidebar-DDJC1MeD.js → sidebar-BETMObNj.js} +25 -24
- package/dist/chunks/{sidebar-DDJC1MeD.js.map → sidebar-BETMObNj.js.map} +1 -1
- package/dist/chunks/{spinner-Cy8bkyJf.js → spinner-BzLUo4Xk.js} +2 -2
- package/dist/chunks/{spinner-Cy8bkyJf.js.map → spinner-BzLUo4Xk.js.map} +1 -1
- package/dist/chunks/{status-view-CcbctPqR.js → status-view-C8tabjDu.js} +2 -2
- package/dist/chunks/{status-view-CcbctPqR.js.map → status-view-C8tabjDu.js.map} +1 -1
- package/dist/components/accordion.js +1 -1
- package/dist/components/autocomplete.js +1 -1
- package/dist/components/avatar.js +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/button.js +2 -2
- package/dist/components/card.js +1 -1
- package/dist/components/checkbox.js +1 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/collapsible.js +1 -1
- package/dist/components/combobox.js +1 -1
- package/dist/components/command.js +2 -2
- package/dist/components/command.js.map +1 -1
- package/dist/components/context-menu.js +2 -2
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/dialog.js +1 -1
- package/dist/components/drawer.js +1 -1
- package/dist/components/error.js +1 -1
- package/dist/components/field.js +1 -1
- package/dist/components/fieldset.js +1 -1
- package/dist/components/form.js +1 -1
- package/dist/components/input-group.js +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/kbd.js +1 -1
- package/dist/components/kbd.js.map +1 -1
- package/dist/components/menu.js +1 -1
- package/dist/components/menubar.js +1 -1
- package/dist/components/meter.js +1 -1
- package/dist/components/navigation-menu.js +1 -1
- package/dist/components/notification.js +1 -1
- package/dist/components/notification.js.map +1 -1
- package/dist/components/number-field.js +1 -1
- package/dist/components/otp-field.js +1 -1
- package/dist/components/otp-field.js.map +1 -1
- package/dist/components/phone-input.js +2064 -2050
- package/dist/components/phone-input.js.map +1 -1
- package/dist/components/popover.js +1 -1
- package/dist/components/preview-card.js +1 -1
- package/dist/components/progress.js +1 -1
- package/dist/components/radio-group.js +1 -1
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/scroll-area.js +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/select.js.map +1 -1
- package/dist/components/separator.js +1 -1
- package/dist/components/skeleton.js +1 -1
- package/dist/components/slider.js +1 -1
- package/dist/components/spinner.js +1 -1
- package/dist/components/status-view.js +1 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/switch.js.map +1 -1
- package/dist/components/tabs.js +1 -1
- package/dist/components/theme-toggle.js +1 -1
- package/dist/components/theme-toggle.js.map +1 -1
- package/dist/components/toast.js +1 -1
- package/dist/components/toggle-group.js +1 -1
- package/dist/components/toggle.js +1 -1
- package/dist/components/toolbar.js +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/icons.js.map +1 -1
- package/dist/index.js +11 -11
- package/dist/primitives.js +1 -3
- package/dist/sections/basic-page.js +1 -1
- package/dist/sections/sidebar.js +1 -1
- package/dist/src/components/toast/toast.d.ts +1 -1
- package/dist/src/sections/basic-page/basic-page.d.ts +57 -21
- package/dist/src/sections/basic-page/index.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/package.json +21 -20
- package/dist/chunks/basic-page-x3aFcSVs.js +0 -482
- package/dist/chunks/basic-page-x3aFcSVs.js.map +0 -1
- package/dist/primitives.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"basic-page-CB36yrpL.js","names":[],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.46.0_react@19.2.8/node_modules/@tabler/icons-react/dist/esm/icons/IconChevronRight.mjs","../../src/sections/basic-page/basic-page.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.46.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M9 6l6 6l-6 6\", \"key\": \"svg-0\" }]];\nconst IconChevronRight = createReactComponent(\"outline\", \"chevron-right\", \"ChevronRight\", __iconNode);\n\nexport { __iconNode, IconChevronRight as default };\n//# sourceMappingURL=IconChevronRight.mjs.map\n","import {\n Children,\n createContext,\n isValidElement,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type AnchorHTMLAttributes,\n type ComponentProps,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { IconChevronLeft, IconChevronRight } from \"@tabler/icons-react\";\nimport { ButtonLink, type ButtonLinkProps } from \"../../components/button\";\nimport { Error as ErrorLine } from \"../../components/error\";\nimport { AnimatedHeight } from \"../../utils/animated-height\";\nimport { cn } from \"../../utils/cn\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\n\n/**\n * BasicPage — Pho's standard content page: a page title, section titles, and\n * bordered cards of label/control rows. Settings, billing, members — any\n * single-column management surface.\n *\n * Spacing: wrap the page in `Root` (a 42rem column with `px-5` gutters,\n * vertical padding). `Stack` separates blocks (`gap-10`). Inside a block,\n * children share `gap-3`. Wrap a section title + description in `Header`\n * (no gap — title and description sit flush) so that pair stays tight —\n * don't put them as loose siblings.\n * Parts already carry `px-5`.\n *\n * Navigation: pages may drill into subpages. `RowLink` is a whole-row link\n * (trailing chevron) and `Back` returns to the parent — both render through\n * the host's `LinkProvider`; the URL itself stays the router's job. `Back`\n * floats in `Root`'s top padding, so `Title` never moves between pages that\n * have a back link and pages that don't.\n */\n\n/* ------------------------------- class map ------------------------------- */\n\n/** Card surface — border, corner, fill; no divider rules. */\nconst BASIC_PAGE_CARD_SURFACE =\n \"rounded-base border border-pho-secondary bg-pho-primary\";\n\n/**\n * Card divider rules — hairlines between direct children. A row followed by\n * an `Alert` drops its divider (the `:has()` rule) so the error line reads\n * as part of the row it belongs to — the alert's own bottom divider takes\n * over toward whatever follows. Kept separate from the surface so\n * `animateHeight` can move the dividers onto the measured content wrapper.\n */\nconst BASIC_PAGE_CARD_DIVIDERS =\n \"divide-y divide-pho-secondary [&>:has(+[data-basic-page-alert])]:border-b-0\";\n\n/** Bordered card that stacks rows with hairline dividers. */\nexport const BASIC_PAGE_CARD = `${BASIC_PAGE_CARD_SURFACE} ${BASIC_PAGE_CARD_DIVIDERS}`;\n\n/**\n * Standalone callout surface — featured block (invite form, notice, etc.).\n * Don't wrap in `Card`; this is the outer chrome.\n */\nexport const BASIC_PAGE_CALLOUT =\n \"flex min-w-0 flex-col gap-3 rounded-base border border-pho-secondary bg-pho-page p-5\";\n\n/**\n * One row — label (+ optional description) left, control right.\n * `min-h-[4.25rem]` (68px) keeps avatar/control rows from collapsing shorter\n * than a comfortable touch target.\n */\nexport const BASIC_PAGE_ROW =\n \"flex min-h-[4.25rem] min-w-0 flex-col gap-3 px-5 py-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6\";\n\n/**\n * One card row that can host subviews. Same padding rhythm as a plain row —\n * an optional trailing `ItemPanel` breaks out of it on its own.\n */\nexport const BASIC_PAGE_ITEM = \"flex flex-col gap-3 px-5 py-4\";\n\n/**\n * Secondary strip closing an `Item` that is last in its card. Negative\n * margins escape the item's `px-5 pb-4` so the gray sheet runs edge-to-edge\n * and owns the row's bottom padding. The body above stays square and the\n * sheet's top is flush against it; only the bottom rounds, nesting into the\n * card's 12px corner (11px inside the 1px border). Content lands back on the\n * item's left gutter.\n */\nexport const BASIC_PAGE_ITEM_PANEL =\n \"-mx-5 -mb-4 flex items-center rounded-b-[calc(0.75rem-1px)] bg-pho-secondary py-1 pr-3 pl-5\";\n\n/**\n * Mid-list variant — the full-bleed sheet only reads well against the card's\n * bottom corner, so when later rows follow, the panel becomes an inset chip\n * instead: fully rounded at the small-control radius, overhanging the text\n * gutter by 8px per side (content stays aligned with the body above), and\n * leaving the row's own bottom padding and the card's hairline divider alone.\n * `-mt-1.5` collapses the item's `gap-3` (12px) to an effective `gap-1.5` (6px).\n */\nexport const BASIC_PAGE_ITEM_PANEL_DIVIDED =\n \"-mx-2 -mt-1.5 flex items-center rounded-sm bg-pho-secondary px-2 py-0.5\";\n\n/* --------------------------------- motion -------------------------------- */\n\n/**\n * One spring for every BasicPage motion — the `Stack` entrance, `Title` /\n * `Header` enter, `Reveal`, the Title ↔ Back morph, and the `Alert` /\n * `Footer` height reveals. Measured off a 60fps iOS push: ~330ms with a\n * long, gentle glide, everything co-landing. Essentially critically damped\n * (an overshooting edge would show clipped content).\n */\nconst ENTRANCE_SPRING = {\n type: \"spring\",\n stiffness: 400,\n damping: 40,\n} as const;\n\n/** Fade paired with the spring — spans most of the glide. */\nconst ENTRANCE_FADE = { duration: 0.25, ease: \"easeOut\" } as const;\n\n/**\n * Rise distance for the `Reveal` / `Stack` / `enter` entrances — small\n * travel; the content surfaces, it doesn't slide in.\n */\nconst REVEAL_RISE_PX = 12;\n\n/* --------------------------------- parts --------------------------------- */\n\n/**\n * Page chrome — centered column at the standard content width. Hosts\n * `Title` + `Stack` (and any route-level siblings). Prefer this over\n * ad-hoc `max-w-*` wrappers in the app shell.\n *\n * `44.5rem` = the 42rem (`2xl`) content column plus the two `px-5` gutters,\n * so wide viewports render exactly as before while narrow ones keep a 20px\n * breathing edge instead of running content into the viewport.\n *\n * `relative` anchors `Back`, which floats in the top padding. The top zone\n * is conditional (`:has()`): pages carrying a `Back` reserve 64px for it;\n * pages without one keep the tighter 48px so the title doesn't sit low.\n *\n * The column is also a `min-h-full` flex stack: when the host gives it a\n * height context (an app shell's scroll pane), a full-page state can claim\n * the leftover viewport with `flex-1` — `ErrorView className=\"flex-1\"`\n * centers itself vertically below the title. Hosts without a height context\n * see a normal column; regular pages are unaffected.\n *\n * Root itself never animates — the page entrance lives on `Stack`, so\n * `Title` and `Back` (the page chrome) stay put while the body below rises\n * in.\n */\nfunction Root({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n return (\n <div\n className={cn(\n \"relative mx-auto flex min-h-full w-full max-w-[44.5rem] min-w-0 flex-col px-5 pt-12 pb-12 *:min-w-0 has-[[data-basic-page-back]]:pt-16\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Slug for hash targets — \"Danger zone\" → `danger-zone`. Empty / non-string\n * children yield no id; pass an explicit `id` to override or `id=\"\"` to opt out.\n */\nfunction slugifyHeading(value: string): string {\n return value\n .normalize(\"NFKD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\nfunction headingIdFromChildren(children: ReactNode): string | undefined {\n if (typeof children !== \"string\" && typeof children !== \"number\") {\n return undefined;\n }\n const slug = slugifyHeading(String(children));\n return slug || undefined;\n}\n\n/** Resolve `id`: explicit wins; `\"\"` opts out; else slug from plain-text children. */\nfunction resolveHeadingId(\n id: string | undefined,\n children: ReactNode,\n): string | undefined {\n if (id === \"\") return undefined;\n if (id != null) return id;\n return headingIdFromChildren(children);\n}\n\n/**\n * Namespaced Motion `layoutId` for the Title ↔ Back shared-element morph —\n * give a parent page's `Title` and its subpage's `Back` the same `morphId`\n * and the word itself travels between the two: the large title shrinks into\n * the back link on drill-in, and grows back on return.\n */\nconst morphLayoutId = (id: string) => `basic-page-morph-${id}`;\n\n/** The morph rides the page-tier entrance spring — one glide, everywhere. */\nconst MORPH_TRANSITION = { layout: ENTRANCE_SPRING } as const;\n\n/**\n * Chevron entrance for a morphing `Back`, measured off a 60fps iOS push\n * (sub-pixel tracking of the recording): the arrow emerges from under the\n * left edge of the arriving word at ~62% of the flight — sliding left by\n * ~2/3 of its own width with a strong ease-out while fading in over ~120ms\n * — and lands together with the word, which is still gliding its last few\n * pixels the whole time. Nothing is sequential.\n */\nconst MORPH_CHEVRON_RISE_X = 10;\nconst MORPH_CHEVRON_ENTER = {\n delay: 0.2,\n duration: 0.12,\n ease: \"easeOut\",\n} as const;\n\n/**\n * Ink crossfade for the morphing word — iOS redraws the word in the\n * destination's ink and fades the origin-ink rendition out over the first\n * half of the flight, so the word takes off in the color it had and lands\n * in the color it gets. `MorphLabel` mounts the ghost only while a layout\n * animation is actually running, so static renders never see it.\n */\nconst MORPH_INK_FADE = { duration: 0.18, ease: \"easeOut\" } as const;\n\n/**\n * Deeper than one level, the back link's slot is already occupied when a\n * navigation happens, and iOS treats the chevron as furniture: it stays put\n * across pushes and pops between subpages, and only the label changes.\n * Measured off the WLAN → Succade push and its pop in the same recording:\n *\n * - **Push** — the outgoing back label leaves as the old title shrinks\n * into its place: iOS slides it the whole way under the chevron in\n * ~130ms; here it fades out with a short drift in that direction — the\n * full slide read as a whoosh next to a word that is still arriving.\n * - **Pop** — the incoming back label slides right out from under the\n * chevron, starting ~40% into the title's return flight, ~150ms.\n * - **Pop to the root** — no back link remains; the chevron fades out in\n * place over the first frames while the word grows into the title.\n */\nconst BACK_LABEL_LEAVE_X = -8;\nconst BACK_LABEL_LEAVE = { duration: 0.16, ease: \"easeOut\" } as const;\nconst BACK_LABEL_ARRIVE = {\n delay: 0.14,\n duration: 0.16,\n ease: \"easeOut\",\n} as const;\nconst BACK_CHEVRON_LEAVE = { duration: 0.12, ease: \"easeOut\" } as const;\n\n/**\n * Trails (breadcrumb navs) currently mounted — one per page. Read at render\n * by an arriving `Breadcrumbs` or morphing `Title`: the outgoing page's\n * trail is still mounted at that moment, so the arriving side knows one is\n * on its way out, and how long it was, and can play the depth transition —\n * hold the chevron, see the old label off, fade a separator, or fade the\n * chevron when no trail returns. `useLayoutEffect` keeps the list exact\n * within a commit: an outgoing trail's cleanup runs before an incoming\n * trail's mount effect.\n */\nconst mountedTrails: { labels: ReactNode[] }[] = [];\n\nfunction useTrailPresence(labels: ReactNode[]) {\n const entry = useRef({ labels });\n entry.current.labels = labels;\n useLayoutEffect(() => {\n const self = entry.current;\n mountedTrails.push(self);\n return () => {\n mountedTrails.splice(mountedTrails.indexOf(self), 1);\n };\n }, []);\n}\n\n/** The trail on its way out at this render, if any — snapshot at mount. */\nfunction useDepartingTrail() {\n const [departing] = useState<{ labels: ReactNode[] } | null>(() => {\n const last = mountedTrails[mountedTrails.length - 1];\n return last ? { labels: [...last.labels] } : null;\n });\n return departing;\n}\n\n/**\n * A shared element can only fly between two points the user has seen. Each\n * morph word tracks whether it is on screen (keyed by `morphId`); when the\n * other end mounts, it flies only if the word it continues was visible.\n * A title scrolled out of view yields a plain fade at the destination\n * instead of a dive in from off-screen.\n */\nconst morphWordVisible = new Map<string, boolean>();\n\n/**\n * Mount-locked flight decision + live visibility tracking for a morph word.\n * `fly` is read from the registry once at mount (was the other end on\n * screen?); the ref then feeds this word's own visibility back for the next\n * navigation. No-ops without `IntersectionObserver` (jsdom, SSR).\n */\nfunction useMorphFlight(morphId: string | undefined) {\n const [fly] = useState(\n () => morphId != null && morphWordVisible.get(morphId) === true,\n );\n const ref = useRef<HTMLSpanElement | null>(null);\n useEffect(() => {\n if (morphId == null || typeof IntersectionObserver === \"undefined\") return;\n const el = ref.current;\n if (!el) return;\n const observer = new IntersectionObserver(([entry]) => {\n if (entry) morphWordVisible.set(morphId, entry.isIntersecting);\n });\n observer.observe(el);\n return () => {\n observer.disconnect();\n // The other end reads the registry while rendering — before this\n // cleanup runs in the same commit — so clearing here keeps a page-swap\n // flight intact while a later, unrelated mount (the list re-entered\n // from the sidebar) starts fresh instead of trusting stale sight.\n morphWordVisible.delete(morphId);\n };\n }, [morphId]);\n return { fly, ref };\n}\n\n/** How a morph word shows up when it is not flying in. */\ntype MorphArrival = \"fade\" | \"rise\" | \"slide\";\n\nconst MORPH_ARRIVALS = {\n // Back label with nothing to fly from — a plain fade.\n fade: {\n initial: { opacity: 0 },\n animate: { opacity: 1 },\n transition: { opacity: ENTRANCE_FADE },\n },\n // Subpage title behind a departing morph — rises from just below.\n rise: {\n initial: { opacity: 0, y: REVEAL_RISE_PX },\n animate: { opacity: 1, y: 0 },\n transition: { y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE },\n },\n // Back label on a pop between subpages — out from under the chevron.\n slide: {\n initial: { x: \"-100%\" },\n animate: { x: 0 },\n transition: { x: BACK_LABEL_ARRIVE },\n },\n} as const;\n\n/**\n * The shared-element word of a Title ↔ Back morph. While the box flies, an\n * origin-ink copy of the word sits on top and fades out (the iOS snapshot\n * crossfade); the copy only exists during a real layout animation. When the\n * flight is skipped (`fly` false — the other end wasn't on screen, or there\n * is no other end yet), the layout snap is instant and the word appears in\n * place, or plays its `arrival` instead: a fade, a rise on the entrance\n * spring (`Title`'s `enter`), or a slide out from under the back chevron.\n */\nfunction MorphLabel({\n morphId,\n fromClassName,\n fly,\n morphRef,\n arrival,\n className,\n children,\n}: {\n morphId: string;\n /** Ink the word arrives FROM (the other end's text color). */\n fromClassName: string;\n /** Fly the layout box — the other end was on screen at navigation. */\n fly: boolean;\n morphRef: React.Ref<HTMLSpanElement>;\n /** Entrance to play when not flying; omit to appear in place. */\n arrival?: MorphArrival;\n /** Extra classes on the flying span (Title's ellipsis lives here). */\n className?: string;\n children: ReactNode;\n}) {\n const [crossfading, setCrossfading] = useState(false);\n const arriving = !fly && arrival != null ? MORPH_ARRIVALS[arrival] : null;\n return (\n <motion.span\n ref={morphRef}\n layoutId={morphLayoutId(morphId)}\n initial={arriving?.initial}\n animate={arriving?.animate}\n transition={\n fly\n ? MORPH_TRANSITION\n : { layout: { duration: 0 }, ...arriving?.transition }\n }\n onLayoutAnimationStart={() => {\n if (fly) setCrossfading(true);\n }}\n onLayoutAnimationComplete={() => setCrossfading(false)}\n className={cn(\"relative inline-block\", className)}\n >\n {children}\n {crossfading && (\n <motion.span\n aria-hidden\n initial={{ opacity: 1 }}\n animate={{ opacity: 0 }}\n transition={MORPH_INK_FADE}\n // The ghost retires itself: a zero-distance \"flight\" (a crumb that\n // stays put between pages) starts a layout animation that never\n // reports completion.\n onAnimationComplete={() => setCrossfading(false)}\n className={cn(\"absolute inset-0\", fromClassName)}\n >\n {children}\n </motion.span>\n )}\n </motion.span>\n );\n}\n\nexport interface BasicPageTitleProps extends ComponentProps<\"h1\"> {\n /**\n * Shared-element key. Pair with the same `morphId` on the subpage's\n * `Back` and the word morphs between title and back link across the\n * navigation. Skipped under reduced motion.\n */\n morphId?: string;\n /**\n * Rise this title into its line on mount — for a subpage title whose line\n * an inbound Title ↔ Back morph departs from. The moment the word lifts\n * off, this title floats up from just below it (the Stack physics), so\n * the two form one upward stream and never overlap. Combines with\n * `morphId` for a mid-level page (a list that is itself a subpage): the\n * word rises in when the page is entered from its parent and flies in\n * from the back link when returned to from its own subpage. Skipped\n * under reduced motion.\n */\n enter?: boolean;\n ref?: Ref<HTMLHeadingElement>;\n}\n\n/**\n * Ellipsis for a title word that moves. A morphing word flies in from the\n * back link's line above, and an entering word rises from below the\n * baseline: both spend part of the animation outside the heading's box, so\n * the heading itself must not clip — the `overflow: hidden` that\n * `truncate` brings lives on the moving span instead. `align-top` keeps an\n * `inline-block` with hidden overflow from taking its baseline at the\n * bottom edge, which would push the line box (and everything under the\n * title) down by the descender.\n */\nconst TITLE_MOTION_LABEL = \"max-w-full min-w-0 truncate align-top\";\n\n/**\n * Page title — largest heading on the surface. Plain-text children get a slug\n * `id` for hash targets (`#profile`); the heading itself is not a link.\n * Long names ellipsize; keep trailing accessories (`Badge`, status) as\n * `shrink-0` siblings of a `min-w-0 truncate` label inside a `flex min-w-0`\n * row so they stay visible.\n */\nfunction Title({\n id,\n morphId,\n enter,\n className,\n children,\n ...props\n}: BasicPageTitleProps) {\n const headingId = resolveHeadingId(id, children);\n const reduceMotion = useReducedMotion();\n const flight = useMorphFlight(morphId);\n const departingTrail = useDepartingTrail();\n const moving = !reduceMotion && (morphId != null || enter === true);\n // A word flying in from a trail while that trail's page leaves: if no\n // trail takes over the slot (a pop to the root), its chevron is left to\n // this title to fade out. Decided after the commit's mount effects, so a\n // page that brings its own trail (a pop between subpages) keeps the\n // chevron still instead of ghosting one under it.\n const [chevronGhost, setChevronGhost] = useState(\n flight.fly && departingTrail != null,\n );\n useLayoutEffect(() => {\n if (mountedTrails.length > 0) setChevronGhost(false);\n }, []);\n return (\n <h1\n id={headingId}\n className={cn(\n \"text-display-base text-pho-primary max-w-full min-w-0 scroll-mt-12 px-5 font-medium\",\n // A static title ellipsizes on the heading; a moving one hands the\n // clip to its span so the flight / rise is never cut off.\n moving ? \"whitespace-nowrap\" : \"truncate\",\n className,\n )}\n {...props}\n >\n {morphId != null && !reduceMotion ? (\n <MorphLabel\n morphId={morphId}\n fromClassName=\"text-pho-secondary\"\n fly={flight.fly}\n morphRef={flight.ref}\n arrival={enter ? \"rise\" : undefined}\n className={TITLE_MOTION_LABEL}\n >\n {children}\n </MorphLabel>\n ) : enter && !reduceMotion ? (\n <motion.span\n initial={{ opacity: 0, y: REVEAL_RISE_PX }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }}\n className={cn(\"inline-block\", TITLE_MOTION_LABEL)}\n >\n {children}\n </motion.span>\n ) : (\n children\n )}\n {chevronGhost && (\n <motion.span\n aria-hidden\n initial={{ opacity: 1 }}\n animate={{ opacity: 0 }}\n transition={BACK_CHEVRON_LEAVE}\n onAnimationComplete={() => setChevronGhost(false)}\n className={cn(\n \"text-pho-secondary pointer-events-none absolute inline-flex items-center\",\n BACK_CHEVRON_GHOST_POSITION,\n )}\n >\n <IconChevronLeft />\n </motion.span>\n )}\n </h1>\n );\n}\n\nexport interface BasicPageStackProps extends Omit<\n ComponentProps<\"div\">,\n RevealMotionConflicts\n> {\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Vertical stack under the page title. Blocks (forms / sections) use `gap-10`;\n * each block lays out its children with `gap-3`.\n *\n * Mounting plays the page entrance: the stack rises 12px and fades in on the\n * Card-shell / Command spring while `Title` / `Back` above stay put. What\n * that means per state:\n *\n * - **Entering the page** — whatever the page first shows (skeleton\n * included) rides in with its stack.\n * - **Skeleton in a separate component** — swapping it for the loaded\n * return mounts a fresh stack, so the entrance replays as the\n * data-arrival animation. That's the right feel for shapes the skeleton\n * couldn't predict (lists of unknown length).\n * - **1:1 skeleton** — keep ONE stack mounted and branch its children\n * (`{pending ? bones : content}`): values then fill in place with no\n * motion.\n *\n * Static under reduced motion.\n */\nfunction Stack({ className, ...props }: BasicPageStackProps) {\n const reduceMotion = useReducedMotion();\n return (\n <motion.div\n initial={reduceMotion ? false : { opacity: 0, y: REVEAL_RISE_PX }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }}\n className={cn(\n \"mt-6 flex min-w-0 flex-col gap-10\",\n \"*:flex *:min-w-0 *:flex-col *:gap-3\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Title + optional description pair. No gap between them — the block's\n * `gap-3` only applies between this header and the card.\n */\nexport interface BasicPageHeaderProps extends Omit<\n ComponentProps<\"div\">,\n RevealMotionConflicts\n> {\n /**\n * Rise this header into place on mount — the Header flavor of `Title`'s\n * `enter`, for a subpage title that carries a description: the pair\n * floats up from below the departing morph word as one block. Put `enter`\n * here and NOT on the `Title` inside. Skipped under reduced motion.\n */\n enter?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\nfunction Header({ enter, className, ...props }: BasicPageHeaderProps) {\n const reduceMotion = useReducedMotion();\n if (enter && !reduceMotion) {\n return (\n <motion.div\n initial={{ opacity: 0, y: REVEAL_RISE_PX }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }}\n className={cn(\"flex min-w-0 flex-col\", className)}\n {...props}\n />\n );\n }\n return <div className={cn(\"flex min-w-0 flex-col\", className)} {...props} />;\n}\n\n/**\n * Section heading — same padding as the page title, smaller type. Hashable via\n * slug `id` (not a link).\n */\nfunction SectionTitle({\n id,\n className,\n children,\n ...props\n}: ComponentProps<\"h2\"> & { ref?: Ref<HTMLHeadingElement> }) {\n const headingId = resolveHeadingId(id, children);\n return (\n <h2\n id={headingId}\n className={cn(\n \"text-display-xs text-pho-primary max-w-full min-w-0 scroll-mt-12 truncate px-5 font-medium\",\n className,\n )}\n {...props}\n >\n {children}\n </h2>\n );\n}\n\n/**\n * Supporting copy under a section title. Same display step as the title;\n * color + normal weight keep it secondary. Always wrap with `Header`.\n */\nfunction Description({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\n \"text-display-xs text-pho-description px-5 font-normal\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Optional caption below a section's `Card` (outside the card). One step\n * smaller than `Description` (`text-xs`). Not the card-internal dirty-state\n * `Footer`.\n */\nfunction SectionFooter({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\"text-pho-description px-5 text-xs font-normal\", className)}\n {...props}\n />\n );\n}\n\nexport interface BasicPageCardProps extends ComponentProps<\"div\"> {\n /**\n * Glide the card's height when its content swaps — bones becoming a\n * different number of rows, a row created or revoked, an inline state\n * change. The height follows the content on the page spring instead of\n * jumping. Reserve it for cards whose contents change while mounted.\n */\n animateHeight?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/** Bordered card that stacks rows with hairline dividers. */\nfunction Card({\n animateHeight,\n className,\n children,\n ...props\n}: BasicPageCardProps) {\n if (animateHeight) {\n return (\n <div\n className={cn(\n BASIC_PAGE_CARD_SURFACE,\n \"min-h-0 overflow-hidden\",\n className,\n )}\n {...props}\n >\n <AnimatedHeight\n transition={ENTRANCE_SPRING}\n className={BASIC_PAGE_CARD_DIVIDERS}\n >\n {children}\n </AnimatedHeight>\n </div>\n );\n }\n return (\n <div className={cn(BASIC_PAGE_CARD, className)} {...props}>\n {children}\n </div>\n );\n}\n\n/**\n * One card row that can host subviews. Padding matches a plain row, so use it\n * for any row that may grow an `ItemPanel` — primary content goes straight in\n * as children; the panel, when present, is the last child.\n *\n * ```tsx\n * <BasicPage.Card>\n * <BasicPage.Item>\n * …name, meta, actions…\n * <BasicPage.ItemPanel divided>…secret / expanded content…</BasicPage.ItemPanel>\n * </BasicPage.Item>\n * …later siblings (pass `divided` on the panel above)…\n * </BasicPage.Card>\n * ```\n */\nfunction Item({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n return <div className={cn(BASIC_PAGE_ITEM, className)} {...props} />;\n}\n\nexport interface BasicPageItemPanelProps extends ComponentProps<\"div\"> {\n /**\n * Set when later card rows follow this item. The panel then renders as an\n * inset rounded chip (keeping the card's own divider) instead of the\n * full-bleed bottom sheet, which only reads well against the card's corner.\n * @default false\n */\n divided?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Secondary strip at the end of an `Item`. When the item closes the card,\n * it's a gray sheet flush under the body, full-bleed and owning the row's\n * bottom padding, rounding only into the card's bottom corner. Pass `divided`\n * when later card rows follow — the panel becomes an inset rounded chip and\n * the card's hairline divider stays in charge. Content is already a centered\n * flex row either way.\n */\nfunction ItemPanel({\n divided = false,\n className,\n ...props\n}: BasicPageItemPanelProps) {\n return (\n <div\n className={cn(\n divided ? BASIC_PAGE_ITEM_PANEL_DIVIDED : BASIC_PAGE_ITEM_PANEL,\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * Standalone callout. Pair with `CalloutTitle` / `CalloutDescription` (and\n * optional actions) for a featured block — e.g. an invite form. Do not wrap\n * in `Card`; this is already the outer surface.\n *\n * Pass `icon` for a decorative glyph anchored to the header block (first\n * child) — typically a Tabler outline icon. The icon is aria-hidden; keep\n * meaning in the title/description.\n */\nfunction Callout({\n className,\n icon,\n children,\n ...props\n}: ComponentProps<\"div\"> & {\n ref?: Ref<HTMLDivElement>;\n /** Decorative icon, top-right of the header (first child). */\n icon?: ReactNode;\n}) {\n // Treat boolean false (and other empty values) as absent — `icon={cond &&\n // <Icon />}` is a common pattern and must not reserve header padding.\n if (icon == null || icon === false || icon === true) {\n return (\n <div className={cn(BASIC_PAGE_CALLOUT, className)} {...props}>\n {children}\n </div>\n );\n }\n\n const [header, ...rest] = Children.toArray(children);\n\n return (\n <div className={cn(BASIC_PAGE_CALLOUT, className)} {...props}>\n <div className=\"relative\">\n <div className=\"min-w-0 pr-16\">{header}</div>\n <div\n aria-hidden\n className=\"text-pho-description pointer-events-none absolute top-1/2 right-0 size-14 -translate-y-1/2 opacity-50 [&>svg]:size-full\"\n >\n {icon}\n </div>\n </div>\n {rest}\n </div>\n );\n}\n\n/** Callout heading — same type as `SectionTitle`. Hashable via slug `id`. */\nfunction CalloutTitle({\n id,\n className,\n children,\n ...props\n}: ComponentProps<\"h2\"> & { ref?: Ref<HTMLHeadingElement> }) {\n const headingId = resolveHeadingId(id, children);\n return (\n <h2\n id={headingId}\n className={cn(\n \"text-display-xs text-pho-primary max-w-full min-w-0 scroll-mt-12 truncate font-medium\",\n className,\n )}\n {...props}\n >\n {children}\n </h2>\n );\n}\n\n/** Supporting copy under a callout title. */\nfunction CalloutDescription({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\n \"text-display-xs text-pho-description font-normal\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface BasicPageRowProps extends Omit<\n ComponentProps<\"div\">,\n \"title\"\n> {\n /** Left-side field name — plain text, or compose with a trailing hint. */\n label: ReactNode;\n /** Optional helper under the label. */\n description?: ReactNode;\n /** Associates the label with a control id. When omitted, the label is not a `<label>`. */\n htmlFor?: string;\n /**\n * Stack the label above a full-width control instead of the default\n * label-left / control-right layout at `sm+`. Keeps the compact `gap-3`\n * rhythm — the default row's `justify-between` would otherwise stretch\n * label and control apart under `min-h-[4.25rem]`.\n */\n stacked?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * One row: label (+ optional description) left, control right.\n * Pass `htmlFor` when the row wraps a labelled input; omit it for display/\n * action rows (avatar, danger-zone delete). Pass `stacked` for full-width\n * controls that sit under the label.\n */\nfunction Row({\n label,\n description,\n htmlFor,\n stacked = false,\n className,\n children,\n ...props\n}: BasicPageRowProps) {\n return (\n <div\n className={cn(\n BASIC_PAGE_ROW,\n stacked && \"sm:flex-col sm:items-stretch sm:justify-start sm:gap-3\",\n className,\n )}\n {...props}\n >\n <div className=\"min-w-0\">\n {htmlFor ? (\n <label\n htmlFor={htmlFor}\n className=\"text-pho-primary block text-base font-normal\"\n >\n {label}\n </label>\n ) : (\n <p className=\"text-pho-primary text-base font-normal\">{label}</p>\n )}\n {description ? (\n <p className=\"text-pho-description text-xs\">{description}</p>\n ) : null}\n </div>\n <div\n className={cn(\n \"flex w-full min-w-0 items-center gap-2 sm:w-auto\",\n stacked && \"sm:w-full\",\n )}\n >\n {children}\n </div>\n </div>\n );\n}\n\n/** Read-only value on the right side of a row (email, etc.). */\nfunction Value({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\n \"text-pho-description max-w-full min-w-0 truncate text-base\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface BasicPageAlertProps extends Omit<\n ComponentProps<\"div\">,\n \"children\"\n> {\n /** The caught value — a problem body, an `Error` wrapping one, or unknown. */\n error?: unknown;\n /** Override the derived message. */\n children?: ReactNode;\n /**\n * Explicit presence override — defaults to whether there is anything to\n * show, so the reveal plays as the mutation's error comes and goes.\n */\n show?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Server error under a row, still inside the card — the partial failure\n * (\"Couldn't update your name\"), rendered by `Error` from the problem body.\n * Keep it mounted and pass the mutation's error: the line reveals and\n * collapses on `Footer`'s height spring + fade. The card drops its divider\n * above the line so it reads as part of the row it belongs to. Matches card\n * horizontal padding — don't re-apply `px-5`.\n *\n * For a page that failed to load outright, swap the stack for an `ErrorView`\n * instead.\n */\nfunction Alert({\n error,\n show,\n className,\n children,\n ...props\n}: BasicPageAlertProps) {\n const reduceMotion = useReducedMotion();\n const hasContent = error != null || children != null;\n // Explicit `show` cannot force an empty animated slot — ErrorLine returns\n // null when there is nothing to render.\n const visible = (show ?? true) && hasContent;\n return (\n <AnimatePresence initial={false}>\n {visible ? (\n <motion.div\n data-basic-page-alert=\"\"\n className=\"overflow-hidden\"\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: \"auto\", opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={\n reduceMotion\n ? { duration: 0 }\n : { height: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }\n }\n >\n <ErrorLine\n error={error}\n className={cn(\"px-5 pb-3\", className)}\n {...props}\n >\n {children}\n </ErrorLine>\n </motion.div>\n ) : null}\n </AnimatePresence>\n );\n}\n\nexport interface BasicPageFooterProps extends ComponentProps<\"div\"> {\n /**\n * Animated presence for dirty-state bars: the card glides open/closed on a\n * height spring while the content fades. Omit for a static, always-visible\n * footer (no animation). No reveal plays on first mount — only on flips.\n */\n show?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Trailing bar inside a card — typically a save status + submit button that\n * appears after the form is dirty. Same horizontal/vertical padding as a row.\n * Pass `show` (instead of conditional rendering) to animate the bar in and\n * out following the onboarding card's motion language.\n */\nfunction Footer({ show, className, ...props }: BasicPageFooterProps) {\n const reduceMotion = useReducedMotion();\n const bar = (\n <div\n className={cn(\n \"flex items-center justify-between gap-6 px-5 py-4\",\n className,\n )}\n {...props}\n />\n );\n\n if (show === undefined) return bar;\n\n return (\n <AnimatePresence initial={false}>\n {show ? (\n <motion.div\n className=\"overflow-hidden\"\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: \"auto\", opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={\n reduceMotion\n ? { duration: 0 }\n : { height: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }\n }\n >\n {bar}\n </motion.div>\n ) : null}\n </AnimatePresence>\n );\n}\n\n/**\n * DOM handlers whose React signatures clash with Motion's gesture props of\n * the same name on `motion.*` elements — excluded from `Reveal`'s public API\n * (same policy as Button).\n */\ntype RevealMotionConflicts =\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onDragEnter\"\n | \"onDragExit\"\n | \"onDragLeave\"\n | \"onDragOver\"\n | \"onDrop\";\n\nexport interface BasicPageRevealProps extends Omit<\n ComponentProps<\"div\">,\n RevealMotionConflicts\n> {\n /**\n * Animated presence for a `Stack` block that comes and goes: keep the\n * block mounted and drive it with `show` — it glides open and closed\n * (height + fade) on the page spring, with the Stack's `gap-10`\n * compensated so nothing jumps when it fully collapses. Omit `show` for\n * the one-shot data-arrival entrance.\n */\n show?: boolean;\n /**\n * Which gap the presence variant compensates while collapsing —\n * `\"stack\"` for a Stack block (`gap-10`), `\"block\"` for an element\n * inside a block (`gap-3`). @default \"stack\"\n */\n gap?: \"stack\" | \"block\";\n /**\n * Set when this wraps multiple `Card` rows — the extra element Reveal\n * needs for `AnimatePresence` sits between them and the card's own\n * `divide-y`, so the hairlines between the wrapped rows disappear unless\n * this restores them. Leave off for content that isn't a run of rows.\n */\n dividers?: boolean;\n ref?: Ref<HTMLDivElement>;\n}\n\n/**\n * Exactly one surrounding flex gap — the presence variant trades this much\n * margin against its height so neighbours glide to their final positions\n * instead of snapping by a gap when the block unmounts. `dividers` rows sit\n * in a `divide-y` card with no flex `gap` at all, so they get no\n * compensation — `gap` would otherwise overshoot into the next row's padding\n * as the block collapses.\n */\nconst GAP_COMPENSATION = {\n stack: \"-2.5rem\",\n block: \"-0.75rem\",\n none: \"0px\",\n} as const;\n\n/**\n * Data-arrival entrance for regions whose shape the skeleton couldn't\n * predict — a list of unknown length, a region that depends on the data.\n * Wrap that region (keeping `Title` and 1:1 chrome outside): once the shape\n * is known it rises 12px and fades in on the Card-shell / Command spring,\n * the same physics as the `Footer` / `Alert` reveals. When the skeleton\n * mirrors the loaded layout element for element, skip the reveal — values\n * fill into a shape that never moves. The page *entrance* is separate and\n * already built into `Stack`: whatever the page first shows (including the\n * skeleton) rides that. Static under reduced motion.\n *\n * With `show`, Reveal becomes an animated-presence block instead (the\n * `Footer` grammar): a whole section of the Stack that appears or leaves at\n * runtime glides open/closed while its neighbours follow.\n */\nfunction Reveal({\n show,\n gap = \"stack\",\n dividers,\n className,\n ...props\n}: BasicPageRevealProps) {\n const reduceMotion = useReducedMotion();\n // Card rows carry their own padding and a divide-y border, not a flex\n // gap — dividers always means zero gap to compensate for.\n const gapCompensation = GAP_COMPENSATION[dividers ? \"none\" : gap];\n\n if (show === undefined) {\n return (\n <motion.div\n initial={reduceMotion ? false : { opacity: 0, y: REVEAL_RISE_PX }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ y: ENTRANCE_SPRING, opacity: ENTRANCE_FADE }}\n className={cn(dividers && BASIC_PAGE_CARD_DIVIDERS, className)}\n {...props}\n />\n );\n }\n\n return (\n <AnimatePresence initial={false}>\n {show ? (\n <motion.div\n initial={{\n height: 0,\n opacity: 0,\n marginTop: gapCompensation,\n }}\n animate={{ height: \"auto\", opacity: 1, marginTop: 0 }}\n exit={{ height: 0, opacity: 0, marginTop: gapCompensation }}\n transition={\n reduceMotion\n ? { duration: 0 }\n : {\n height: ENTRANCE_SPRING,\n marginTop: ENTRANCE_SPRING,\n opacity: ENTRANCE_FADE,\n }\n }\n className={cn(\n \"overflow-hidden\",\n dividers && BASIC_PAGE_CARD_DIVIDERS,\n className,\n )}\n {...props}\n />\n ) : null}\n </AnimatePresence>\n );\n}\n\n/* ------------------------------- navigation ------------------------------ */\n\n/**\n * Where the trail's chevron sits, in `Root` coordinates, for the ghost a\n * root `Title` fades out on a pop: the nav's `top-4 left-[10.25px]` plus\n * the button's 8px icon-side padding, centred in the 32px control; the\n * glyph is 1.125em of the `sm` button's 14px type. Kept next to the trail's\n * own offset math.\n */\nconst BACK_CHEVRON_GHOST_POSITION =\n \"top-4 left-[18.25px] h-8 text-base [&>svg]:size-[1.125em] [&>svg]:shrink-0\";\n\n/** Tailwind's `sm` — below it the trail collapses to its last crumb. */\nconst TRAIL_WIDE_QUERY = \"(min-width: 40rem)\";\n\nfunction isNarrowViewport() {\n return (\n typeof window !== \"undefined\" &&\n typeof window.matchMedia === \"function\" &&\n !window.matchMedia(TRAIL_WIDE_QUERY).matches\n );\n}\n\ninterface TrailItemContextValue {\n index: number;\n count: number;\n /** The trail on its way out at this render, if any. */\n departing: { labels: ReactNode[] } | null;\n /** Below `sm` — only the last crumb shows, with the chevron; decided at mount. */\n narrow: boolean;\n}\n\nconst TrailItemContext = createContext<TrailItemContextValue | null>(null);\n\nexport interface BasicPageBreadcrumbsProps extends ComponentProps<\"nav\"> {\n /** `Crumb`s, root-most first; the last one is the immediate parent. */\n children: ReactNode;\n}\n\n/**\n * The trail above the title — where this page sits, root-most crumb first,\n * ending with the immediate parent; the current page is the `Title` below.\n * Place inside `Root`, before `Title`. It floats in `Root`'s reserved top\n * padding (vertically centered in the 64px zone) instead of taking layout\n * space, so `Title` sits at the same position whether or not a page has\n * one.\n *\n * One form at every depth: `‹ parent / parent / parent` — the chevron opens\n * the trail (it is the way up), slashes separate the crumbs (they are the\n * path), one crumb or many. Below `sm` the path collapses to `‹ parent` —\n * the last crumb alone. Crumbs render through the host's `LinkProvider`.\n *\n * The offsets align the first *label* glyph with the title glyph. Title\n * text sits 40px in (`px-5` root gutter + `px-5` title pad); inside a `sm`\n * ghost button the label sits 29.75px in (8px icon-side pad + 15.75px\n * chevron, 1.125em at the 14px `text-base`, + 4px gap + 2px label-span\n * `px-0.5`) — so `left` is the 10.25px difference. `top-4` centers the 32px\n * buttons in the 64px reserved zone. The chevron and the ghost hover pills\n * hang in the padding — the usual optical alignment for borderless controls.\n *\n * Motion between pages: the tail crumb is the morph partner of the child\n * page's `Title` (pair their `morphId`s); everything before it holds still.\n * On a push the trail grows by one — a separator emerges from under the\n * arriving word; on a pop it loses its tail — the word grows back into the\n * title and the spare separator fades. Collapsed, it behaves as `Back`.\n */\nfunction Breadcrumbs({\n className,\n children,\n ...props\n}: BasicPageBreadcrumbsProps) {\n const items = Children.toArray(children).filter(isValidElement);\n const labels = items.map(\n (item) => (item.props as { children?: ReactNode }).children ?? null,\n );\n const departing = useDepartingTrail();\n useTrailPresence(labels);\n const [narrow] = useState(isNarrowViewport);\n const reduceMotion = useReducedMotion();\n const count = items.length;\n // A pop, wide: the trail lost its tail — the separator that stood before\n // it has nothing to precede any more and fades where it was.\n const [spareSeparator, setSpareSeparator] = useState(\n !reduceMotion &&\n !narrow &&\n departing != null &&\n departing.labels.length > count,\n );\n return (\n <nav\n aria-label=\"Breadcrumb\"\n data-basic-page-back=\"\"\n className={cn(\n \"absolute top-4 left-[10.25px] flex min-w-max items-center\",\n className,\n )}\n {...props}\n >\n <ol className=\"m-0 flex list-none items-center p-0\">\n {items.map((item, index) => (\n <li\n key={item.key ?? index}\n className={cn(\n \"m-0 flex items-center\",\n index < count - 1 && \"max-sm:hidden\",\n )}\n >\n <TrailItemContext.Provider\n value={{ index, count, departing, narrow }}\n >\n {item}\n </TrailItemContext.Provider>\n </li>\n ))}\n {spareSeparator && (\n <li aria-hidden className=\"m-0 flex items-center max-sm:hidden\">\n <motion.span\n initial={{ opacity: 1 }}\n animate={{ opacity: 0 }}\n transition={BACK_CHEVRON_LEAVE}\n onAnimationComplete={() => setSpareSeparator(false)}\n className={TRAIL_SEPARATOR}\n >\n {TRAIL_SEPARATOR_GLYPH}\n </motion.span>\n </li>\n )}\n </ol>\n </nav>\n );\n}\n\n/**\n * `/` between crumbs — description ink, tucked into the buttons' padding.\n * A slash, not a chevron: the trail opens with `‹`, and a `›` after it read\n * as two directions at once. The `‹` is the way up; the slashes are the\n * path.\n */\nconst TRAIL_SEPARATOR =\n \"text-pho-description -mx-1 inline-flex shrink-0 text-base select-none\";\nconst TRAIL_SEPARATOR_GLYPH = \"/\";\n\nexport interface BasicPageCrumbProps extends Omit<\n ButtonLinkProps,\n \"variant\" | \"size\" | \"shape\" | \"prefix\" | \"suffix\" | \"svgOnly\" | \"href\"\n> {\n /** Destination — rendered through the host's `LinkProvider`. */\n href: string;\n /** Label — name the page. @default \"Back\" */\n children?: ReactNode;\n /**\n * Shared-element key. Pair with the same `morphId` on that page's `Title`\n * and the word morphs between title and crumb across the navigation.\n * Skipped under reduced motion.\n */\n morphId?: string;\n}\n\n/**\n * One step of the trail — a small ghost `ButtonLink`. Only meaningful\n * inside `Breadcrumbs`, which decides what shows at which width and which\n * crumb is the tail; `Back` is the one-crumb shorthand.\n */\nfunction Crumb({\n href,\n className,\n morphId,\n children = \"Back\",\n ...props\n}: BasicPageCrumbProps) {\n const trail = useContext(TrailItemContext);\n const reduceMotion = useReducedMotion();\n const morphing = morphId != null && !reduceMotion;\n const flight = useMorphFlight(morphing ? morphId : undefined);\n if (trail == null) {\n throw new Error(\n \"BasicPage.Crumb must be rendered inside BasicPage.Breadcrumbs\",\n );\n }\n const { index, count, departing, narrow } = trail;\n const first = index === 0;\n const last = index === count - 1;\n const grew = departing != null && departing.labels.length < count;\n const shrank = departing != null && departing.labels.length > count;\n // Is this crumb new on screen? Wide: only a tail the trail just grew by\n // (or any tail with no trail before it — a cold mount, the first push).\n // Collapsed: the tail is always the one visible crumb, so a change of\n // trail always changes what shows.\n const newHere = last && (departing == null || grew || narrow);\n // Wide, the chevron opens the trail (first crumb); collapsed, it leads\n // the tail (the only crumb). Both, when they are the same crumb.\n const chevron = first || last;\n const chevronClass =\n first && last ? \"\" : first ? \"max-sm:hidden\" : \"sm:hidden\";\n // Between subpages the chevron is already there — it holds still. Only\n // the first push (no trail on the way out) brings it in with the word.\n const chevronHolds = departing != null;\n // How the label shows up when nothing flies in. Collapsed and a pop: out\n // from under the chevron; a crumb that was already there: no entrance.\n const arrival: MorphArrival | undefined = !last\n ? undefined\n : narrow\n ? shrank\n ? \"slide\"\n : \"fade\"\n : newHere\n ? \"fade\"\n : undefined;\n // Collapsed and a push: the old tail label leaves as the new one arrives\n // in the same slot. Wide, the old tail simply stays as the crumb before.\n const [leavingLabel, setLeavingLabel] = useState<ReactNode>(\n morphing && narrow && flight.fly && departing != null && !shrank\n ? departing.labels[departing.labels.length - 1]\n : null,\n );\n const animateChevron = morphing && chevron && !chevronHolds;\n const animateSeparator =\n morphing && !first && last && (departing == null || grew);\n\n return (\n <>\n {!first && (\n <span aria-hidden className={cn(TRAIL_SEPARATOR, \"max-sm:hidden\")}>\n {animateSeparator ? (\n <motion.span\n initial={\n flight.fly\n ? { opacity: 0, x: MORPH_CHEVRON_RISE_X }\n : { opacity: 0 }\n }\n animate={flight.fly ? { opacity: 1, x: 0 } : { opacity: 1 }}\n transition={flight.fly ? MORPH_CHEVRON_ENTER : ENTRANCE_FADE}\n className=\"inline-flex\"\n >\n {TRAIL_SEPARATOR_GLYPH}\n </motion.span>\n ) : (\n TRAIL_SEPARATOR_GLYPH\n )}\n </span>\n )}\n <ButtonLink\n variant=\"ghost\"\n size=\"sm\"\n href={href}\n prefix={\n chevron ? (\n animateChevron ? (\n // Mirrors the slot's direct-child svg sizing — the fade\n // wrapper sits between the slot and the icon.\n <motion.span\n initial={\n flight.fly\n ? { opacity: 0, x: MORPH_CHEVRON_RISE_X }\n : { opacity: 0 }\n }\n animate={flight.fly ? { opacity: 1, x: 0 } : { opacity: 1 }}\n transition={flight.fly ? MORPH_CHEVRON_ENTER : ENTRANCE_FADE}\n className={cn(\n \"inline-flex [&>svg]:size-[1.125em] [&>svg]:shrink-0\",\n chevronClass,\n )}\n >\n <IconChevronLeft />\n </motion.span>\n ) : (\n <span\n className={cn(\n \"inline-flex [&>svg]:size-[1.125em] [&>svg]:shrink-0\",\n chevronClass,\n )}\n >\n <IconChevronLeft />\n </span>\n )\n ) : (\n // An empty slot keeps every crumb on the icon-side padding, so a\n // crumb measures the same whether or not it is showing a chevron\n // — a label must not shift by 2px when it changes role.\n <span className=\"hidden\" />\n )\n }\n className={cn(\"min-w-max\", className)}\n {...props}\n >\n {morphing && morphId != null ? (\n // The word's box: unclipped while a flight is possible, clipped at\n // its left edge while the label slides out from under the chevron.\n // The departing label sits on top as its own layer so the arriving\n // word is never affected by it. A block, not an inline-block: hidden\n // overflow on an inline-block moves its baseline to the bottom edge\n // and would grow the button by the descender. Crumbs before the tail\n // ellipsize; the tail stays whole so its flight is never clipped.\n <span\n className={cn(\n \"relative block\",\n !flight.fly && arrival === \"slide\" && \"overflow-hidden\",\n !last && \"max-w-40 truncate\",\n )}\n >\n <MorphLabel\n morphId={morphId}\n fromClassName=\"text-pho-primary\"\n fly={flight.fly}\n morphRef={flight.ref}\n arrival={arrival}\n >\n {children}\n </MorphLabel>\n {leavingLabel != null && (\n <motion.span\n aria-hidden\n initial={{ x: 0, opacity: 1 }}\n animate={{ x: BACK_LABEL_LEAVE_X, opacity: 0 }}\n transition={BACK_LABEL_LEAVE}\n onAnimationComplete={() => setLeavingLabel(null)}\n className=\"pointer-events-none absolute inset-y-0 left-0 whitespace-nowrap\"\n >\n {leavingLabel}\n </motion.span>\n )}\n </span>\n ) : (\n <span className={cn(\"block\", !last && \"max-w-40 truncate\")}>\n {children}\n </span>\n )}\n </ButtonLink>\n </>\n );\n}\n\nexport type BasicPageBackProps = BasicPageCrumbProps;\n\n/**\n * Back link for a subpage one level down — the one-crumb `Breadcrumbs`:\n * `‹ parent`, floating in `Root`'s reserved top padding, morphing with the\n * parent's `Title` when they share a `morphId`. Deeper pages list the whole\n * path with `Breadcrumbs` + `Crumb` instead.\n */\nfunction Back({ className, ...props }: BasicPageBackProps) {\n return (\n <Breadcrumbs className={className}>\n <Crumb {...props} />\n </Breadcrumbs>\n );\n}\n\nexport interface BasicPageRowLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\" | \"title\"\n> {\n /** Left-side field name — plain text, or compose with a trailing badge. */\n label: ReactNode;\n /** Optional helper under the label. */\n description?: ReactNode;\n /** Optional media left of the label (e.g. project avatar). */\n leading?: ReactNode;\n /** Subpage destination — rendered through the host's `LinkProvider`. */\n href: string;\n /** Optional trailing value before the chevron (compose with `Value`). */\n children?: ReactNode;\n ref?: Ref<HTMLAnchorElement>;\n}\n\n/**\n * A row that navigates — the whole row is one link into a subpage, with a\n * trailing chevron cue. Always horizontal: a column stack would park the\n * chevron under the label on narrow viewports. Hover lifts the row, and\n * first/last rows follow the card's corner radius.\n */\nfunction RowLink({\n label,\n description,\n leading,\n href,\n className,\n children,\n ...props\n}: BasicPageRowLinkProps) {\n const Link = useLinkComponent();\n return (\n <Link\n href={href}\n {...props}\n className={cn(\n // Same padding / min-height as `BASIC_PAGE_ROW`, but never `flex-col`\n // — navigation rows keep label left and chevron right at every width.\n \"flex min-h-[4.25rem] min-w-0 flex-row items-center justify-between gap-6 px-5 py-4\",\n \"group outline-pho-brand hover:bg-pho-primary-hover cursor-pointer transition-colors focus-visible:outline-2 focus-visible:-outline-offset-2\",\n // Hover wash follows the card's rounding (minus the 1px border).\n \"first:rounded-t-[calc(var(--radius-base)-1px)] last:rounded-b-[calc(var(--radius-base)-1px)]\",\n className,\n )}\n >\n <div\n className={cn(\n \"flex min-w-0 flex-1 items-center\",\n leading != null && \"gap-3\",\n )}\n >\n {leading}\n <div className=\"min-w-0\">\n <div className=\"flex min-w-0 items-center gap-1\">\n {typeof label === \"string\" || typeof label === \"number\" ? (\n <p className=\"text-pho-primary truncate text-base font-medium\">\n {label}\n </p>\n ) : (\n label\n )}\n </div>\n {description ? (\n <div className=\"text-pho-description truncate text-xs\">\n {description}\n </div>\n ) : null}\n </div>\n </div>\n <div className=\"flex min-w-0 items-center justify-end gap-2\">\n {children != null ? <div className=\"min-w-0\">{children}</div> : null}\n <IconChevronRight\n aria-hidden\n className=\"text-pho-secondary group-hover:text-pho-secondary-hover size-4 shrink-0 transition-colors\"\n />\n </div>\n </Link>\n );\n}\n\n/* -------------------------------- assembly ------------------------------- */\n\n/**\n * Basic page layout. One stack, one gap rhythm — only title scale differs:\n *\n * ```tsx\n * <BasicPage.Root>\n * <BasicPage.Title>Profile</BasicPage.Title>\n * <BasicPage.Stack>\n * <form>\n * <BasicPage.Card>\n * <BasicPage.Row label=\"Name\" htmlFor=\"first-name\">\n * <Input id=\"first-name\" size=\"sm\" />\n * </BasicPage.Row>\n * </BasicPage.Card>\n * </form>\n * <form>\n * <BasicPage.Header>\n * <BasicPage.SectionTitle>Agent profile</BasicPage.SectionTitle>\n * <BasicPage.Description>…</BasicPage.Description>\n * </BasicPage.Header>\n * <BasicPage.Card>…</BasicPage.Card>\n * </form>\n * <div>\n * <BasicPage.SectionTitle>Danger zone</BasicPage.SectionTitle>\n * <BasicPage.Card>\n * <BasicPage.Row label=\"Delete account\" description=\"…\">\n * <Button color=\"destructive\" size=\"sm\">Delete</Button>\n * </BasicPage.Row>\n * </BasicPage.Card>\n * </div>\n * <BasicPage.Callout icon={<IconMail stroke={1.25} />}>\n * <BasicPage.Header>\n * <BasicPage.CalloutTitle>Invitations</BasicPage.CalloutTitle>\n * <BasicPage.CalloutDescription>…</BasicPage.CalloutDescription>\n * </BasicPage.Header>\n * …invite form…\n * </BasicPage.Callout>\n * </BasicPage.Stack>\n * </BasicPage.Root>\n * ```\n *\n * Subpage — `Back` above the title, `RowLink` rows to drill in:\n *\n * ```tsx\n * <BasicPage.Root>\n * <BasicPage.Back href=\"/settings\">Settings</BasicPage.Back>\n * <BasicPage.Title>Billing</BasicPage.Title>\n * <BasicPage.Stack>\n * <div>\n * <BasicPage.Card>\n * <BasicPage.RowLink label=\"Invoices\" href=\"/settings/invoices\">\n * <BasicPage.Value>12 documents</BasicPage.Value>\n * </BasicPage.RowLink>\n * </BasicPage.Card>\n * </div>\n * </BasicPage.Stack>\n * </BasicPage.Root>\n * ```\n */\nexport const BasicPage = {\n Root,\n Breadcrumbs,\n Crumb,\n Back,\n Title,\n Stack,\n Header,\n SectionTitle,\n Description,\n SectionFooter,\n Card,\n Item,\n ItemPanel,\n Callout,\n CalloutTitle,\n CalloutDescription,\n Row,\n RowLink,\n Value,\n Alert,\n Footer,\n Reveal,\n};\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { t as l } from "./cn-
|
|
2
|
+
import { t as l } from "./cn-ChIgwEl3.js";
|
|
3
3
|
import { n as Z, r as A } from "./link-provider-9TFNyDqO.js";
|
|
4
4
|
import { t as R } from "./resolve-variant-CcD8iG5l.js";
|
|
5
|
-
import { n as C } from "./spinner-
|
|
5
|
+
import { n as C } from "./spinner-BzLUo4Xk.js";
|
|
6
6
|
import { isValidElement as I, useEffect as Q, useMemo as J, useRef as X, useState as Y } from "react";
|
|
7
7
|
import { Fragment as $, jsx as s, jsxs as z } from "react/jsx-runtime";
|
|
8
8
|
import { motion as w, useReducedMotion as ee } from "motion/react";
|
|
@@ -472,4 +472,4 @@ export {
|
|
|
472
472
|
F as u
|
|
473
473
|
};
|
|
474
474
|
|
|
475
|
-
//# sourceMappingURL=button-
|
|
475
|
+
//# sourceMappingURL=button-BmhyLgy5.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-X5wsWvRa.js","names":[],"sources":["../../src/components/button/button.tsx"],"sourcesContent":["import {\n isValidElement,\n useEffect,\n useMemo,\n useRef,\n useState,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type ComponentType,\n type FC,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\nimport { isReactComponent } from \"../../utils/is-react-component\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\nimport { Spinner } from \"../spinner/spinner\";\n\n/**\n * Design notes:\n * - TWO visual axes: `variant` is the surface (filled / outlined / ghost /\n * effect) and `color` is the semantic ink (default / destructive). Dialog\n * confirms use the default `effect`; danger-zone triggers are `ghost` +\n * `destructive`. `effect` is the marketing pill: same-hue 1.5px outer rim\n * plus inset sheen that springs on hover.\n * - `iconReveal` is a separate boolean: on hover, prefix/suffix icons spring\n * in from collapsed — works with any variant, independent of `effect`.\n * - Press scale (Motion `whileTap` → 0.97) is shared by every variant; only\n * `effect` also springs its inset sheen on hover/press, and replays a\n * one-shot sheen glint when it leaves `disabled`/`loading` (wake).\n * - THREE sizes (`sm` / `md` / `lg`, default `md`). No `xs` / `xl`.\n * - `prefix` / `suffix` are generic slots (icon, spinner, count…), not\n * icon-only props. `svgOnly` makes an icon button and *requires* an\n * `aria-label`. `loading` is declarative. Effect’s rim/sheen are\n * box-shadow layers (not layout `border`).\n * - Navigation belongs to `ButtonLink`; an inline text link is an anchor.\n */\n\nconst COMMON_ROOT = cn(\n // Transition colors/shadows in CSS; transform (press scale) is owned by Motion.\n \"group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-pho-brand transition-[color,background-color,border-color,opacity,box-shadow,outline-color] duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2\",\n // Disabled (native `disabled` on <button>, `aria-disabled` on links).\n \"disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none\",\n);\n\n/**\n * Size axis — padding, text size, and icon-only (svgOnly) padding.\n *\n * Icon-side tuck (LiftKit-derived optical correction): a side that carries an\n * icon gets its padding divided by the golden ratio — an icon's rounded form\n * brings its own whitespace, so the full text-edge padding reads as a hole.\n * LiftKit: `--button-padX-sideWithIcon: calc(font-size / 1.618)`. Applied to\n * Pho's paddings: sm 14px/φ ≈ 8px · md 16px/φ ≈ 10px · lg 20px/φ ≈ 12px,\n * selected structurally via `:has()` on the slot markers (`data-icon` is\n * `prefix`, `suffix`, or `loading` — the spinner is an icon too).\n */\nexport const BUTTON_SIZES = {\n sm: {\n classes: cn(\n \"gap-1 px-3.5 py-1.5 text-base font-medium data-svg-only:p-2\",\n \"has-[[data-icon=prefix]]:pl-2 has-[[data-icon=loading]]:pl-2 has-[[data-icon=suffix]]:pr-2\",\n ),\n description: \"Small — compact rows, tables, and toolbars\",\n },\n md: {\n classes: cn(\n \"gap-1 px-4 py-2 text-base font-medium data-svg-only:p-2.5\",\n \"has-[[data-icon=prefix]]:pl-2.5 has-[[data-icon=loading]]:pl-2.5 has-[[data-icon=suffix]]:pr-2.5\",\n ),\n description: \"Medium — the default\",\n },\n lg: {\n classes: cn(\n \"gap-1.5 px-5 py-2.5 text-md font-medium data-svg-only:p-3\",\n \"has-[[data-icon=prefix]]:pl-3 has-[[data-icon=loading]]:pl-3 has-[[data-icon=suffix]]:pr-3\",\n ),\n description: \"Large — prominent and marketing actions\",\n },\n} satisfies VariantMap;\n\n/** Shape axis — corner treatment. Pills are the photon.codes default. */\nexport const BUTTON_SHAPES = {\n rounded: {\n classes: \"rounded-full\",\n description: \"Pill — the default; every button on photon.codes\",\n },\n square: {\n classes: \"rounded-base\",\n description: \"Gently rounded corners for dense app chrome\",\n },\n circle: {\n classes: \"rounded-full\",\n description: \"Circular — pair with svgOnly for round icon buttons\",\n },\n} satisfies VariantMap;\n\n/**\n * At `lg`, square buttons add squircle smoothing on top of `rounded-base`\n * (12px) — the same treatment as Input `lg`, so 44px buttons and fields share\n * a corner language. 12/44 ≈ 0.272 — LiftKit's half-step decimal (√φ − 1).\n */\nconst SQUARE_LG_RADIUS = \"squircle\";\n\n/**\n * Optical pad for `effect`: the 1.5px outer rim is a box-shadow (no layout\n * cost), so without this the painted height reads ~3px taller than filled /\n * outlined / ghost. Subtract 1.5px from each side so fill+rim matches siblings.\n */\nconst EFFECT_OPTICAL_PAD: Record<ButtonSize, string> = {\n sm: \"py-[4.5px] data-svg-only:p-[6.5px]\",\n md: \"py-[6.5px] data-svg-only:p-[8.5px]\",\n lg: \"py-[8.5px] data-svg-only:p-[10.5px]\",\n};\n\n/** Surface axis — how the control is drawn. `effect` is the default. */\nexport const BUTTON_VARIANTS = {\n effect: {\n classes: \"\",\n description:\n \"Default — marketing pill with same-hue outer rim and inset sheen\",\n },\n filled: {\n classes: \"\",\n description: \"Solid fill without sheen\",\n },\n outlined: {\n classes: \"\",\n description: \"Hairline ring — supporting action\",\n },\n ghost: {\n classes: \"\",\n description: \"Borderless — quiet / inline action\",\n },\n} satisfies VariantMap;\n\n/** Color axis — semantic ink. `default` is the default. */\nexport const BUTTON_COLORS = {\n default: {\n classes: \"\",\n description: \"Neutral / brand contrast\",\n },\n destructive: {\n classes: \"\",\n description: \"Destructive intent (delete, discard)\",\n },\n} satisfies VariantMap;\n\n/**\n * Surface × color looks. Kept as a matrix so each cell can own its tokens\n * (filled destructive is solid red; ghost destructive is red text only).\n */\nconst BUTTON_LOOKS: Record<\n keyof typeof BUTTON_VARIANTS,\n Record<keyof typeof BUTTON_COLORS, string>\n> = {\n // Icons inherit the label ink (currentColor) — no separate opacity/tint.\n // The rim replicates photon.codes' Framer CTA border: 1.5px in the fill hue\n // at 62% alpha, reading as a soft same-hue edge (the site has NO outer\n // glow — verified against live computed styles). Sheen geometry is CSS\n // vars that Motion springs on hover (see Button). All edges are shadows —\n // never a layout `border` — so height stays paired with other variants.\n effect: {\n default: cn(\n \"squircle overflow-hidden bg-pho-brand-solid text-pho-on-brand transition-none\",\n \"hover:bg-pho-brand-solid data-loading:bg-pho-brand-solid\",\n \"[--pho-rim:color-mix(in_oklab,var(--background-color-pho-brand-solid)_62%,transparent)]\",\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.5),rgba(0,0,0,0.12))]\",\n \"shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_var(--border-color-pho-on-brand),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]\",\n ),\n destructive: cn(\n \"squircle overflow-hidden bg-pho-error-solid text-white outline-pho-error transition-none\",\n \"hover:bg-pho-error-solid data-loading:bg-pho-error-solid\",\n \"[--pho-rim:color-mix(in_oklab,var(--background-color-pho-error-solid)_62%,transparent)]\",\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.45),rgba(0,0,0,0.18))]\",\n \"shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_oklch(1_0_0/0.2),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]\",\n ),\n },\n filled: {\n default:\n \"bg-pho-brand-solid text-pho-on-brand hover:bg-pho-brand-solid-hover data-loading:bg-pho-brand-solid-hover\",\n destructive:\n \"bg-pho-error-solid text-white outline-pho-error hover:bg-pho-error-solid-hover data-loading:bg-pho-error-solid-hover\",\n },\n outlined: {\n default:\n // Rest hairline is the card stroke; hover climbs to primary ink so the\n // edge reads as black (white in dark), matching the label's hover lift.\n // The ring spans the full ladder, so ease it in gently — 200ms ease-out\n // approximates the effect variant's soft spring instead of base's\n // 100ms linear snap. Prefer the named `ring-pho-ink` token over an\n // arbitrary value so consumers' Tailwind scanners pick it up reliably.\n \"bg-pho-primary text-pho-secondary ring-1 ring-pho-secondary ring-inset duration-200 ease-out hover:bg-pho-primary-hover hover:text-pho-secondary-hover hover:ring-pho-ink data-loading:bg-pho-primary-hover\",\n destructive:\n \"bg-pho-primary text-pho-error outline-pho-error ring-1 ring-pho-error ring-inset duration-200 ease-out hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary\",\n },\n ghost: {\n default:\n \"text-pho-secondary hover:bg-pho-ghost-hover hover:text-pho-secondary-hover data-loading:bg-pho-ghost-hover\",\n destructive:\n \"text-pho-error outline-pho-error hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary\",\n },\n};\n\nexport type ButtonSize = keyof typeof BUTTON_SIZES;\nexport type ButtonShape = keyof typeof BUTTON_SHAPES;\nexport type ButtonVariant = keyof typeof BUTTON_VARIANTS;\nexport type ButtonColor = keyof typeof BUTTON_COLORS;\n\n/** Compose the full class string for a button — exported for reuse/testing. */\nexport function buttonVariants(opts?: {\n variant?: ButtonVariant;\n color?: ButtonColor;\n size?: ButtonSize;\n shape?: ButtonShape;\n className?: string;\n}): string {\n const {\n variant = \"effect\",\n color = \"default\",\n size = \"md\",\n shape: shapeOpt = \"rounded\",\n className,\n } = opts ?? {};\n // Effect pills are always fully rounded — shape is ignored.\n const shape = variant === \"effect\" ? \"rounded\" : shapeOpt;\n const look =\n BUTTON_LOOKS[variant as keyof typeof BUTTON_LOOKS]?.[\n color as keyof (typeof BUTTON_LOOKS)[\"effect\"]\n ] ?? \"\";\n return cn(\n COMMON_ROOT,\n resolveVariant(BUTTON_SHAPES, shape),\n shape === \"square\" && size === \"lg\" && SQUARE_LG_RADIUS,\n resolveVariant(BUTTON_SIZES, size),\n // Outer rim is a 1.5px box-shadow (no layout cost). Shrink padding by the\n // same amount so fill+rim paints at the same visual size as filled /\n // outlined / ghost siblings of the same `size`.\n variant === \"effect\" && EFFECT_OPTICAL_PAD[size],\n look,\n className,\n );\n}\n\n/** An icon/element slot rendered before or after the label. */\ntype Slottable = ReactNode | FC<{ className?: string }>;\n\ninterface ButtonBaseProps {\n /** Surface variant. @default \"effect\" */\n variant?: ButtonVariant;\n /** Semantic color. @default \"default\" */\n color?: ButtonColor;\n /** Size variant. @default \"md\" */\n size?: ButtonSize;\n /** Corner shape. @default \"rounded\" */\n shape?: ButtonShape;\n /** Slot before the label — a component (`Plus`) or element (`<Plus />`). */\n prefix?: Slottable;\n /** Slot after the label. */\n suffix?: Slottable;\n /**\n * Reveal prefix/suffix icons on hover — collapsed and invisible at rest,\n * spring in on hover. Independent of `variant` (including `effect`).\n * Ignored when `svgOnly`. @default false\n */\n iconReveal?: boolean;\n /** Render as an icon-only button. Requires an `aria-label`. */\n svgOnly?: boolean;\n /** Show a spinner and block interaction while staying readable. */\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * DOM event handlers whose React signatures clash with Motion's gesture\n * props of the same name (Motion re-types these on `motion.*` elements).\n * Excluded from the public API — Button renders through Motion when the\n * sheen or icon reveal is active, so these handlers would be intercepted.\n */\ntype MotionHandlerConflicts =\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onDragEnter\"\n | \"onDragExit\"\n | \"onDragLeave\"\n | \"onDragOver\"\n | \"onDrop\";\n\nexport interface ButtonProps\n extends\n ButtonBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof ButtonBaseProps | MotionHandlerConflicts\n > {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface ButtonLinkProps\n extends\n ButtonBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof ButtonBaseProps | MotionHandlerConflicts\n > {\n /** Navigate to this URL (rendered via the host's `LinkProvider`). */\n href: string;\n /** Visually and semantically disable the link. */\n disabled?: boolean;\n}\n\nconst SLOT_CLASS =\n // Icons scale with the label: 1.125em tracks the button's font size\n // (16px icon beside 14px text, 18px beside 16px) so small buttons never\n // carry an oversized glyph.\n \"transition-inherit-all pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-[1.125em] [&>svg]:shrink-0\";\n\nfunction Slot({\n position,\n children,\n}: {\n position: \"prefix\" | \"suffix\" | \"loading\";\n children: ReactNode;\n}) {\n return (\n <span data-icon={position} className={SLOT_CLASS}>\n {children}\n </span>\n );\n}\n\nfunction renderSlot(icon: Slottable, position: \"prefix\" | \"suffix\") {\n if (icon == null || icon === false) return null;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <Slot position={position}>\n <Icon />\n </Slot>\n );\n }\n if (isValidElement(icon)) return <Slot position={position}>{icon}</Slot>;\n return null;\n}\n\nfunction warnMissingLabel(svgOnly: boolean | undefined, label: unknown) {\n if (\n svgOnly &&\n !label &&\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.warn(\n \"Button: `svgOnly` buttons require an `aria-label` that names the action.\",\n );\n }\n}\n\n/* ======================== Motion: sheen + icon reveal ======================== */\n\n/** Framer source transition: `spring-physics 200 30 0.1`. */\nconst BUTTON_SPRING = {\n type: \"spring\",\n stiffness: 200,\n damping: 30,\n mass: 0.1,\n} as const;\n\nconst EFFECT_SHEEN_REST = {\n \"--pho-sheen-y\": \"0px\",\n \"--pho-sheen-blur\": \"5px\",\n \"--pho-sheen-spread\": \"2px\",\n} as const;\n\n/** Per-size sheen hover targets. */\nconst EFFECT_SHEEN_HOVER = {\n sm: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n },\n md: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n },\n lg: {\n \"--pho-sheen-y\": \"-6px\",\n \"--pho-sheen-blur\": \"14px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n} as const;\n\n/**\n * Per-size sheen press targets — the inverse gesture of hover: the light\n * drops back below the rest line and tightens while the pill compresses\n * slightly, so hover → press reads as \"lift, then push in\".\n */\nconst EFFECT_SHEEN_PRESS = {\n sm: {\n \"--pho-sheen-y\": \"1.5px\",\n \"--pho-sheen-blur\": \"4px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n md: {\n \"--pho-sheen-y\": \"1.5px\",\n \"--pho-sheen-blur\": \"4px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n lg: {\n \"--pho-sheen-y\": \"3px\",\n \"--pho-sheen-blur\": \"7px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n} as const;\n\n/** Pill compression while pressed — subtle, transform-only; every variant. */\nconst BUTTON_PRESS_SCALE = 0.97;\n\n/**\n * One-shot \"wake\" glint when an effect pill re-enables (disabled / loading →\n * interactive). The keyframes replay the pill's own gesture in reverse: the\n * sheen starts from its pressed-in position (low, tight — the dormant read),\n * springs up past the hover line, and settles at rest. Shadow-only, so the\n * pill never moves or resizes next to its neighbours.\n */\nconst EFFECT_SHEEN_WAKE = {\n sm: {\n \"--pho-sheen-y\": [\"1.5px\", \"-4px\", \"0px\"],\n \"--pho-sheen-blur\": [\"4px\", \"9px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"2px\", \"2px\"],\n },\n md: {\n \"--pho-sheen-y\": [\"1.5px\", \"-4px\", \"0px\"],\n \"--pho-sheen-blur\": [\"4px\", \"9px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"2px\", \"2px\"],\n },\n lg: {\n \"--pho-sheen-y\": [\"3px\", \"-8px\", \"0px\"],\n \"--pho-sheen-blur\": [\"7px\", \"16px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"1px\", \"2px\"],\n },\n} as const;\n\n/**\n * Wake timing: a short delay lets the 100ms opacity fade land first, then a\n * quick rise (feedback) and a relaxed settle. Keyframes need a tween — the\n * shared spring only animates between two values.\n */\nconst WAKE_TRANSITION = {\n duration: 0.6,\n delay: 0.05,\n times: [0, 0.4, 1],\n ease: [\"easeOut\", \"easeInOut\"],\n} as const;\n\n/**\n * Fire the wake glint on the disabled/loading → enabled edge (never on\n * mount), skipped under reduced motion. `data-waking` mirrors the window so\n * tests and styling can observe it.\n */\nfunction useWakeOnEnable(inert: boolean, sheen: boolean) {\n const [waking, setWaking] = useState(false);\n const prevInert = useRef(inert);\n const reduceMotion = useReducedMotion();\n\n useEffect(() => {\n if (prevInert.current && !inert && sheen && !reduceMotion) {\n setWaking(true);\n } else if (inert) {\n setWaking(false);\n }\n prevInert.current = inert;\n }, [inert, sheen, reduceMotion]);\n\n return {\n waking,\n onWakeComplete: (definition: unknown) => {\n if (definition === \"wake\") setWaking(false);\n },\n };\n}\n\n/**\n * Icon reveal sizes — collapsed at rest, full on hover. `lg` matches the\n * Framer 20×21 source; `sm`/`md` scale with the 14px label.\n */\nconst ICON_REVEAL_VARIANTS = {\n sm: {\n rest: { opacity: 0, width: 1, height: 9 },\n hover: { opacity: 1, width: 17, height: 18 },\n },\n md: {\n rest: { opacity: 0, width: 1, height: 9 },\n hover: { opacity: 1, width: 17, height: 18 },\n },\n lg: {\n rest: { opacity: 0, width: 1, height: 10 },\n hover: { opacity: 1, width: 20, height: 21 },\n },\n} as const;\n\n/**\n * When icons are hidden at rest, undo Button's icon-side padding tuck so the\n * pill doesn't look lopsided. Same `:has()` keys as the tuck (later wins).\n */\nconst ICON_REVEAL_PAD_RESTORE: Record<ButtonSize, string> = {\n sm: \"has-[[data-icon=prefix]]:pl-3.5 has-[[data-icon=loading]]:pl-3.5 has-[[data-icon=suffix]]:pr-3.5\",\n md: \"has-[[data-icon=prefix]]:pl-4 has-[[data-icon=loading]]:pl-4 has-[[data-icon=suffix]]:pr-4\",\n lg: \"has-[[data-icon=prefix]]:pl-5 has-[[data-icon=loading]]:pl-5 has-[[data-icon=suffix]]:pr-5\",\n};\n\nfunction RevealSlot({\n position,\n size,\n children,\n}: {\n position: \"prefix\" | \"suffix\";\n size: ButtonSize;\n children: ReactNode;\n}) {\n return (\n <motion.span\n data-icon={position}\n variants={ICON_REVEAL_VARIANTS[size]}\n transition={BUTTON_SPRING}\n style={{\n display: \"inline-flex\",\n flex: \"none\",\n alignItems: \"center\",\n overflow: \"hidden\",\n }}\n className=\"pointer-events-none [&>svg]:block [&>svg]:size-full\"\n >\n {children}\n </motion.span>\n );\n}\n\nfunction wrapRevealIcon(\n icon: Slottable,\n position: \"prefix\" | \"suffix\",\n size: ButtonSize,\n): ReactNode | undefined {\n if (icon == null || icon === false) return undefined;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <RevealSlot position={position} size={size}>\n <Icon />\n </RevealSlot>\n );\n }\n if (isValidElement(icon)) {\n return (\n <RevealSlot position={position} size={size}>\n {icon}\n </RevealSlot>\n );\n }\n return (\n <RevealSlot position={position} size={size}>\n {icon}\n </RevealSlot>\n );\n}\n\nfunction buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal,\n size,\n}: Pick<\n ButtonBaseProps,\n | \"svgOnly\"\n | \"loading\"\n | \"prefix\"\n | \"suffix\"\n | \"children\"\n | \"iconReveal\"\n | \"size\"\n> & { size: ButtonSize }) {\n if (svgOnly) {\n return loading ? <Spinner /> : children;\n }\n const reveal = Boolean(iconReveal);\n return (\n <>\n {loading ? (\n <Slot position=\"loading\">\n <Spinner />\n </Slot>\n ) : reveal ? (\n wrapRevealIcon(prefix, \"prefix\", size)\n ) : (\n renderSlot(prefix, \"prefix\")\n )}\n {children != null && children !== false && (\n <span data-text className=\"transition-inherit-all px-0.5\">\n {children}\n </span>\n )}\n {reveal\n ? wrapRevealIcon(suffix, \"suffix\", size)\n : renderSlot(suffix, \"suffix\")}\n </>\n );\n}\n\n/**\n * Root Motion props — press scale on every variant; sheen + icon-reveal gap\n * only when those features are on. Press composes over hover (`whileTap` >\n * `whileHover`): effect sheen inverts while the pill compresses; other\n * variants get the same scale with no sheen change. `waking` plays the\n * one-shot re-enable glint; a real hover mid-glint takes over seamlessly.\n */\nfunction buttonRootMotionProps(\n size: ButtonSize,\n inert: boolean | undefined,\n opts: { sheen: boolean; iconReveal: boolean; waking?: boolean },\n) {\n const rest: Record<string, string | number> = {\n scale: 1,\n ...(opts.sheen ? EFFECT_SHEEN_REST : {}),\n };\n const hover: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_HOVER[size] }\n : {};\n const press: Record<string, string | number> = {\n scale: BUTTON_PRESS_SCALE,\n ...(opts.sheen ? EFFECT_SHEEN_PRESS[size] : {}),\n };\n const wake = opts.sheen\n ? { ...EFFECT_SHEEN_WAKE[size], transition: WAKE_TRANSITION }\n : {};\n if (opts.iconReveal) {\n // Collapse gap at rest so the hidden icon leaves no hole; lg eases open.\n rest.gap = 0;\n hover.gap = size === \"lg\" ? 4 : 0;\n }\n return {\n initial: false as const,\n animate: opts.waking && opts.sheen ? \"wake\" : \"rest\",\n whileHover: inert ? undefined : \"hover\",\n whileTap: inert ? undefined : \"press\",\n variants: { rest, hover, press, wake },\n transition: BUTTON_SPRING,\n };\n}\n\nfunction buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className,\n}: {\n variant?: ButtonVariant;\n color?: ButtonColor;\n size?: ButtonSize;\n shape?: ButtonShape;\n loading?: boolean;\n svgOnly?: boolean;\n className?: string;\n}) {\n return cn(\n buttonVariants({ variant, color, size, shape }),\n loading && \"pointer-events-none\",\n svgOnly && \"[&>svg]:size-[1.25em] [&>svg]:shrink-0\",\n className,\n );\n}\n\n/**\n * Button — triggers an action that mutates state (submit, save, delete).\n * For navigation that changes the URL, use {@link ButtonLink}.\n *\n * Default surface is `effect` (same-hue 1.5px outer rim + inset sheen). Use\n * `filled` for a solid fill without those layers. `iconReveal` springs\n * prefix/suffix icons in on hover — independent of variant.\n */\nexport function Button({\n variant = \"effect\",\n color,\n size = \"md\",\n shape,\n prefix,\n suffix,\n iconReveal = false,\n svgOnly,\n loading,\n children,\n className,\n ref,\n ...props\n}: ButtonProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n const sheen = variant === \"effect\";\n const reveal = iconReveal && !svgOnly;\n const inert = Boolean(props.disabled || loading);\n const { waking, onWakeComplete } = useWakeOnEnable(inert, sheen);\n const cls = buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className: cn(reveal && ICON_REVEAL_PAD_RESTORE[size], className),\n });\n const inner = buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal: reveal,\n size,\n });\n\n return (\n <motion.button\n ref={ref}\n type=\"button\"\n {...buttonRootMotionProps(size, inert, {\n sheen,\n iconReveal: reveal,\n waking,\n })}\n onAnimationComplete={onWakeComplete}\n {...props}\n aria-busy={loading || undefined}\n data-loading={loading ? \"true\" : undefined}\n data-svg-only={svgOnly ? \"true\" : undefined}\n data-waking={waking || undefined}\n className={cls}\n >\n {inner}\n </motion.button>\n );\n}\n\nButton.displayName = \"Button\";\n\n/**\n * ButtonLink — a link styled as a button, for navigation that changes the URL.\n * Shares every visual prop with {@link Button}; renders the host router's link\n * via `LinkProvider`.\n */\nexport function ButtonLink({\n variant = \"effect\",\n color,\n size = \"md\",\n shape,\n prefix,\n suffix,\n iconReveal = false,\n svgOnly,\n loading,\n disabled,\n href,\n children,\n className,\n ...props\n}: ButtonLinkProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n const Link = useLinkComponent();\n const sheen = variant === \"effect\";\n const reveal = iconReveal && !svgOnly;\n const cls = buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className: cn(reveal && ICON_REVEAL_PAD_RESTORE[size], className),\n });\n const inner = buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal: reveal,\n size,\n });\n // Hooks must run unconditionally — only used when Motion is needed.\n const MotionLink = useMemo(\n // LinkProvider requires `href`; motion.create needs an open prop record —\n // bridge the two through `unknown` (props are supplied at the call site).\n () =>\n motion.create(Link as unknown as ComponentType<Record<string, unknown>>),\n [Link],\n );\n\n const inert = Boolean(disabled || loading);\n const { waking, onWakeComplete } = useWakeOnEnable(inert, sheen);\n const shared = {\n ...props,\n ...(inert ? {} : { href }),\n \"aria-disabled\": inert || undefined,\n \"aria-busy\": loading || undefined,\n \"data-loading\": loading ? (\"true\" as const) : undefined,\n \"data-svg-only\": svgOnly ? (\"true\" as const) : undefined,\n className: cls,\n tabIndex: inert ? -1 : props.tabIndex,\n onClick: inert\n ? (event: ReactMouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n }\n : props.onClick,\n };\n\n if (inert) {\n return <span {...shared}>{inner}</span>;\n }\n\n return (\n <MotionLink\n {...buttonRootMotionProps(size, inert, {\n sheen,\n iconReveal: reveal,\n waking,\n })}\n onAnimationComplete={onWakeComplete}\n data-waking={waking || undefined}\n {...shared}\n >\n {inner}\n </MotionLink>\n );\n}\n\nButtonLink.displayName = \"ButtonLink\";\n\n/* ======================= EffectButton (compat alias) ======================= */\n\nexport const EFFECT_BUTTON_SIZES = BUTTON_SIZES;\nexport type EffectButtonSize = ButtonSize;\n\n/** Compose classes for an effect pill — `Button` with `variant=\"effect\"`. */\nexport function effectButtonVariants(opts?: {\n size?: EffectButtonSize;\n fullWidth?: boolean;\n className?: string;\n}): string {\n const { size = \"md\", fullWidth, className } = opts ?? {};\n return buttonVariants({\n variant: \"effect\",\n size,\n className: cn(fullWidth && \"w-full\", className),\n });\n}\n\n/**\n * Legacy EffectButton also reserved `style` (sheen variables lived there) and\n * never exposed the HTML `color` attribute — Button's semantic `color` prop\n * now occupies that name.\n */\ntype MotionConflicts = MotionHandlerConflicts | \"style\" | \"color\";\n\ninterface EffectButtonBaseProps {\n /** Size variant — Button's scale (32 / 36 / 44px). @default \"md\" */\n size?: EffectButtonSize;\n /** Stretch to the container's full width (stacked login / OAuth pills). */\n fullWidth?: boolean;\n /**\n * @deprecated Use `iconReveal` on `Button` instead. Mapped to `iconReveal`\n * when set.\n */\n animated?: boolean;\n /** @see ButtonBaseProps.iconReveal */\n iconReveal?: boolean;\n prefix?: Slottable;\n suffix?: Slottable;\n svgOnly?: boolean;\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\nexport interface EffectButtonProps\n extends\n EffectButtonBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof EffectButtonBaseProps | MotionConflicts\n > {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface EffectButtonLinkProps\n extends\n EffectButtonBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof EffectButtonBaseProps | MotionConflicts\n > {\n href: string;\n disabled?: boolean;\n}\n\n/**\n * @deprecated Prefer `<Button variant=\"effect\">`. Thin alias that forwards to\n * Button with the sheen surface (no icon reveal).\n */\nexport function EffectButton({\n size = \"md\",\n fullWidth,\n animated,\n iconReveal,\n className,\n ...props\n}: EffectButtonProps) {\n return (\n <Button\n variant=\"effect\"\n size={size}\n iconReveal={iconReveal ?? animated ?? true}\n className={cn(fullWidth && \"w-full\", className)}\n {...props}\n />\n );\n}\n\nEffectButton.displayName = \"EffectButton\";\n\n/**\n * @deprecated Prefer `<ButtonLink variant=\"effect\">`.\n */\nexport function EffectButtonLink({\n size = \"md\",\n fullWidth,\n animated,\n iconReveal,\n className,\n ...props\n}: EffectButtonLinkProps) {\n return (\n <ButtonLink\n variant=\"effect\"\n size={size}\n iconReveal={iconReveal ?? animated ?? true}\n className={cn(fullWidth && \"w-full\", className)}\n {...props}\n />\n );\n}\n\nEffectButtonLink.displayName = \"EffectButtonLink\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"button-BmhyLgy5.js","names":[],"sources":["../../src/components/button/button.tsx"],"sourcesContent":["import {\n isValidElement,\n useEffect,\n useMemo,\n useRef,\n useState,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type ComponentType,\n type FC,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\nimport { isReactComponent } from \"../../utils/is-react-component\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\nimport { Spinner } from \"../spinner/spinner\";\n\n/**\n * Design notes:\n * - TWO visual axes: `variant` is the surface (filled / outlined / ghost /\n * effect) and `color` is the semantic ink (default / destructive). Dialog\n * confirms use the default `effect`; danger-zone triggers are `ghost` +\n * `destructive`. `effect` is the marketing pill: same-hue 1.5px outer rim\n * plus inset sheen that springs on hover.\n * - `iconReveal` is a separate boolean: on hover, prefix/suffix icons spring\n * in from collapsed — works with any variant, independent of `effect`.\n * - Press scale (Motion `whileTap` → 0.97) is shared by every variant; only\n * `effect` also springs its inset sheen on hover/press, and replays a\n * one-shot sheen glint when it leaves `disabled`/`loading` (wake).\n * - THREE sizes (`sm` / `md` / `lg`, default `md`). No `xs` / `xl`.\n * - `prefix` / `suffix` are generic slots (icon, spinner, count…), not\n * icon-only props. `svgOnly` makes an icon button and *requires* an\n * `aria-label`. `loading` is declarative. Effect’s rim/sheen are\n * box-shadow layers (not layout `border`).\n * - Navigation belongs to `ButtonLink`; an inline text link is an anchor.\n */\n\nconst COMMON_ROOT = cn(\n // Transition colors/shadows in CSS; transform (press scale) is owned by Motion.\n \"group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-pho-brand transition-[color,background-color,border-color,opacity,box-shadow,outline-color] duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2\",\n // Disabled (native `disabled` on <button>, `aria-disabled` on links).\n \"disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none\",\n);\n\n/**\n * Size axis — padding, text size, and icon-only (svgOnly) padding.\n *\n * Icon-side tuck (LiftKit-derived optical correction): a side that carries an\n * icon gets its padding divided by the golden ratio — an icon's rounded form\n * brings its own whitespace, so the full text-edge padding reads as a hole.\n * LiftKit: `--button-padX-sideWithIcon: calc(font-size / 1.618)`. Applied to\n * Pho's paddings: sm 14px/φ ≈ 8px · md 16px/φ ≈ 10px · lg 20px/φ ≈ 12px,\n * selected structurally via `:has()` on the slot markers (`data-icon` is\n * `prefix`, `suffix`, or `loading` — the spinner is an icon too).\n */\nexport const BUTTON_SIZES = {\n sm: {\n classes: cn(\n \"gap-1 px-3.5 py-1.5 text-base font-medium data-svg-only:p-2\",\n \"has-[[data-icon=prefix]]:pl-2 has-[[data-icon=loading]]:pl-2 has-[[data-icon=suffix]]:pr-2\",\n ),\n description: \"Small — compact rows, tables, and toolbars\",\n },\n md: {\n classes: cn(\n \"gap-1 px-4 py-2 text-base font-medium data-svg-only:p-2.5\",\n \"has-[[data-icon=prefix]]:pl-2.5 has-[[data-icon=loading]]:pl-2.5 has-[[data-icon=suffix]]:pr-2.5\",\n ),\n description: \"Medium — the default\",\n },\n lg: {\n classes: cn(\n \"gap-1.5 px-5 py-2.5 text-md font-medium data-svg-only:p-3\",\n \"has-[[data-icon=prefix]]:pl-3 has-[[data-icon=loading]]:pl-3 has-[[data-icon=suffix]]:pr-3\",\n ),\n description: \"Large — prominent and marketing actions\",\n },\n} satisfies VariantMap;\n\n/** Shape axis — corner treatment. Pills are the photon.codes default. */\nexport const BUTTON_SHAPES = {\n rounded: {\n classes: \"rounded-full\",\n description: \"Pill — the default; every button on photon.codes\",\n },\n square: {\n classes: \"rounded-base\",\n description: \"Gently rounded corners for dense app chrome\",\n },\n circle: {\n classes: \"rounded-full\",\n description: \"Circular — pair with svgOnly for round icon buttons\",\n },\n} satisfies VariantMap;\n\n/**\n * At `lg`, square buttons add squircle smoothing on top of `rounded-base`\n * (12px) — the same treatment as Input `lg`, so 44px buttons and fields share\n * a corner language. 12/44 ≈ 0.272 — LiftKit's half-step decimal (√φ − 1).\n */\nconst SQUARE_LG_RADIUS = \"squircle\";\n\n/**\n * Optical pad for `effect`: the 1.5px outer rim is a box-shadow (no layout\n * cost), so without this the painted height reads ~3px taller than filled /\n * outlined / ghost. Subtract 1.5px from each side so fill+rim matches siblings.\n */\nconst EFFECT_OPTICAL_PAD: Record<ButtonSize, string> = {\n sm: \"py-[4.5px] data-svg-only:p-[6.5px]\",\n md: \"py-[6.5px] data-svg-only:p-[8.5px]\",\n lg: \"py-[8.5px] data-svg-only:p-[10.5px]\",\n};\n\n/** Surface axis — how the control is drawn. `effect` is the default. */\nexport const BUTTON_VARIANTS = {\n effect: {\n classes: \"\",\n description:\n \"Default — marketing pill with same-hue outer rim and inset sheen\",\n },\n filled: {\n classes: \"\",\n description: \"Solid fill without sheen\",\n },\n outlined: {\n classes: \"\",\n description: \"Hairline ring — supporting action\",\n },\n ghost: {\n classes: \"\",\n description: \"Borderless — quiet / inline action\",\n },\n} satisfies VariantMap;\n\n/** Color axis — semantic ink. `default` is the default. */\nexport const BUTTON_COLORS = {\n default: {\n classes: \"\",\n description: \"Neutral / brand contrast\",\n },\n destructive: {\n classes: \"\",\n description: \"Destructive intent (delete, discard)\",\n },\n} satisfies VariantMap;\n\n/**\n * Surface × color looks. Kept as a matrix so each cell can own its tokens\n * (filled destructive is solid red; ghost destructive is red text only).\n */\nconst BUTTON_LOOKS: Record<\n keyof typeof BUTTON_VARIANTS,\n Record<keyof typeof BUTTON_COLORS, string>\n> = {\n // Icons inherit the label ink (currentColor) — no separate opacity/tint.\n // The rim replicates photon.codes' Framer CTA border: 1.5px in the fill hue\n // at 62% alpha, reading as a soft same-hue edge (the site has NO outer\n // glow — verified against live computed styles). Sheen geometry is CSS\n // vars that Motion springs on hover (see Button). All edges are shadows —\n // never a layout `border` — so height stays paired with other variants.\n effect: {\n default: cn(\n \"squircle overflow-hidden bg-pho-brand-solid text-pho-on-brand transition-none\",\n \"hover:bg-pho-brand-solid data-loading:bg-pho-brand-solid\",\n \"[--pho-rim:color-mix(in_oklab,var(--background-color-pho-brand-solid)_62%,transparent)]\",\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.5),rgba(0,0,0,0.12))]\",\n \"shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_var(--border-color-pho-on-brand),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]\",\n ),\n destructive: cn(\n \"squircle overflow-hidden bg-pho-error-solid text-white outline-pho-error transition-none\",\n \"hover:bg-pho-error-solid data-loading:bg-pho-error-solid\",\n \"[--pho-rim:color-mix(in_oklab,var(--background-color-pho-error-solid)_62%,transparent)]\",\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.45),rgba(0,0,0,0.18))]\",\n \"shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_oklch(1_0_0/0.2),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]\",\n ),\n },\n filled: {\n default:\n \"bg-pho-brand-solid text-pho-on-brand hover:bg-pho-brand-solid-hover data-loading:bg-pho-brand-solid-hover\",\n destructive:\n \"bg-pho-error-solid text-white outline-pho-error hover:bg-pho-error-solid-hover data-loading:bg-pho-error-solid-hover\",\n },\n outlined: {\n default:\n // Rest hairline is the card stroke; hover climbs to primary ink so the\n // edge reads as black (white in dark), matching the label's hover lift.\n // The ring spans the full ladder, so ease it in gently — 200ms ease-out\n // approximates the effect variant's soft spring instead of base's\n // 100ms linear snap. Prefer the named `ring-pho-ink` token over an\n // arbitrary value so consumers' Tailwind scanners pick it up reliably.\n \"bg-pho-primary text-pho-secondary ring-1 ring-pho-secondary ring-inset duration-200 ease-out hover:bg-pho-primary-hover hover:text-pho-secondary-hover hover:ring-pho-ink data-loading:bg-pho-primary-hover\",\n destructive:\n \"bg-pho-primary text-pho-error outline-pho-error ring-1 ring-pho-error ring-inset duration-200 ease-out hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary\",\n },\n ghost: {\n default:\n \"text-pho-secondary hover:bg-pho-ghost-hover hover:text-pho-secondary-hover data-loading:bg-pho-ghost-hover\",\n destructive:\n \"text-pho-error outline-pho-error hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary\",\n },\n};\n\nexport type ButtonSize = keyof typeof BUTTON_SIZES;\nexport type ButtonShape = keyof typeof BUTTON_SHAPES;\nexport type ButtonVariant = keyof typeof BUTTON_VARIANTS;\nexport type ButtonColor = keyof typeof BUTTON_COLORS;\n\n/** Compose the full class string for a button — exported for reuse/testing. */\nexport function buttonVariants(opts?: {\n variant?: ButtonVariant;\n color?: ButtonColor;\n size?: ButtonSize;\n shape?: ButtonShape;\n className?: string;\n}): string {\n const {\n variant = \"effect\",\n color = \"default\",\n size = \"md\",\n shape: shapeOpt = \"rounded\",\n className,\n } = opts ?? {};\n // Effect pills are always fully rounded — shape is ignored.\n const shape = variant === \"effect\" ? \"rounded\" : shapeOpt;\n const look =\n BUTTON_LOOKS[variant as keyof typeof BUTTON_LOOKS]?.[\n color as keyof (typeof BUTTON_LOOKS)[\"effect\"]\n ] ?? \"\";\n return cn(\n COMMON_ROOT,\n resolveVariant(BUTTON_SHAPES, shape),\n shape === \"square\" && size === \"lg\" && SQUARE_LG_RADIUS,\n resolveVariant(BUTTON_SIZES, size),\n // Outer rim is a 1.5px box-shadow (no layout cost). Shrink padding by the\n // same amount so fill+rim paints at the same visual size as filled /\n // outlined / ghost siblings of the same `size`.\n variant === \"effect\" && EFFECT_OPTICAL_PAD[size],\n look,\n className,\n );\n}\n\n/** An icon/element slot rendered before or after the label. */\ntype Slottable = ReactNode | FC<{ className?: string }>;\n\ninterface ButtonBaseProps {\n /** Surface variant. @default \"effect\" */\n variant?: ButtonVariant;\n /** Semantic color. @default \"default\" */\n color?: ButtonColor;\n /** Size variant. @default \"md\" */\n size?: ButtonSize;\n /** Corner shape. @default \"rounded\" */\n shape?: ButtonShape;\n /** Slot before the label — a component (`Plus`) or element (`<Plus />`). */\n prefix?: Slottable;\n /** Slot after the label. */\n suffix?: Slottable;\n /**\n * Reveal prefix/suffix icons on hover — collapsed and invisible at rest,\n * spring in on hover. Independent of `variant` (including `effect`).\n * Ignored when `svgOnly`. @default false\n */\n iconReveal?: boolean;\n /** Render as an icon-only button. Requires an `aria-label`. */\n svgOnly?: boolean;\n /** Show a spinner and block interaction while staying readable. */\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * DOM event handlers whose React signatures clash with Motion's gesture\n * props of the same name (Motion re-types these on `motion.*` elements).\n * Excluded from the public API — Button renders through Motion when the\n * sheen or icon reveal is active, so these handlers would be intercepted.\n */\ntype MotionHandlerConflicts =\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onDragEnter\"\n | \"onDragExit\"\n | \"onDragLeave\"\n | \"onDragOver\"\n | \"onDrop\";\n\nexport interface ButtonProps\n extends\n ButtonBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof ButtonBaseProps | MotionHandlerConflicts\n > {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface ButtonLinkProps\n extends\n ButtonBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof ButtonBaseProps | MotionHandlerConflicts\n > {\n /** Navigate to this URL (rendered via the host's `LinkProvider`). */\n href: string;\n /** Visually and semantically disable the link. */\n disabled?: boolean;\n}\n\nconst SLOT_CLASS =\n // Icons scale with the label: 1.125em tracks the button's font size\n // (16px icon beside 14px text, 18px beside 16px) so small buttons never\n // carry an oversized glyph.\n \"transition-inherit-all pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-[1.125em] [&>svg]:shrink-0\";\n\nfunction Slot({\n position,\n children,\n}: {\n position: \"prefix\" | \"suffix\" | \"loading\";\n children: ReactNode;\n}) {\n return (\n <span data-icon={position} className={SLOT_CLASS}>\n {children}\n </span>\n );\n}\n\nfunction renderSlot(icon: Slottable, position: \"prefix\" | \"suffix\") {\n if (icon == null || icon === false) return null;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <Slot position={position}>\n <Icon />\n </Slot>\n );\n }\n if (isValidElement(icon)) return <Slot position={position}>{icon}</Slot>;\n return null;\n}\n\nfunction warnMissingLabel(svgOnly: boolean | undefined, label: unknown) {\n if (\n svgOnly &&\n !label &&\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.warn(\n \"Button: `svgOnly` buttons require an `aria-label` that names the action.\",\n );\n }\n}\n\n/* ======================== Motion: sheen + icon reveal ======================== */\n\n/** Framer source transition: `spring-physics 200 30 0.1`. */\nconst BUTTON_SPRING = {\n type: \"spring\",\n stiffness: 200,\n damping: 30,\n mass: 0.1,\n} as const;\n\nconst EFFECT_SHEEN_REST = {\n \"--pho-sheen-y\": \"0px\",\n \"--pho-sheen-blur\": \"5px\",\n \"--pho-sheen-spread\": \"2px\",\n} as const;\n\n/** Per-size sheen hover targets. */\nconst EFFECT_SHEEN_HOVER = {\n sm: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n },\n md: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n },\n lg: {\n \"--pho-sheen-y\": \"-6px\",\n \"--pho-sheen-blur\": \"14px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n} as const;\n\n/**\n * Per-size sheen press targets — the inverse gesture of hover: the light\n * drops back below the rest line and tightens while the pill compresses\n * slightly, so hover → press reads as \"lift, then push in\".\n */\nconst EFFECT_SHEEN_PRESS = {\n sm: {\n \"--pho-sheen-y\": \"1.5px\",\n \"--pho-sheen-blur\": \"4px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n md: {\n \"--pho-sheen-y\": \"1.5px\",\n \"--pho-sheen-blur\": \"4px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n lg: {\n \"--pho-sheen-y\": \"3px\",\n \"--pho-sheen-blur\": \"7px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n} as const;\n\n/** Pill compression while pressed — subtle, transform-only; every variant. */\nconst BUTTON_PRESS_SCALE = 0.97;\n\n/**\n * One-shot \"wake\" glint when an effect pill re-enables (disabled / loading →\n * interactive). The keyframes replay the pill's own gesture in reverse: the\n * sheen starts from its pressed-in position (low, tight — the dormant read),\n * springs up past the hover line, and settles at rest. Shadow-only, so the\n * pill never moves or resizes next to its neighbours.\n */\nconst EFFECT_SHEEN_WAKE = {\n sm: {\n \"--pho-sheen-y\": [\"1.5px\", \"-4px\", \"0px\"],\n \"--pho-sheen-blur\": [\"4px\", \"9px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"2px\", \"2px\"],\n },\n md: {\n \"--pho-sheen-y\": [\"1.5px\", \"-4px\", \"0px\"],\n \"--pho-sheen-blur\": [\"4px\", \"9px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"2px\", \"2px\"],\n },\n lg: {\n \"--pho-sheen-y\": [\"3px\", \"-8px\", \"0px\"],\n \"--pho-sheen-blur\": [\"7px\", \"16px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"1px\", \"2px\"],\n },\n} as const;\n\n/**\n * Wake timing: a short delay lets the 100ms opacity fade land first, then a\n * quick rise (feedback) and a relaxed settle. Keyframes need a tween — the\n * shared spring only animates between two values.\n */\nconst WAKE_TRANSITION = {\n duration: 0.6,\n delay: 0.05,\n times: [0, 0.4, 1],\n ease: [\"easeOut\", \"easeInOut\"],\n} as const;\n\n/**\n * Fire the wake glint on the disabled/loading → enabled edge (never on\n * mount), skipped under reduced motion. `data-waking` mirrors the window so\n * tests and styling can observe it.\n */\nfunction useWakeOnEnable(inert: boolean, sheen: boolean) {\n const [waking, setWaking] = useState(false);\n const prevInert = useRef(inert);\n const reduceMotion = useReducedMotion();\n\n useEffect(() => {\n if (prevInert.current && !inert && sheen && !reduceMotion) {\n setWaking(true);\n } else if (inert) {\n setWaking(false);\n }\n prevInert.current = inert;\n }, [inert, sheen, reduceMotion]);\n\n return {\n waking,\n onWakeComplete: (definition: unknown) => {\n if (definition === \"wake\") setWaking(false);\n },\n };\n}\n\n/**\n * Icon reveal sizes — collapsed at rest, full on hover. `lg` matches the\n * Framer 20×21 source; `sm`/`md` scale with the 14px label.\n */\nconst ICON_REVEAL_VARIANTS = {\n sm: {\n rest: { opacity: 0, width: 1, height: 9 },\n hover: { opacity: 1, width: 17, height: 18 },\n },\n md: {\n rest: { opacity: 0, width: 1, height: 9 },\n hover: { opacity: 1, width: 17, height: 18 },\n },\n lg: {\n rest: { opacity: 0, width: 1, height: 10 },\n hover: { opacity: 1, width: 20, height: 21 },\n },\n} as const;\n\n/**\n * When icons are hidden at rest, undo Button's icon-side padding tuck so the\n * pill doesn't look lopsided. Same `:has()` keys as the tuck (later wins).\n */\nconst ICON_REVEAL_PAD_RESTORE: Record<ButtonSize, string> = {\n sm: \"has-[[data-icon=prefix]]:pl-3.5 has-[[data-icon=loading]]:pl-3.5 has-[[data-icon=suffix]]:pr-3.5\",\n md: \"has-[[data-icon=prefix]]:pl-4 has-[[data-icon=loading]]:pl-4 has-[[data-icon=suffix]]:pr-4\",\n lg: \"has-[[data-icon=prefix]]:pl-5 has-[[data-icon=loading]]:pl-5 has-[[data-icon=suffix]]:pr-5\",\n};\n\nfunction RevealSlot({\n position,\n size,\n children,\n}: {\n position: \"prefix\" | \"suffix\";\n size: ButtonSize;\n children: ReactNode;\n}) {\n return (\n <motion.span\n data-icon={position}\n variants={ICON_REVEAL_VARIANTS[size]}\n transition={BUTTON_SPRING}\n style={{\n display: \"inline-flex\",\n flex: \"none\",\n alignItems: \"center\",\n overflow: \"hidden\",\n }}\n className=\"pointer-events-none [&>svg]:block [&>svg]:size-full\"\n >\n {children}\n </motion.span>\n );\n}\n\nfunction wrapRevealIcon(\n icon: Slottable,\n position: \"prefix\" | \"suffix\",\n size: ButtonSize,\n): ReactNode | undefined {\n if (icon == null || icon === false) return undefined;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <RevealSlot position={position} size={size}>\n <Icon />\n </RevealSlot>\n );\n }\n if (isValidElement(icon)) {\n return (\n <RevealSlot position={position} size={size}>\n {icon}\n </RevealSlot>\n );\n }\n return (\n <RevealSlot position={position} size={size}>\n {icon}\n </RevealSlot>\n );\n}\n\nfunction buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal,\n size,\n}: Pick<\n ButtonBaseProps,\n | \"svgOnly\"\n | \"loading\"\n | \"prefix\"\n | \"suffix\"\n | \"children\"\n | \"iconReveal\"\n | \"size\"\n> & { size: ButtonSize }) {\n if (svgOnly) {\n return loading ? <Spinner /> : children;\n }\n const reveal = Boolean(iconReveal);\n return (\n <>\n {loading ? (\n <Slot position=\"loading\">\n <Spinner />\n </Slot>\n ) : reveal ? (\n wrapRevealIcon(prefix, \"prefix\", size)\n ) : (\n renderSlot(prefix, \"prefix\")\n )}\n {children != null && children !== false && (\n <span data-text className=\"transition-inherit-all px-0.5\">\n {children}\n </span>\n )}\n {reveal\n ? wrapRevealIcon(suffix, \"suffix\", size)\n : renderSlot(suffix, \"suffix\")}\n </>\n );\n}\n\n/**\n * Root Motion props — press scale on every variant; sheen + icon-reveal gap\n * only when those features are on. Press composes over hover (`whileTap` >\n * `whileHover`): effect sheen inverts while the pill compresses; other\n * variants get the same scale with no sheen change. `waking` plays the\n * one-shot re-enable glint; a real hover mid-glint takes over seamlessly.\n */\nfunction buttonRootMotionProps(\n size: ButtonSize,\n inert: boolean | undefined,\n opts: { sheen: boolean; iconReveal: boolean; waking?: boolean },\n) {\n const rest: Record<string, string | number> = {\n scale: 1,\n ...(opts.sheen ? EFFECT_SHEEN_REST : {}),\n };\n const hover: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_HOVER[size] }\n : {};\n const press: Record<string, string | number> = {\n scale: BUTTON_PRESS_SCALE,\n ...(opts.sheen ? EFFECT_SHEEN_PRESS[size] : {}),\n };\n const wake = opts.sheen\n ? { ...EFFECT_SHEEN_WAKE[size], transition: WAKE_TRANSITION }\n : {};\n if (opts.iconReveal) {\n // Collapse gap at rest so the hidden icon leaves no hole; lg eases open.\n rest.gap = 0;\n hover.gap = size === \"lg\" ? 4 : 0;\n }\n return {\n initial: false as const,\n animate: opts.waking && opts.sheen ? \"wake\" : \"rest\",\n whileHover: inert ? undefined : \"hover\",\n whileTap: inert ? undefined : \"press\",\n variants: { rest, hover, press, wake },\n transition: BUTTON_SPRING,\n };\n}\n\nfunction buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className,\n}: {\n variant?: ButtonVariant;\n color?: ButtonColor;\n size?: ButtonSize;\n shape?: ButtonShape;\n loading?: boolean;\n svgOnly?: boolean;\n className?: string;\n}) {\n return cn(\n buttonVariants({ variant, color, size, shape }),\n loading && \"pointer-events-none\",\n svgOnly && \"[&>svg]:size-[1.25em] [&>svg]:shrink-0\",\n className,\n );\n}\n\n/**\n * Button — triggers an action that mutates state (submit, save, delete).\n * For navigation that changes the URL, use {@link ButtonLink}.\n *\n * Default surface is `effect` (same-hue 1.5px outer rim + inset sheen). Use\n * `filled` for a solid fill without those layers. `iconReveal` springs\n * prefix/suffix icons in on hover — independent of variant.\n */\nexport function Button({\n variant = \"effect\",\n color,\n size = \"md\",\n shape,\n prefix,\n suffix,\n iconReveal = false,\n svgOnly,\n loading,\n children,\n className,\n ref,\n ...props\n}: ButtonProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n const sheen = variant === \"effect\";\n const reveal = iconReveal && !svgOnly;\n const inert = Boolean(props.disabled || loading);\n const { waking, onWakeComplete } = useWakeOnEnable(inert, sheen);\n const cls = buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className: cn(reveal && ICON_REVEAL_PAD_RESTORE[size], className),\n });\n const inner = buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal: reveal,\n size,\n });\n\n return (\n <motion.button\n ref={ref}\n type=\"button\"\n {...buttonRootMotionProps(size, inert, {\n sheen,\n iconReveal: reveal,\n waking,\n })}\n onAnimationComplete={onWakeComplete}\n {...props}\n aria-busy={loading || undefined}\n data-loading={loading ? \"true\" : undefined}\n data-svg-only={svgOnly ? \"true\" : undefined}\n data-waking={waking || undefined}\n className={cls}\n >\n {inner}\n </motion.button>\n );\n}\n\nButton.displayName = \"Button\";\n\n/**\n * ButtonLink — a link styled as a button, for navigation that changes the URL.\n * Shares every visual prop with {@link Button}; renders the host router's link\n * via `LinkProvider`.\n */\nexport function ButtonLink({\n variant = \"effect\",\n color,\n size = \"md\",\n shape,\n prefix,\n suffix,\n iconReveal = false,\n svgOnly,\n loading,\n disabled,\n href,\n children,\n className,\n ...props\n}: ButtonLinkProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n const Link = useLinkComponent();\n const sheen = variant === \"effect\";\n const reveal = iconReveal && !svgOnly;\n const cls = buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className: cn(reveal && ICON_REVEAL_PAD_RESTORE[size], className),\n });\n const inner = buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal: reveal,\n size,\n });\n // Hooks must run unconditionally — only used when Motion is needed.\n const MotionLink = useMemo(\n // LinkProvider requires `href`; motion.create needs an open prop record —\n // bridge the two through `unknown` (props are supplied at the call site).\n () =>\n motion.create(Link as unknown as ComponentType<Record<string, unknown>>),\n [Link],\n );\n\n const inert = Boolean(disabled || loading);\n const { waking, onWakeComplete } = useWakeOnEnable(inert, sheen);\n const shared = {\n ...props,\n ...(inert ? {} : { href }),\n \"aria-disabled\": inert || undefined,\n \"aria-busy\": loading || undefined,\n \"data-loading\": loading ? (\"true\" as const) : undefined,\n \"data-svg-only\": svgOnly ? (\"true\" as const) : undefined,\n className: cls,\n tabIndex: inert ? -1 : props.tabIndex,\n onClick: inert\n ? (event: ReactMouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n }\n : props.onClick,\n };\n\n if (inert) {\n return <span {...shared}>{inner}</span>;\n }\n\n return (\n <MotionLink\n {...buttonRootMotionProps(size, inert, {\n sheen,\n iconReveal: reveal,\n waking,\n })}\n onAnimationComplete={onWakeComplete}\n data-waking={waking || undefined}\n {...shared}\n >\n {inner}\n </MotionLink>\n );\n}\n\nButtonLink.displayName = \"ButtonLink\";\n\n/* ======================= EffectButton (compat alias) ======================= */\n\nexport const EFFECT_BUTTON_SIZES = BUTTON_SIZES;\nexport type EffectButtonSize = ButtonSize;\n\n/** Compose classes for an effect pill — `Button` with `variant=\"effect\"`. */\nexport function effectButtonVariants(opts?: {\n size?: EffectButtonSize;\n fullWidth?: boolean;\n className?: string;\n}): string {\n const { size = \"md\", fullWidth, className } = opts ?? {};\n return buttonVariants({\n variant: \"effect\",\n size,\n className: cn(fullWidth && \"w-full\", className),\n });\n}\n\n/**\n * Legacy EffectButton also reserved `style` (sheen variables lived there) and\n * never exposed the HTML `color` attribute — Button's semantic `color` prop\n * now occupies that name.\n */\ntype MotionConflicts = MotionHandlerConflicts | \"style\" | \"color\";\n\ninterface EffectButtonBaseProps {\n /** Size variant — Button's scale (32 / 36 / 44px). @default \"md\" */\n size?: EffectButtonSize;\n /** Stretch to the container's full width (stacked login / OAuth pills). */\n fullWidth?: boolean;\n /**\n * @deprecated Use `iconReveal` on `Button` instead. Mapped to `iconReveal`\n * when set.\n */\n animated?: boolean;\n /** @see ButtonBaseProps.iconReveal */\n iconReveal?: boolean;\n prefix?: Slottable;\n suffix?: Slottable;\n svgOnly?: boolean;\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\nexport interface EffectButtonProps\n extends\n EffectButtonBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof EffectButtonBaseProps | MotionConflicts\n > {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface EffectButtonLinkProps\n extends\n EffectButtonBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof EffectButtonBaseProps | MotionConflicts\n > {\n href: string;\n disabled?: boolean;\n}\n\n/**\n * @deprecated Prefer `<Button variant=\"effect\">`. Thin alias that forwards to\n * Button with the sheen surface (no icon reveal).\n */\nexport function EffectButton({\n size = \"md\",\n fullWidth,\n animated,\n iconReveal,\n className,\n ...props\n}: EffectButtonProps) {\n return (\n <Button\n variant=\"effect\"\n size={size}\n iconReveal={iconReveal ?? animated ?? true}\n className={cn(fullWidth && \"w-full\", className)}\n {...props}\n />\n );\n}\n\nEffectButton.displayName = \"EffectButton\";\n\n/**\n * @deprecated Prefer `<ButtonLink variant=\"effect\">`.\n */\nexport function EffectButtonLink({\n size = \"md\",\n fullWidth,\n animated,\n iconReveal,\n className,\n ...props\n}: EffectButtonLinkProps) {\n return (\n <ButtonLink\n variant=\"effect\"\n size={size}\n iconReveal={iconReveal ?? animated ?? true}\n className={cn(fullWidth && \"w-full\", className)}\n {...props}\n />\n );\n}\n\nEffectButtonLink.displayName = \"EffectButtonLink\";\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { t as r } from "./cn-
|
|
2
|
+
import { t as r } from "./cn-ChIgwEl3.js";
|
|
3
3
|
import "react";
|
|
4
4
|
import { jsx as o } from "react/jsx-runtime";
|
|
5
5
|
var a = "border-pho-secondary bg-pho-primary rounded-lg border";
|
|
@@ -53,4 +53,4 @@ export {
|
|
|
53
53
|
a as t
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
//# sourceMappingURL=card-
|
|
56
|
+
//# sourceMappingURL=card-DSW3Uhag.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-
|
|
1
|
+
{"version":3,"file":"card-DSW3Uhag.js","names":[],"sources":["../../src/components/card/card.tsx"],"sourcesContent":["import { type ComponentProps, type Ref } from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/**\n * Design notes:\n * - A card is a flat bordered panel on the page surface — hairline border,\n * primary background, no shadow. Elevation shadows stay on floating layers\n * (menus, dialogs); see the same rule on Input and Select.\n * - Radius sits at the `lg` stop (16px) like Dialog — a standalone content\n * panel reads bigger than a control, so it takes the panel radius rather\n * than the control default (`base`).\n * - Vertical rhythm: `pt-5` / `pb-4`, `gap-4` between parts. Parts carry\n * `px-5`, so a child without padding (an image, a table, a `Separator`)\n * runs edge-to-edge on its own.\n * - No size or color axes. A card is chrome, not a signal — content sets the\n * tone. Sizing belongs to the layout that places the card.\n */\n\n/** Surface chrome — exported for hosts that need the look on another element. */\nexport const CARD_SURFACE =\n \"border-pho-secondary bg-pho-primary rounded-lg border\";\n\n/** The panel: bordered surface, vertical rhythm between parts. */\nfunction Root({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n return (\n <div\n className={cn(CARD_SURFACE, \"flex flex-col gap-4 pt-5 pb-4\", className)}\n {...props}\n />\n );\n}\n\n/** Title + optional description pair, kept tight (`gap-0.5`). */\nfunction Header({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n return (\n <div className={cn(\"flex flex-col gap-0.5 px-5\", className)} {...props} />\n );\n}\n\n/**\n * Card heading — an `<h3>` so it nests under page (`h1`) and section (`h2`)\n * headings without skipping levels.\n */\nfunction Title({\n className,\n children,\n ...props\n}: ComponentProps<\"h3\"> & { ref?: Ref<HTMLHeadingElement> }) {\n return (\n <h3\n className={cn(\"text-display-xs text-pho-primary font-medium\", className)}\n {...props}\n >\n {children}\n </h3>\n );\n}\n\n/** Supporting copy under the title — same step, secondary tone. */\nfunction Description({\n className,\n ...props\n}: ComponentProps<\"p\"> & { ref?: Ref<HTMLParagraphElement> }) {\n return (\n <p\n className={cn(\n \"text-display-xs text-pho-description font-normal\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/** The card body. Carries the horizontal padding; bring your own layout. */\nfunction Content({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n return <div className={cn(\"px-5\", className)} {...props} />;\n}\n\n/**\n * Trailing row — actions, a status line. Unopinionated about justification;\n * add `justify-end` for a dialog-style action bar.\n */\nfunction Footer({\n className,\n ...props\n}: ComponentProps<\"div\"> & { ref?: Ref<HTMLDivElement> }) {\n return (\n <div className={cn(\"flex items-center gap-2 px-5\", className)} {...props} />\n );\n}\n\n/**\n * Card — a bordered content panel: header (title + description), body,\n * optional footer. It's static chrome with no interactivity of its own.\n * For a label/control list use `BasicPage.Card`; for a featured\n * block on the page surface use `BasicPage.Callout`.\n *\n * ```tsx\n * <Card.Root>\n * <Card.Header>\n * <Card.Title>Usage</Card.Title>\n * <Card.Description>Since Aug 1.</Card.Description>\n * </Card.Header>\n * <Card.Content>…</Card.Content>\n * <Card.Footer className=\"justify-end\">\n * <Button size=\"sm\">Upgrade</Button>\n * </Card.Footer>\n * </Card.Root>\n * ```\n */\nexport const Card = {\n Root,\n Header,\n Title,\n Description,\n Content,\n Footer,\n};\n"],"mappings":";;;;AAmBA,IAAa,IACX;AAGF,SAAS,EAAK,EACZ,WAAA,GACA,GAAG,EAAA,GACqD;AACxD,SACE,gBAAA,EAAC,OAAD;AAAA,IACE,WAAW,EAAG,GAAc,iCAAiC,CAAS;AAAA,IACtE,GAAI;AAAA,EACL,CAAA;AAEL;AAGA,SAAS,EAAO,EACd,WAAA,GACA,GAAG,EAAA,GACqD;AACxD,SACE,gBAAA,EAAC,OAAD;AAAA,IAAK,WAAW,EAAG,8BAA8B,CAAS;AAAA,IAAG,GAAI;AAAA,EAAQ,CAAA;AAE7E;AAMA,SAAS,EAAM,EACb,WAAA,GACA,UAAA,GACA,GAAG,EAAA,GACwD;AAC3D,SACE,gBAAA,EAAC,MAAD;AAAA,IACE,WAAW,EAAG,gDAAgD,CAAS;AAAA,IACvE,GAAI;AAAA,IAEH,UAAA;AAAA,EACC,CAAA;AAER;AAGA,SAAS,EAAY,EACnB,WAAA,GACA,GAAG,EAAA,GACyD;AAC5D,SACE,gBAAA,EAAC,KAAD;AAAA,IACE,WAAW,EACT,oDACA,CACF;AAAA,IACA,GAAI;AAAA,EACL,CAAA;AAEL;AAGA,SAAS,EAAQ,EACf,WAAA,GACA,GAAG,EAAA,GACqD;AACxD,SAAO,gBAAA,EAAC,OAAD;AAAA,IAAK,WAAW,EAAG,QAAQ,CAAS;AAAA,IAAG,GAAI;AAAA,EAAQ,CAAA;AAC5D;AAMA,SAAS,EAAO,EACd,WAAA,GACA,GAAG,EAAA,GACqD;AACxD,SACE,gBAAA,EAAC,OAAD;AAAA,IAAK,WAAW,EAAG,gCAAgC,CAAS;AAAA,IAAG,GAAI;AAAA,EAAQ,CAAA;AAE/E;AAqBA,IAAa,IAAO;AAAA,EAClB,MAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { t as r } from "./cn-
|
|
2
|
+
import { t as r } from "./cn-ChIgwEl3.js";
|
|
3
3
|
import { t as n } from "./resolve-variant-CcD8iG5l.js";
|
|
4
4
|
import { t as a } from "./IconX-pZ9vrtSr.js";
|
|
5
5
|
import "react";
|
|
@@ -26,4 +26,4 @@ export {
|
|
|
26
26
|
u as t
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
//# sourceMappingURL=close-button-
|
|
29
|
+
//# sourceMappingURL=close-button-GvqWbLl0.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"close-button-
|
|
1
|
+
{"version":3,"file":"close-button-GvqWbLl0.js","names":[],"sources":["../../src/components/button/close-button.tsx"],"sourcesContent":["import { type ButtonHTMLAttributes } from \"react\";\nimport { IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\n\n// `rounded-base` (12px) at every size; base classes already apply squircle.\nconst CLOSE_SIZES = {\n sm: { classes: \"size-9 rounded-base *:size-5\" },\n md: { classes: \"size-10 rounded-base *:size-5\" },\n lg: { classes: \"size-11 rounded-base *:size-6\" },\n} satisfies VariantMap;\n\nexport interface CloseButtonProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"color\"\n> {\n /** Size variant. @default \"sm\" */\n size?: keyof typeof CLOSE_SIZES;\n /** Accessible label. @default \"Close\" */\n label?: string;\n}\n\n/**\n * Icon-only dismiss control — a squircle chip that fills on hover. Adapts to\n * light/dark automatically via semantic tokens. Used by Dialog and dismissible\n * surfaces.\n */\nexport function CloseButton({\n size = \"sm\",\n label,\n className,\n ...props\n}: CloseButtonProps) {\n return (\n <button\n type=\"button\"\n aria-label={label || \"Close\"}\n {...props}\n className={cn(\n \"squircle text-pho-secondary outline-pho-brand hover:bg-pho-ghost-hover hover:text-pho-secondary-hover inline-flex cursor-pointer items-center justify-center p-2 transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2\",\n resolveVariant(CLOSE_SIZES, size),\n className,\n )}\n >\n <IconX aria-hidden=\"true\" className=\"transition-inherit-all shrink-0\" />\n </button>\n );\n}\n\nCloseButton.displayName = \"CloseButton\";\n"],"mappings":";;;;;;AAMA,IAAM,IAAc;AAAA,EAClB,IAAI,EAAE,SAAS,+BAA+B;AAAA,EAC9C,IAAI,EAAE,SAAS,gCAAgC;AAAA,EAC/C,IAAI,EAAE,SAAS,gCAAgC;AACjD;AAiBA,SAAgB,EAAY,EAC1B,MAAA,IAAO,MACP,OAAA,GACA,WAAA,GACA,GAAG,EAAA,GACgB;AACnB,SACE,gBAAA,EAAC,UAAD;AAAA,IACE,MAAK;AAAA,IACL,cAAY,KAAS;AAAA,IACrB,GAAI;AAAA,IACJ,WAAW,EACT,+PACA,EAAe,GAAa,CAAI,GAChC,CACF;AAAA,IAEA,UAAA,gBAAA,EAAC,GAAD;AAAA,MAAO,eAAY;AAAA,MAAO,WAAU;AAAA,IAAmC,CAAA;AAAA,EACjE,CAAA;AAEZ;AAEA,EAAY,cAAc"}
|