import * as React from 'react'; /** * A hash router in a hundred lines, so the showcase adds no dependency. * * The hash carries the page, the global toggles and the playground's own props — * `#/button?theme=dark&p.variant=outline` — which makes any view of the gallery, * down to a single configured instance, a shareable link. */ export interface Route { /** Path segment of the hash, always starting with `/`. */ path: string; /** Query segment — the toolbar's global toggles plus any `p.*` prop. */ params: URLSearchParams; } /** * Prefix for a playground prop. * * It does two jobs. It keeps an axis named `size` or `theme` from colliding with * a page filter or a global toggle, and — because the prefix makes prop params * recognisable — it lets `Link`/`navigate` drop them when the path changes. See * `stripProps`. */ export const PROP_PREFIX = 'p.'; /** * `history.replaceState` does not fire `hashchange`, so a replacing write has to * announce itself. Both are subscribed below, and the store is the hash string, * so a duplicate notification is a no-op rather than a double render. */ const ROUTE_EVENT = 'showcase:route'; function subscribe(onChange: () => void) { window.addEventListener('hashchange', onChange); window.addEventListener(ROUTE_EVENT, onChange); return () => { window.removeEventListener('hashchange', onChange); window.removeEventListener(ROUTE_EVENT, onChange); }; } function readHash() { return window.location.hash.slice(1) || '/'; } function parse(hash: string): Route { const [path, query = ''] = hash.split('?'); return { path: path || '/', params: new URLSearchParams(query) }; } /** `#/button?theme=dark` — the string an `` or a shared link needs. */ export function hrefFor(path: string, params: URLSearchParams): string { const query = params.toString(); return `#${path}${query ? `?${query}` : ''}`; } /** * The same link, absolute — what goes on the clipboard. * * Resolved against the current document rather than assembled from parts, so a * `SHOWCASE_BASE` sub-path deployment (`https://host/kit/#/button`) comes out * right without this file knowing the base exists. */ export function absoluteHref(path: string, params: URLSearchParams): string { return new URL(hrefFor(path, params), window.location.href).href; } /** * Drops every playground prop, in place. * * `Link` carries the current params across navigation on purpose — a dark-themed * Button page should not throw you back to light. A prop is the opposite: it * belongs to the component you are leaving, and `p.variant=outline` following * you onto the Badge page would silently reconfigure it. */ export function stripProps(params: URLSearchParams): URLSearchParams { for (const key of [...params.keys()]) { if (key.startsWith(PROP_PREFIX)) params.delete(key); } return params; } /** * `replace` writes through `history.replaceState` instead of pushing an entry. * * The playground writes on every control change; without this, ten clicks on a * `