@hanzo/ui 8.0.24 → 8.0.25
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/README.md +59 -0
- package/dist/chunk-3NG33DV2.cjs +607 -0
- package/dist/chunk-3NG33DV2.cjs.map +1 -0
- package/dist/chunk-5CZOYONP.js +343 -0
- package/dist/chunk-5CZOYONP.js.map +1 -0
- package/dist/{chunk-P2P6ST6J.js → chunk-7ZNK6JSS.js} +9 -394
- package/dist/chunk-7ZNK6JSS.js.map +1 -0
- package/dist/chunk-GVCOAJLO.js +397 -0
- package/dist/chunk-GVCOAJLO.js.map +1 -0
- package/dist/chunk-GWNNLYX4.cjs +353 -0
- package/dist/chunk-GWNNLYX4.cjs.map +1 -0
- package/dist/{chunk-NBZDIMP3.cjs → chunk-ONVERVFG.cjs} +33 -438
- package/dist/chunk-ONVERVFG.cjs.map +1 -0
- package/dist/chunk-P2QU5MN5.js +555 -0
- package/dist/chunk-P2QU5MN5.js.map +1 -0
- package/dist/chunk-WHJR5S4L.cjs +418 -0
- package/dist/chunk-WHJR5S4L.cjs.map +1 -0
- package/dist/framework/CollectionBuilder.d.ts +12 -0
- package/dist/framework/CollectionsBrowser.d.ts +26 -19
- package/dist/framework/DocTypeRecords.d.ts +31 -11
- package/dist/framework/MediaGrid.d.ts +42 -0
- package/dist/framework/RecordCards.d.ts +35 -0
- package/dist/framework/builder-logic.d.ts +76 -0
- package/dist/framework/client.d.ts +31 -44
- package/dist/framework/core.cjs +213 -0
- package/dist/framework/core.cjs.map +1 -0
- package/dist/framework/core.d.ts +6 -0
- package/dist/framework/core.js +4 -0
- package/dist/framework/core.js.map +1 -0
- package/dist/framework/data.d.ts +2 -2
- package/dist/framework/fields.d.ts +47 -4
- package/dist/framework/index.cjs +1340 -0
- package/dist/framework/index.cjs.map +1 -0
- package/dist/framework/index.d.ts +9 -23
- package/dist/framework/index.js +1119 -0
- package/dist/framework/index.js.map +1 -0
- package/dist/framework/media.d.ts +53 -0
- package/dist/framework/parts.d.ts +74 -0
- package/dist/framework/responsive.d.ts +41 -0
- package/dist/framework/types.d.ts +13 -7
- package/dist/product/SelectMenu.d.ts +13 -1
- package/dist/product/index.cjs +2061 -336
- package/dist/product/index.cjs.map +1 -1
- package/dist/product/index.js +1909 -3
- package/dist/product/index.js.map +1 -1
- package/dist/product/social/index.cjs +17 -16
- package/dist/product/social/index.js +3 -2
- package/package.json +11 -5
- package/dist/chunk-2BM42AZR.cjs +0 -2301
- package/dist/chunk-2BM42AZR.cjs.map +0 -1
- package/dist/chunk-7P7R7FC5.js +0 -2231
- package/dist/chunk-7P7R7FC5.js.map +0 -1
- package/dist/chunk-NBZDIMP3.cjs.map +0 -1
- package/dist/chunk-P2P6ST6J.js.map +0 -1
- package/dist/framework/Loader.d.ts +0 -4
- package/dist/framework.cjs +0 -760
- package/dist/framework.cjs.map +0 -1
- package/dist/framework.d.ts +0 -1
- package/dist/framework.js +0 -730
- package/dist/framework.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/product/host.tsx","../src/product/BackendState.tsx","../src/product/PageHeader.tsx","../src/product/accent.ts","../src/product/PrimaryButton.tsx","../src/product/EmptyState.tsx","../src/product/Field.tsx"],"names":["jsx","jsxs","XStack","Text","Button","Card","YStack","createContext","useContext"],"mappings":"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","file":"chunk-GVCOAJLO.js","sourcesContent":["'use client'\n\n/**\n * The host seam — the ONE place an app injects its effects into this layer.\n *\n * The product layer is presentational and host-agnostic: it never imports a\n * router or an auth module. An honest state card still has to OFFER \"Sign in\n * again\" (401) or \"Add credits\" (402), so the app provides those two effects\n * once at its root and every card below renders the right affordance. Without a\n * provider the affordance is simply not offered — nothing is faked.\n */\nimport { createContext, useContext, type ReactNode } from 'react'\n\nexport type HostActions = {\n /** Re-authenticate and return to this exact page (401). */\n signIn?: () => void\n /** Navigate to the org's top-up surface (402). */\n addCredits?: () => void\n}\n\nconst HostCtx = createContext<HostActions>({})\n\nexport const HostProvider = ({ actions, children }: { actions: HostActions; children: ReactNode }) => (\n <HostCtx.Provider value={actions}>{children}</HostCtx.Provider>\n)\n\nexport const useHost = (): HostActions => useContext(HostCtx)\n","'use client'\n\n/**\n * Honest backend states for `/v1`-backed modules — ONE place that maps a thrown\n * HTTP error to a truthful explanation.\n *\n * A 503 means the runtime/keys aren't wired on this deployment yet, a 404/405\n * means the route isn't mounted here, 401/403 means the session can't read it,\n * 402 means the org has no funded balance. Nothing is fabricated — on failure the\n * caller shows this card instead of placeholder rows/charts.\n *\n * Classification is duck-typed on `status`, so any client's error class works\n * (console's `ApiError`, commerce's `CommerceError`, …) without this layer\n * depending on one. Effects come from `useHost()`.\n */\nimport type { ReactNode } from 'react'\nimport { Button, Card, Text, XStack } from '@hanzo/gui'\nimport { TriangleAlert, CreditCard } from '@hanzogui/lucide-icons-2'\n\nimport { useHost } from './host'\n\nexport type BackendStateKind =\n | 'not-initialized'\n | 'unavailable'\n | 'not-implemented'\n | 'access'\n | 'signin'\n | 'billing'\n | 'error'\n\nexport type BackendState = { kind: BackendStateKind; message: string }\n\n/**\n * Classify a thrown `/v1` error into an honest kind + raw message.\n *\n * 401 and 403 are DISTINCT for a signed-in user: 401 = the session is expired /\n * not recognized (re-auth fixes it → `signin`), 403 = the account IS signed in\n * but this surface isn't authorized/enabled for its org (`access`). A signed-in\n * user is NEVER told to \"sign in\" for a 403 — that would be a false instruction.\n */\nexport function classifyBackend(e: unknown): BackendState {\n const status = typeof (e as { status?: unknown })?.status === 'number' ? (e as { status: number }).status : 0\n const message = e instanceof Error ? e.message : String(e)\n if (status === 503) return { kind: 'not-initialized', message }\n if (status === 404 || status === 405) return { kind: 'unavailable', message }\n // 501 — the route EXISTS and answers honestly that its body ships later (the\n // Cloudflare R2/KV/D1 Phase-2 seams). Distinct from 404 (not mounted here): the\n // capability is real and planned, so say that rather than \"not available yet\".\n if (status === 501) return { kind: 'not-implemented', message }\n if (status === 402) return { kind: 'billing', message }\n if (status === 401) return { kind: 'signin', message }\n if (status === 403) return { kind: 'access', message }\n return { kind: 'error', message }\n}\n\n/**\n * Classify a READ (GET/list) failure. Identical to `classifyBackend` EXCEPT a 402\n * is NOT a wall: listing/reading a resource is never credit-gated, so a 402 on a\n * read means \"nothing is provisioned yet\" — the caller shows its honest EMPTY\n * state (e.g. \"No buckets yet · Create one\"), never an \"add credits\" paywall\n * (that belongs only on a paid WRITE). Returns `null` for a 402 → treat as empty.\n */\nexport function classifyRead(e: unknown): BackendState | null {\n const s = classifyBackend(e)\n return s.kind === 'billing' ? null : s\n}\n\nconst TITLES: Record<BackendStateKind, string> = {\n 'not-initialized': 'Backend not initialized',\n unavailable: 'Not available on this deployment yet',\n 'not-implemented': 'Not yet available',\n access: 'Not enabled for your account',\n signin: 'Your session expired',\n billing: 'Add credits to continue',\n error: 'Could not reach the backend',\n}\n\nconst BODIES: Record<BackendStateKind, string> = {\n 'not-initialized':\n 'The /v1 route is mounted but its runtime (or the console API key it proxies to) is not configured on this deployment yet. Real data appears here once it is — no placeholder data is shown.',\n unavailable:\n 'This endpoint is not mounted at the gateway on this host yet. The view lights up automatically once the route is live.',\n // 501 — a real, planned capability whose body ships in a later phase.\n 'not-implemented':\n 'This capability is planned but not wired yet. The backend answers honestly rather than pretending it worked — nothing here is fabricated, and the view lights up automatically once it ships.',\n // 403 for a SIGNED-IN user — never \"sign in\".\n access:\n \"You're signed in, but this isn't enabled for your organization on this deployment, or it's an admin-only surface. It appears here automatically once your account has access — nothing is fabricated.\",\n // 401 — the session itself lapsed; re-auth returns to this exact page.\n signin: 'Your session has expired or isn’t recognized here. Sign in again to continue where you left off.',\n // Empty → the card shows the backend's own message (the honest \"Insufficient\n // balance. Please add credits…\" from the gateway billing gate).\n billing: '',\n error: '',\n}\n\n/** A truthful state card for a `/v1` load failure, with an optional retry + hint. */\nexport function BackendStateCard({\n state,\n onRetry,\n hint,\n}: {\n state: BackendState\n onRetry?: () => void\n hint?: ReactNode\n}) {\n const { signIn, addCredits } = useHost()\n return (\n <Card borderWidth={1} borderColor=\"$borderColor\" p=\"$4\" gap=\"$2\" maxWidth={640}>\n <XStack gap=\"$2\" items=\"center\">\n <TriangleAlert size={16} />\n <Text fontSize=\"$4\" fontWeight=\"700\">\n {TITLES[state.kind]}\n </Text>\n </XStack>\n <Text fontSize=\"$3\" color=\"$color11\">\n {BODIES[state.kind] || state.message}\n </Text>\n {hint ? (\n <Text fontSize=\"$2\" color=\"$color10\">\n {hint}\n </Text>\n ) : null}\n {state.kind === 'signin' && signIn ? (\n <Button size=\"$2\" theme=\"light\" self=\"flex-start\" onPress={signIn}>\n Sign in again\n </Button>\n ) : state.kind === 'billing' && addCredits ? (\n // 402 — the org has no funded balance. Offer a top-up CTA (and keep Retry so\n // a returning, funded caller can reload in place).\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\">\n <Button size=\"$2\" theme=\"light\" self=\"flex-start\" icon={<CreditCard size={14} />} onPress={addCredits}>\n Add credits\n </Button>\n {onRetry ? (\n <Button size=\"$2\" self=\"flex-start\" onPress={onRetry}>\n Retry\n </Button>\n ) : null}\n </XStack>\n ) : onRetry ? (\n <Button size=\"$2\" self=\"flex-start\" onPress={onRetry}>\n Retry\n </Button>\n ) : null}\n </Card>\n )\n}\n","'use client'\n\nimport type { ReactNode } from 'react'\nimport { Text, XStack, YStack } from '@hanzo/gui'\n\n/** Section title + optional subtitle and right-aligned actions. */\nexport function PageHeader({\n title,\n subtitle,\n actions,\n}: {\n title: string\n subtitle?: string\n actions?: ReactNode\n}) {\n return (\n // flexWrap lets the actions drop below the title on narrow screens; flex={1}\n // minW={0} lets the title column shrink so a long subtitle WRAPS instead of\n // running off the viewport (a flex child defaults to min-width:auto = content\n // width, which would overflow on mobile).\n <XStack justify=\"space-between\" items=\"flex-start\" gap=\"$3\" flexWrap=\"wrap\">\n <YStack gap=\"$1\" flex={1} minW={200}>\n <Text fontSize=\"$7\" fontWeight=\"500\" letterSpacing={-0.4}>\n {title}\n </Text>\n {subtitle ? (\n <Text fontSize=\"$3\" color=\"$color11\">\n {subtitle}\n </Text>\n ) : null}\n </YStack>\n {/* Actions take a FULL line below the title on phones (width 100%) and WRAP —\n so a header with many buttons (e.g. Tracker's Projects/Refresh/List/Board/\n New issue/Delete) never runs off-screen and clips the last controls. At $md+\n they collapse back to a right-aligned inline row beside the title. */}\n {actions ? (\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\" width=\"100%\" $md={{ width: 'auto', justify: 'flex-end' }}>\n {actions}\n </XStack>\n ) : null}\n </XStack>\n )\n}\n","'use client'\n\n/**\n * Org accent theme — the ONE place that turns an organization's saved brand color\n * (`themeData.colorPrimary`, when `themeData.isEnabled`) into the LIVE console accent.\n *\n * The console is monochrome by default; an org opts into a custom accent in Settings →\n * Branding. Saving persists the color on the org record (that half already works). This\n * module is the missing APPLY half: a tiny external store holds the resolved accent\n * (hex + a readable contrast, or null); `setOrgAccent` updates it on load AND on save\n * (one mechanism, two callers), and `useAccent()` lets the genuine accent surfaces (the\n * primary button, the active tabs, the active nav item) read it and recolor with real\n * `@hanzo/gui` props — reverting cleanly to the default monochrome when the org disables\n * its theme (or the hex is invalid). React-prop driven (Tamagui's own styling path), NOT\n * a CSS class — Tamagui does not forward `className` to a Button's DOM node.\n *\n * Pure + SSR-safe: the resolve/contrast helpers are pure (unit-tested); the store starts\n * from a stable default snapshot so the server render and first client paint agree.\n */\nimport { useSyncExternalStore } from 'react'\n\n/** An org's persisted theme block (mirrors IAM `themeData`; only the fields we apply). */\nexport type OrgThemeData = {\n colorPrimary?: string\n isEnabled?: boolean\n}\n\n/** The live accent — a resolved hex + a readable text color for content on it, or null. */\nexport type Accent = { accent: string | null; contrast: string }\n\n/** True for a 3- or 6-digit CSS hex color (`#RGB` / `#RRGGBB`). */\nexport function isHexColor(v: string | undefined | null): boolean {\n return typeof v === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(v.trim())\n}\n\n/**\n * The accent hex to apply for an org, or `null` when no custom accent should be used —\n * i.e. the theme is disabled, missing, or the color is not a valid hex. PURE.\n */\nexport function resolveAccent(theme: OrgThemeData | null | undefined): string | null {\n if (!theme || !theme.isEnabled) return null\n const hex = (theme.colorPrimary ?? '').trim()\n return isHexColor(hex) ? hex : null\n}\n\n/** Expand `#abc` → `#aabbcc`; pass a 6-digit hex through (lowercased). */\nfunction expandHex(hex: string): string {\n const h = hex.trim().toLowerCase()\n if (h.length === 4) return `#${h[1]}${h[1]}${h[2]}${h[2]}${h[3]}${h[3]}`\n return h\n}\n\n/**\n * A readable text color (`#000000` / `#ffffff`) for content placed ON the accent,\n * chosen by the accent's relative luminance (WCAG-style). PURE. Falls back to white\n * for a non-hex input (never throws).\n */\nexport function contrastText(hex: string): '#000000' | '#ffffff' {\n if (!isHexColor(hex)) return '#ffffff'\n const h = expandHex(hex)\n const r = parseInt(h.slice(1, 3), 16) / 255\n const g = parseInt(h.slice(3, 5), 16) / 255\n const b = parseInt(h.slice(5, 7), 16) / 255\n const lin = (c: number) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4)\n const luminance = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b)\n return luminance > 0.5 ? '#000000' : '#ffffff'\n}\n\n/** Resolve a theme block to the `Accent` value it should apply (accent+contrast or null). PURE. */\nexport function accentFor(theme: OrgThemeData | null | undefined): Accent {\n const hex = resolveAccent(theme)\n return { accent: hex, contrast: hex ? contrastText(hex) : '#ffffff' }\n}\n\n// ── The live accent store (an external store React subscribes to) ─────────────\n\n/** The default (no custom accent) — a STABLE reference so SSR + first paint agree. */\nconst DEFAULT: Accent = { accent: null, contrast: '#ffffff' }\n\nlet current: Accent = DEFAULT\nconst listeners = new Set<() => void>()\n\n/**\n * Set the live accent from an org theme block. Called on load (from the fetched org) AND\n * immediately on save — the SINGLE mechanism, so the accent survives reload and updates\n * without one. A no-op when the resolved accent is unchanged (keeps the snapshot stable).\n */\nexport function setOrgAccent(theme: OrgThemeData | null | undefined): void {\n const next = accentFor(theme)\n if (next.accent === current.accent) return\n current = next.accent ? next : DEFAULT\n for (const l of listeners) l()\n}\n\nfunction subscribe(cb: () => void): () => void {\n listeners.add(cb)\n return () => {\n listeners.delete(cb)\n }\n}\n\n/** The live accent (subscribes to the store). Returns the default until an org theme is applied. */\nexport function useAccent(): Accent {\n return useSyncExternalStore(\n subscribe,\n () => current,\n () => DEFAULT,\n )\n}\n","'use client'\n\n/**\n * Primary button — the one white, high-emphasis action for the console.\n *\n * Monochrome brand: `theme=\"light\"` flips the button to the light theme inside\n * the dark console, giving a white fill with a near-black label and icon — no\n * hue accent. Use it for the single primary action in a view (sign in, save,\n * get started). Secondary and destructive actions use the default neutral\n * `Button`.\n *\n * When an org enables a custom brand color, this — the console's one primary\n * action — recolors to the org accent (inline bg + the color prop the label/icon\n * inherit), reading the live accent from `useAccent()`; with no custom theme it\n * stays the default white. A caller's own `style`/`color` still win (spread last).\n */\nimport type { ComponentProps } from 'react'\nimport { Button } from '@hanzo/gui'\n\nimport { useAccent } from './accent'\nimport { labelOf, useEmit } from './instrument'\n\nexport function PrimaryButton({ onPress, ...rest }: ComponentProps<typeof Button>) {\n const { accent, contrast } = useAccent()\n const track = useEmit()\n // DESTRUCTURE the caller's handler out first. Wrapping it while still reading it\n // off a rebound `props` makes the wrapper call ITSELF — an unbounded recursion\n // that a real browser click turns into \"Maximum call stack size exceeded\".\n const press = (e: unknown) => {\n // The label IS the identity of a primary action — no app has to name it.\n track({\n component: 'PrimaryButton',\n action: 'click',\n id: labelOf(rest.children) ?? rest['aria-label'],\n })\n ;(onPress as ((e: unknown) => void) | undefined)?.(e)\n }\n const handler = press as ComponentProps<typeof Button>['onPress']\n // Accent set → a filled accent button (bg + readable text, via inline style Tamagui\n // forwards to the DOM). No accent → the default monochrome white (theme=\"light\").\n if (accent) {\n return <Button style={{ backgroundColor: accent, color: contrast, borderColor: accent }} onPress={handler} {...rest} />\n }\n return <Button theme=\"light\" onPress={handler} {...rest} />\n}\n","'use client'\n\n/**\n * Rich empty state — the ONE way the console turns a real, honest \"nothing here\n * yet\" into a helpful onboarding moment (DigitalOcean / Vercel class) instead of\n * a dead screen.\n *\n * It is NOT a fabricated-data fallback: callers render it only when a load\n * genuinely succeeded with zero rows (or a resource has no surface yet). It shows\n * the product icon, a one-line \"what this is\", a couple of guidance bullets, a\n * single primary call-to-action (Deploy / Create / Connect …) and an optional\n * secondary link (Docs / CLI). Orthogonal to `BackendStateCard` (failures) and\n * `DataTable`'s thin inline empty (in-table) — this is the full-bleed first-run\n * surface a module shows in place of its table.\n */\nimport type { ReactElement, ReactNode } from 'react'\nimport { Button, Card, Text, XStack, YStack } from '@hanzo/gui'\nimport { ArrowRight, Check, ExternalLink } from '@hanzogui/lucide-icons-2'\n\nimport type { IconLike } from './color'\nimport { PrimaryButton } from './PrimaryButton'\n\n/** A single call-to-action — primary (filled) or a secondary/external link. */\nexport type EmptyAction = {\n label: string\n /** In-app navigation / handler. */\n onPress?: () => void\n /** External URL — opens in a new tab with an external-link affordance. */\n href?: string\n icon?: ReactElement\n}\n\n/** Open an external URL in a new tab (no-opener), guarded for SSR. */\nfunction openHref(href: string): void {\n if (typeof window !== 'undefined') window.open(href, '_blank', 'noopener')\n}\n\nimport { useEmit } from './instrument'\n\nexport function EmptyState({\n icon: Icon,\n title,\n description,\n bullets,\n primary,\n secondary,\n}: {\n /** Product/section icon — any lucide/Gui icon component (size + optional color). */\n icon: IconLike\n title: string\n description: string\n /** Optional \"here's how to get started\" guidance bullets. */\n bullets?: string[]\n /** The single high-emphasis action (Deploy / Create / Connect). */\n primary?: EmptyAction\n /** An optional lower-emphasis action (Docs / CLI / Learn more). */\n secondary?: EmptyAction\n}) {\n const track = useEmit()\n return (\n <Card\n borderWidth={1}\n borderColor=\"$borderColor\"\n borderStyle=\"dashed\"\n p=\"$6\"\n gap=\"$4\"\n items=\"center\"\n maxWidth={640}\n self=\"center\"\n width=\"100%\"\n >\n <YStack\n width={48}\n height={48}\n items=\"center\"\n justify=\"center\"\n rounded=\"$4\"\n bg=\"$color3\"\n >\n <Icon size={24} />\n </YStack>\n\n <YStack gap=\"$2\" items=\"center\" maxW={480}>\n <Text fontSize=\"$6\" fontWeight=\"500\" text=\"center\" letterSpacing={-0.3}>\n {title}\n </Text>\n <Text fontSize=\"$3\" color=\"$color11\" text=\"center\">\n {description}\n </Text>\n </YStack>\n\n {bullets && bullets.length ? (\n <YStack gap=\"$2\" self=\"stretch\" maxW={420} mx=\"auto\">\n {bullets.map((b) => (\n <XStack key={b} gap=\"$2\" items=\"flex-start\">\n <YStack pt=\"$1\">\n <Check size={14} color=\"$color10\" />\n </YStack>\n <Text fontSize=\"$3\" color=\"$color11\" flex={1}>\n {b}\n </Text>\n </XStack>\n ))}\n </YStack>\n ) : null}\n\n {primary || secondary ? (\n <XStack gap=\"$2\" items=\"center\" flexWrap=\"wrap\" justify=\"center\">\n {primary ? (\n <PrimaryButton\n icon={primary.icon}\n iconAfter={primary.href ? <ExternalLink size={15} /> : <ArrowRight size={15} />}\n onPress={() => {\n track({ component: 'EmptyState', action: 'click', id: title, value: primary.label })\n if (primary.href) openHref(primary.href)\n else primary.onPress?.()\n }}\n >\n {primary.label}\n </PrimaryButton>\n ) : null}\n {secondary ? (\n <Button\n chromeless\n icon={secondary.icon}\n iconAfter={secondary.href ? <ExternalLink size={14} /> : undefined}\n onPress={() => {\n track({ component: 'EmptyState', action: 'click', id: title, value: secondary.label })\n if (secondary.href) openHref(secondary.href)\n else secondary.onPress?.()\n }}\n >\n {secondary.label}\n </Button>\n ) : null}\n </XStack>\n ) : null}\n </Card>\n )\n}\n","'use client'\n\n/**\n * Form field primitives — labeled rows over Hanzo GUI inputs.\n *\n * One way to render a labeled control. Every editor field goes through these, so\n * spacing, disabled styling, and the label column stay consistent. Style props\n * use the v5 shorthand set (p/px/items/justify/...).\n */\nimport { createContext, useContext, type CSSProperties, type ReactNode } from 'react'\nimport {\n Input,\n Label,\n Switch,\n TextArea,\n Slider,\n Text,\n XStack,\n YStack,\n} from '@hanzo/gui'\n\nimport { textSize, useEmit, type UiEvent } from './instrument'\n\n// The label the caller ALREADY typed on the FieldRow becomes the analytics name —\n// so an app never labels a field twice and no field reports as \"unnamed\".\nconst FieldNameContext = createContext<string | undefined>(undefined)\n\n/** Every Field* control reports `{component, action:'change', id:<row label>}`;\n * the VALUE is only ever a scalar or a text LENGTH, never free text. */\nfunction useFieldTrack(component: string): (e: Omit<UiEvent, 'component'>) => void {\n const track = useEmit()\n const name = useContext(FieldNameContext)\n return (e) => track({ component, id: name, ...e })\n}\n\n/**\n * Labeled row. STACKED on phones (label full-width above a full-width control) so a\n * form inside a SlideOver stays usable and never clips its labels; a fixed 180px\n * label column + flexing control at $md+ (the two-column desktop/tablet layout).\n */\nexport function FieldRow({ label, children }: { label: string; children: ReactNode }) {\n return (\n <FieldNameContext.Provider value={label}>\n <XStack gap=\"$3\" items=\"flex-start\" flexWrap=\"wrap\">\n <Label width=\"100%\" pt=\"$2\" color=\"$color11\" fontSize=\"$3\" $md={{ width: 180 }}>\n {label}\n </Label>\n <YStack flex={1} minW={0} $md={{ minW: 240 }}>\n {children}\n </YStack>\n </XStack>\n </FieldNameContext.Provider>\n )\n}\n\nexport function FieldText({\n value,\n onChange,\n disabled,\n secure,\n placeholder,\n}: {\n value: string\n onChange: (v: string) => void\n disabled?: boolean\n secure?: boolean\n placeholder?: string\n}) {\n const track = useFieldTrack('FieldText')\n return (\n <Input\n value={value}\n onChangeText={(v: string) => {\n track({ action: 'change', value: secure ? 0 : textSize(v) })\n onChange(v)\n }}\n disabled={disabled}\n secureTextEntry={secure}\n placeholder={placeholder}\n autoCapitalize=\"none\"\n />\n )\n}\n\nexport function FieldTextArea({\n value,\n onChange,\n disabled,\n rows = 6,\n}: {\n value: string\n onChange: (v: string) => void\n disabled?: boolean\n rows?: number\n}) {\n const track = useFieldTrack('FieldTextArea')\n return (\n <TextArea\n value={value}\n onChangeText={(v: string) => {\n track({ action: 'change', value: textSize(v) })\n onChange(v)\n }}\n disabled={disabled}\n numberOfLines={rows}\n />\n )\n}\n\nexport function FieldSwitch({\n checked,\n onChange,\n disabled,\n}: {\n checked: boolean\n onChange: (v: boolean) => void\n disabled?: boolean\n}) {\n const track = useFieldTrack('FieldSwitch')\n return (\n <Switch\n checked={checked}\n onCheckedChange={(v: boolean) => {\n track({ action: 'change', value: v })\n onChange(v)\n }}\n disabled={disabled}\n size=\"$3\"\n >\n <Switch.Thumb />\n </Switch>\n )\n}\n\n/**\n * A labeled dropdown.\n *\n * Renders a REAL native `<select>` on web (and the OS-native picker on mobile),\n * NOT the `@hanzo/gui` `<Select native>` — that primitive is broken in gui 7.3.0:\n * `native` emits bare `<option>` elements with NO `<select>` wrapper, so every\n * dropdown across the console (region/size pickers, all edit forms) rendered as a\n * non-interactive flat list of option text that buried the surrounding controls.\n * A native `<select>` is compact, keyboard/ARIA-accessible, gives the native\n * mobile picker (best small-screen UX), and is themed via the Tamagui CSS custom\n * properties (`--background`/`--color12`/`--borderColor`) so it tracks light/dark.\n */\nconst CHEVRON = `data:image/svg+xml,${encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#9a9a9a\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>',\n)}`\n\nfunction selectStyle(disabled?: boolean): CSSProperties {\n return {\n width: '100%',\n boxSizing: 'border-box',\n appearance: 'none',\n WebkitAppearance: 'none',\n MozAppearance: 'none',\n background: `var(--background) url(\"${CHEVRON}\") no-repeat right 10px center`,\n color: 'var(--color12)',\n border: '1px solid var(--borderColor)',\n borderRadius: 9,\n padding: '9px 34px 9px 12px',\n fontSize: 14,\n lineHeight: '20px',\n fontFamily: 'inherit',\n height: 40,\n outline: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n }\n}\n\nconst OPTION_STYLE: CSSProperties = { background: 'var(--color2)', color: 'var(--color12)' }\n\nexport function FieldSelect({\n value,\n options,\n onChange,\n disabled,\n placeholder = 'Select…',\n}: {\n value: string\n options: string[]\n onChange: (v: string) => void\n disabled?: boolean\n placeholder?: string\n}) {\n // When the current value isn't one of the options (empty/unset), show the\n // placeholder as a selected-but-disabled row so the control reads honestly.\n const track = useFieldTrack('FieldSelect')\n const showPlaceholder = value === '' || !options.includes(value)\n return (\n <select\n value={showPlaceholder ? '' : value}\n onChange={(e) => {\n track({ action: 'change', value: e.currentTarget.value })\n onChange(e.currentTarget.value)\n }}\n disabled={disabled}\n aria-label={placeholder}\n style={selectStyle(disabled)}\n >\n {showPlaceholder ? (\n <option value=\"\" disabled style={OPTION_STYLE}>\n {placeholder}\n </option>\n ) : null}\n {options.map((opt) => (\n <option key={opt} value={opt} style={OPTION_STYLE}>\n {opt}\n </option>\n ))}\n </select>\n )\n}\n\nexport function FieldSlider({\n value,\n min,\n max,\n step,\n onChange,\n disabled,\n}: {\n value: number\n min: number\n max: number\n step: number\n onChange: (v: number) => void\n disabled?: boolean\n}) {\n const track = useFieldTrack('FieldSlider')\n return (\n <XStack gap=\"$3\" items=\"center\">\n <Text width={56} fontSize=\"$3\" color=\"$color11\">\n {value}\n </Text>\n <Slider\n flex={1}\n min={min}\n max={max}\n step={step}\n value={[value]}\n onValueChange={(v) => {\n track({ action: 'change', value: v[0] ?? min })\n onChange(v[0] ?? min)\n }}\n disabled={disabled}\n >\n <Slider.Track>\n <Slider.TrackActive />\n </Slider.Track>\n <Slider.Thumb index={0} circular />\n </Slider>\n </XStack>\n )\n}\n"]}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var chunk7XDEUEWI_cjs = require('./chunk-7XDEUEWI.cjs');
|
|
5
|
+
var gui = require('@hanzo/gui');
|
|
6
|
+
var lucideIcons2 = require('@hanzogui/lucide-icons-2');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
var react = require('react');
|
|
9
|
+
|
|
10
|
+
var ITEM_MIN_HEIGHT = 30;
|
|
11
|
+
var ITEM_RADIUS = 7;
|
|
12
|
+
var ITEM_PX = 8;
|
|
13
|
+
var ITEM_GAP = 8;
|
|
14
|
+
var ICON_SLOT = 16;
|
|
15
|
+
var PANEL_RADIUS = 12;
|
|
16
|
+
var PANEL_PAD = 4;
|
|
17
|
+
var PANEL_GAP = 2;
|
|
18
|
+
var SEP_MARGIN = 4;
|
|
19
|
+
var FONT_LABEL = 13;
|
|
20
|
+
var FONT_MUTED = 11;
|
|
21
|
+
var FONT_SHORTCUT = 12;
|
|
22
|
+
var ACCENT = "var(--hanzo-accent, #8b5cf6)";
|
|
23
|
+
var ACCENT_SOFT = "var(--hanzo-accent-soft, rgba(139,92,246,0.16))";
|
|
24
|
+
var DANGER = "var(--hanzo-danger, #ef4444)";
|
|
25
|
+
function MenuPanel({
|
|
26
|
+
children,
|
|
27
|
+
minWidth = 200,
|
|
28
|
+
maxHeight = 360,
|
|
29
|
+
onKeyDown,
|
|
30
|
+
panelRef,
|
|
31
|
+
...rest
|
|
32
|
+
}) {
|
|
33
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
34
|
+
gui.YStack,
|
|
35
|
+
{
|
|
36
|
+
ref: panelRef,
|
|
37
|
+
role: "menu",
|
|
38
|
+
bg: "$color2",
|
|
39
|
+
borderColor: "$borderColor",
|
|
40
|
+
borderWidth: 1,
|
|
41
|
+
rounded: PANEL_RADIUS,
|
|
42
|
+
p: PANEL_PAD,
|
|
43
|
+
gap: PANEL_GAP,
|
|
44
|
+
minW: minWidth,
|
|
45
|
+
maxH: maxHeight,
|
|
46
|
+
overflow: "scroll",
|
|
47
|
+
shadowColor: "rgba(0,0,0,0.45)",
|
|
48
|
+
shadowRadius: 20,
|
|
49
|
+
shadowOffset: { width: 0, height: 10 },
|
|
50
|
+
onKeyDown,
|
|
51
|
+
...rest,
|
|
52
|
+
children
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
function MenuItemView({
|
|
57
|
+
icon,
|
|
58
|
+
label,
|
|
59
|
+
description,
|
|
60
|
+
shortcut,
|
|
61
|
+
selected = false,
|
|
62
|
+
disabled = false,
|
|
63
|
+
destructive = false,
|
|
64
|
+
hasSubmenu = false,
|
|
65
|
+
onSelect
|
|
66
|
+
}) {
|
|
67
|
+
const track = chunk7XDEUEWI_cjs.useEmit();
|
|
68
|
+
const choose = () => {
|
|
69
|
+
track({ component: "MenuItem", action: "select", id: chunk7XDEUEWI_cjs.labelOf(label), value: selected });
|
|
70
|
+
onSelect();
|
|
71
|
+
};
|
|
72
|
+
const press = () => {
|
|
73
|
+
if (!disabled) choose();
|
|
74
|
+
};
|
|
75
|
+
const onKeyDown = (e) => {
|
|
76
|
+
if (disabled) return;
|
|
77
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
choose();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const labelColor = destructive ? DANGER : "$color12";
|
|
83
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
84
|
+
gui.XStack,
|
|
85
|
+
{
|
|
86
|
+
role: "menuitem",
|
|
87
|
+
tabIndex: disabled ? -1 : 0,
|
|
88
|
+
"aria-disabled": disabled || void 0,
|
|
89
|
+
items: "center",
|
|
90
|
+
gap: ITEM_GAP,
|
|
91
|
+
px: ITEM_PX,
|
|
92
|
+
minH: ITEM_MIN_HEIGHT,
|
|
93
|
+
rounded: ITEM_RADIUS,
|
|
94
|
+
cursor: disabled ? "default" : "pointer",
|
|
95
|
+
opacity: disabled ? 0.4 : 1,
|
|
96
|
+
select: "none",
|
|
97
|
+
style: { outline: "none" },
|
|
98
|
+
hoverStyle: disabled ? {} : { bg: ACCENT_SOFT },
|
|
99
|
+
pressStyle: disabled ? {} : { bg: ACCENT_SOFT },
|
|
100
|
+
focusStyle: disabled ? {} : { bg: ACCENT_SOFT },
|
|
101
|
+
onPress: press,
|
|
102
|
+
onKeyDown,
|
|
103
|
+
children: [
|
|
104
|
+
/* @__PURE__ */ jsxRuntime.jsx(gui.XStack, { width: ICON_SLOT, height: ICON_SLOT, items: "center", justify: "center", shrink: 0, children: icon ? /* @__PURE__ */ jsxRuntime.jsx(gui.XStack, { items: "center", justify: "center", opacity: destructive ? 1 : 0.9, style: destructive ? { color: DANGER } : void 0, children: icon }) : null }),
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsxs(gui.YStack, { flex: 1, minW: 0, children: [
|
|
106
|
+
/* @__PURE__ */ jsxRuntime.jsx(gui.Text, { fontSize: FONT_LABEL, lineHeight: 18, color: labelColor, numberOfLines: 1, children: label }),
|
|
107
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx(gui.Text, { fontSize: FONT_MUTED, lineHeight: 14, color: "$color11", numberOfLines: 1, children: description }) : null
|
|
108
|
+
] }),
|
|
109
|
+
shortcut ? /* @__PURE__ */ jsxRuntime.jsx(gui.Text, { fontSize: FONT_SHORTCUT, color: "$color11", shrink: 0, children: shortcut }) : null,
|
|
110
|
+
selected ? /* @__PURE__ */ jsxRuntime.jsx(lucideIcons2.Check, { size: 14, color: ACCENT }) : null,
|
|
111
|
+
hasSubmenu ? /* @__PURE__ */ jsxRuntime.jsx(lucideIcons2.ChevronRight, { size: 14, color: "var(--hanzo-muted, currentColor)", opacity: 0.6 }) : null
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
function MenuSeparatorView() {
|
|
117
|
+
return /* @__PURE__ */ jsxRuntime.jsx(gui.YStack, { height: 1, bg: "$borderColor", my: SEP_MARGIN, mx: SEP_MARGIN, role: "separator" });
|
|
118
|
+
}
|
|
119
|
+
function MenuLabelView({ children }) {
|
|
120
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
121
|
+
gui.Text,
|
|
122
|
+
{
|
|
123
|
+
fontSize: FONT_MUTED,
|
|
124
|
+
color: "$color11",
|
|
125
|
+
px: ITEM_PX,
|
|
126
|
+
py: SEP_MARGIN,
|
|
127
|
+
textTransform: "uppercase",
|
|
128
|
+
letterSpacing: 0.4,
|
|
129
|
+
select: "none",
|
|
130
|
+
children
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
function renderMenuItems(items, close) {
|
|
135
|
+
return items.map((it, i) => {
|
|
136
|
+
if (it.type === "separator") return /* @__PURE__ */ jsxRuntime.jsx(MenuSeparatorView, {}, it.key ?? `sep-${i}`);
|
|
137
|
+
if (it.type === "label") return /* @__PURE__ */ jsxRuntime.jsx(MenuLabelView, { children: it.label }, it.key ?? `lbl-${i}`);
|
|
138
|
+
const { key, onSelect, closeOnSelect = true, ...rest } = it;
|
|
139
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
140
|
+
MenuItemView,
|
|
141
|
+
{
|
|
142
|
+
...rest,
|
|
143
|
+
onSelect: () => {
|
|
144
|
+
onSelect();
|
|
145
|
+
if (closeOnSelect) close?.();
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
key
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function PortalTheme({ name, children }) {
|
|
153
|
+
return /* @__PURE__ */ jsxRuntime.jsx(gui.Theme, { name, children });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/product/menu/roving.ts
|
|
157
|
+
function menuKeyDown(e, onClose) {
|
|
158
|
+
if (e.key === "Escape") {
|
|
159
|
+
onClose?.();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (e.key !== "ArrowDown" && e.key !== "ArrowUp" && e.key !== "Home" && e.key !== "End") return;
|
|
163
|
+
if (typeof document === "undefined") return;
|
|
164
|
+
const panel = e.currentTarget;
|
|
165
|
+
const items = Array.from(
|
|
166
|
+
panel.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')
|
|
167
|
+
);
|
|
168
|
+
if (items.length === 0) return;
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
const active = document.activeElement;
|
|
171
|
+
const current = active ? items.indexOf(active) : -1;
|
|
172
|
+
let next;
|
|
173
|
+
if (e.key === "Home") next = 0;
|
|
174
|
+
else if (e.key === "End") next = items.length - 1;
|
|
175
|
+
else if (e.key === "ArrowDown") next = current < 0 ? 0 : (current + 1) % items.length;
|
|
176
|
+
else next = current <= 0 ? items.length - 1 : current - 1;
|
|
177
|
+
items[next]?.focus();
|
|
178
|
+
}
|
|
179
|
+
var EDGE = 8;
|
|
180
|
+
function FloatingMenu({
|
|
181
|
+
open,
|
|
182
|
+
onClose,
|
|
183
|
+
anchorRect,
|
|
184
|
+
anchorEl,
|
|
185
|
+
gap = 4,
|
|
186
|
+
minWidth = 200,
|
|
187
|
+
maxHeight,
|
|
188
|
+
autoFocus = true,
|
|
189
|
+
children
|
|
190
|
+
}) {
|
|
191
|
+
const themeName = gui.useThemeName();
|
|
192
|
+
const panelRef = react.useRef(null);
|
|
193
|
+
const [pos, setPos] = react.useState(null);
|
|
194
|
+
const setPanel = react.useCallback((n) => {
|
|
195
|
+
panelRef.current = n;
|
|
196
|
+
}, []);
|
|
197
|
+
const place = react.useCallback(() => {
|
|
198
|
+
if (typeof window === "undefined") return;
|
|
199
|
+
const a = anchorRect();
|
|
200
|
+
if (!a) return;
|
|
201
|
+
const node = panelRef.current;
|
|
202
|
+
const w = node?.offsetWidth || minWidth;
|
|
203
|
+
const h = node?.offsetHeight || 0;
|
|
204
|
+
let left = a.left;
|
|
205
|
+
let top = a.bottom + gap;
|
|
206
|
+
if (left + w > window.innerWidth - EDGE) left = Math.max(EDGE, window.innerWidth - w - EDGE);
|
|
207
|
+
if (h && top + h > window.innerHeight - EDGE) {
|
|
208
|
+
const above = a.top - gap - h;
|
|
209
|
+
top = above >= EDGE ? above : Math.max(EDGE, window.innerHeight - h - EDGE);
|
|
210
|
+
}
|
|
211
|
+
setPos({ left, top });
|
|
212
|
+
}, [anchorRect, gap, minWidth]);
|
|
213
|
+
react.useLayoutEffect(() => {
|
|
214
|
+
if (!open) {
|
|
215
|
+
setPos(null);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
place();
|
|
219
|
+
if (autoFocus) panelRef.current?.focus?.();
|
|
220
|
+
}, [open, place, autoFocus]);
|
|
221
|
+
react.useEffect(() => {
|
|
222
|
+
if (!open || typeof document === "undefined") return;
|
|
223
|
+
const onPointerDown = (e) => {
|
|
224
|
+
const t = e.target;
|
|
225
|
+
if (panelRef.current?.contains(t)) return;
|
|
226
|
+
const anchor = anchorEl?.();
|
|
227
|
+
if (anchor && anchor.contains(t)) return;
|
|
228
|
+
onClose();
|
|
229
|
+
};
|
|
230
|
+
const onKey = (e) => {
|
|
231
|
+
if (e.key === "Escape") onClose();
|
|
232
|
+
};
|
|
233
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
234
|
+
document.addEventListener("keydown", onKey, true);
|
|
235
|
+
window.addEventListener("scroll", onClose, true);
|
|
236
|
+
window.addEventListener("resize", onClose);
|
|
237
|
+
window.addEventListener("blur", onClose);
|
|
238
|
+
return () => {
|
|
239
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
240
|
+
document.removeEventListener("keydown", onKey, true);
|
|
241
|
+
window.removeEventListener("scroll", onClose, true);
|
|
242
|
+
window.removeEventListener("resize", onClose);
|
|
243
|
+
window.removeEventListener("blur", onClose);
|
|
244
|
+
};
|
|
245
|
+
}, [open, onClose, anchorEl]);
|
|
246
|
+
if (!open) return null;
|
|
247
|
+
return /* @__PURE__ */ jsxRuntime.jsx(gui.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(PortalTheme, { name: themeName, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
248
|
+
MenuPanel,
|
|
249
|
+
{
|
|
250
|
+
panelRef: setPanel,
|
|
251
|
+
minWidth,
|
|
252
|
+
maxHeight,
|
|
253
|
+
tabIndex: -1,
|
|
254
|
+
onKeyDown: (e) => menuKeyDown(e, onClose),
|
|
255
|
+
style: {
|
|
256
|
+
position: "fixed",
|
|
257
|
+
left: pos?.left ?? 0,
|
|
258
|
+
top: pos?.top ?? 0,
|
|
259
|
+
zIndex: 1e5,
|
|
260
|
+
visibility: pos ? "visible" : "hidden"
|
|
261
|
+
},
|
|
262
|
+
children
|
|
263
|
+
}
|
|
264
|
+
) }) });
|
|
265
|
+
}
|
|
266
|
+
function DropdownMenu({
|
|
267
|
+
trigger,
|
|
268
|
+
items,
|
|
269
|
+
open,
|
|
270
|
+
defaultOpen = false,
|
|
271
|
+
onOpenChange,
|
|
272
|
+
minWidth = 200,
|
|
273
|
+
maxHeight
|
|
274
|
+
}) {
|
|
275
|
+
const [isOpen, setOpen] = gui.useControllableState({
|
|
276
|
+
prop: open,
|
|
277
|
+
defaultProp: defaultOpen,
|
|
278
|
+
onChange: onOpenChange
|
|
279
|
+
});
|
|
280
|
+
const anchorRef = react.useRef(null);
|
|
281
|
+
const anchorRect = react.useCallback(() => anchorRef.current?.getBoundingClientRect() ?? null, []);
|
|
282
|
+
const anchorEl = react.useCallback(() => anchorRef.current, []);
|
|
283
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
284
|
+
/* @__PURE__ */ jsxRuntime.jsx(gui.XStack, { ref: anchorRef, self: "flex-start", cursor: "pointer", onPress: () => setOpen(!isOpen), children: trigger }),
|
|
285
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
286
|
+
FloatingMenu,
|
|
287
|
+
{
|
|
288
|
+
open: isOpen,
|
|
289
|
+
onClose: () => setOpen(false),
|
|
290
|
+
anchorRect,
|
|
291
|
+
anchorEl,
|
|
292
|
+
minWidth,
|
|
293
|
+
maxHeight,
|
|
294
|
+
children: renderMenuItems(items, () => setOpen(false))
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
] });
|
|
298
|
+
}
|
|
299
|
+
function SelectMenu({
|
|
300
|
+
options,
|
|
301
|
+
value,
|
|
302
|
+
onChange,
|
|
303
|
+
allLabel = "All",
|
|
304
|
+
icon,
|
|
305
|
+
minWidth = 130,
|
|
306
|
+
required,
|
|
307
|
+
minHeight,
|
|
308
|
+
disabled,
|
|
309
|
+
ariaLabel
|
|
310
|
+
}) {
|
|
311
|
+
const active = value === null ? null : options.find((o) => o.key === value) ?? null;
|
|
312
|
+
const triggerLabel = active ? active.label : allLabel;
|
|
313
|
+
const items = [
|
|
314
|
+
...required ? [] : [{ key: "__all__", label: allLabel, selected: value === null, onSelect: () => onChange(null) }],
|
|
315
|
+
...options.map((o) => ({ key: o.key, label: o.label, selected: value === o.key, onSelect: () => onChange(o.key) }))
|
|
316
|
+
];
|
|
317
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
318
|
+
DropdownMenu,
|
|
319
|
+
{
|
|
320
|
+
minWidth: Math.max(minWidth, 160),
|
|
321
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
322
|
+
gui.Button,
|
|
323
|
+
{
|
|
324
|
+
size: minHeight && minHeight >= 40 ? "$4" : "$2",
|
|
325
|
+
minW: minWidth,
|
|
326
|
+
minH: minHeight,
|
|
327
|
+
disabled,
|
|
328
|
+
"aria-label": ariaLabel,
|
|
329
|
+
justify: "space-between",
|
|
330
|
+
borderWidth: 1,
|
|
331
|
+
borderColor: "$borderColor",
|
|
332
|
+
bg: !required && value !== null ? "$color5" : "transparent",
|
|
333
|
+
icon,
|
|
334
|
+
iconAfter: /* @__PURE__ */ jsxRuntime.jsx(lucideIcons2.ChevronDown, { size: 14, opacity: 0.6 }),
|
|
335
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(gui.Text, { fontSize: "$2", color: "$color12", numberOfLines: 1, flex: 1, children: triggerLabel })
|
|
336
|
+
}
|
|
337
|
+
),
|
|
338
|
+
items
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
exports.DropdownMenu = DropdownMenu;
|
|
344
|
+
exports.FloatingMenu = FloatingMenu;
|
|
345
|
+
exports.MenuItemView = MenuItemView;
|
|
346
|
+
exports.MenuLabelView = MenuLabelView;
|
|
347
|
+
exports.MenuPanel = MenuPanel;
|
|
348
|
+
exports.MenuSeparatorView = MenuSeparatorView;
|
|
349
|
+
exports.PortalTheme = PortalTheme;
|
|
350
|
+
exports.SelectMenu = SelectMenu;
|
|
351
|
+
exports.renderMenuItems = renderMenuItems;
|
|
352
|
+
//# sourceMappingURL=chunk-GWNNLYX4.cjs.map
|
|
353
|
+
//# sourceMappingURL=chunk-GWNNLYX4.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/product/menu/items.tsx","../src/product/menu/portal-theme.tsx","../src/product/menu/roving.ts","../src/product/menu/FloatingMenu.tsx","../src/product/menu/DropdownMenu.tsx","../src/product/SelectMenu.tsx"],"names":["jsx","YStack","useEmit","labelOf","jsxs","XStack","Text","Check","ChevronRight","Theme","useThemeName","useRef","useState","useCallback","useLayoutEffect","useEffect","Portal","useControllableState","Fragment","Button","ChevronDown"],"mappings":"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","file":"chunk-GWNNLYX4.cjs","sourcesContent":["'use client'\n\n/**\n * The ONE menu-item spec — every menu across the fleet renders through these\n * presentational primitives, so DropdownMenu, ContextMenu, SelectMenu and ComboBox\n * are pixel-identical. Geometry is literal px on a strict 8-grid (never random);\n * colour is theme-adaptive tokens ($color2/$color11/$color12/$borderColor) so light\n * and dark both work, with the single purple accent supplied by the brand CSS vars.\n *\n * Style props use the @hanzo/gui config shorthands (bg/items/justify/px/py/p/mx/my/\n * minW/maxH/rounded/select/shrink) — the config omits the longhand aliases.\n *\n * Panel — bg $color2 (#111 dark), hairline border, radius 12, inner pad 4, subtle\n * border+ambient elevation.\n * Item — height 30 (28–32 band), px 8, gap 8, radius 7; icon in a fixed 16px slot\n * on the left; label 13px $color12; right affordance (shortcut / check /\n * chevron) right-aligned. States: hover/active/focus → accent-soft, focus\n * visible; selected → check + purple; disabled → muted, no hover.\n * Separator — 1px hairline, 4px vertical margin.\n * Label — 11px uppercase muted section header, px 8.\n */\nimport type { ReactNode, KeyboardEvent } from 'react'\nimport { Text, XStack, YStack } from '@hanzo/gui'\nimport { Check, ChevronRight } from '@hanzogui/lucide-icons-2'\n\n// ── Geometry — literal px, 8-grid ──────────────────────────────────────────────\nconst ITEM_MIN_HEIGHT = 30\nconst ITEM_RADIUS = 7\nconst ITEM_PX = 8\nconst ITEM_GAP = 8\nconst ICON_SLOT = 16\nexport const PANEL_RADIUS = 12\nexport const PANEL_PAD = 4\nconst PANEL_GAP = 2\nconst SEP_MARGIN = 4\nconst FONT_LABEL = 13\nconst FONT_MUTED = 11\nconst FONT_SHORTCUT = 12\n\n// ── Colour — brand purple accent via CSS vars (fallbacks keep it correct even when\n// @hanzo/brand is not loaded, e.g. a bare Vite/Tauri host); everything else uses\n// theme-adaptive Tamagui tokens. ──────────────────────────────────────────────\nconst ACCENT = 'var(--hanzo-accent, #8b5cf6)'\nconst ACCENT_SOFT = 'var(--hanzo-accent-soft, rgba(139,92,246,0.16))'\nconst DANGER = 'var(--hanzo-danger, #ef4444)'\n\n// ── Declarative item model ──────────────────────────────────────────────────────\nexport type MenuItemSpec =\n | {\n type?: 'item'\n /** Stable key. */\n key: string\n label: string\n /** Leading Lucide icon (16px, unfilled) in the fixed left slot. */\n icon?: ReactNode\n /** Second muted line under the label (e.g. an id or hint). */\n description?: string\n /** Right-aligned shortcut / hint text (e.g. \"⌘K\"). */\n shortcut?: string\n /** Renders a right check + purple tint. */\n selected?: boolean\n disabled?: boolean\n /** Danger styling (red label/icon). */\n destructive?: boolean\n /** Renders a right chevron (submenu / drill-in affordance). */\n hasSubmenu?: boolean\n onSelect: () => void\n /** Keep the menu open after selecting (default: close). */\n closeOnSelect?: boolean\n }\n | { type: 'separator'; key?: string }\n | { type: 'label'; key?: string; label: string }\n\n// ── Surface ───────────────────────────────────────────────────────────────────\nexport function MenuPanel({\n children,\n minWidth = 200,\n maxHeight = 360,\n onKeyDown,\n panelRef,\n ...rest\n}: {\n children: ReactNode\n minWidth?: number\n maxHeight?: number\n onKeyDown?: (e: KeyboardEvent) => void\n /** Web DOM node of the panel (for measuring / edge-flip). */\n panelRef?: (node: HTMLElement | null) => void\n [key: string]: unknown\n}) {\n return (\n <YStack\n // Web DOM ref; Gui forwards it to the underlying node. Cast (not @ts-expect-error)\n // so it is env-agnostic — the ref type is strict under the pkg build, loose here.\n ref={panelRef as never}\n role=\"menu\"\n bg=\"$color2\"\n borderColor=\"$borderColor\"\n borderWidth={1}\n rounded={PANEL_RADIUS}\n p={PANEL_PAD}\n gap={PANEL_GAP}\n minW={minWidth}\n maxH={maxHeight}\n overflow=\"scroll\"\n // Subtle border + ambient elevation, minimal shadow.\n shadowColor=\"rgba(0,0,0,0.45)\"\n shadowRadius={20}\n shadowOffset={{ width: 0, height: 10 }}\n onKeyDown={onKeyDown as never}\n {...rest}\n >\n {children}\n </YStack>\n )\n}\n\n// ── Item — the ONE row ──────────────────────────────────────────────────────────\nimport { labelOf, useEmit } from '../instrument'\n\nexport function MenuItemView({\n icon,\n label,\n description,\n shortcut,\n selected = false,\n disabled = false,\n destructive = false,\n hasSubmenu = false,\n onSelect,\n}: Omit<Extract<MenuItemSpec, { type?: 'item' }>, 'key' | 'type' | 'closeOnSelect'>) {\n const track = useEmit()\n const choose = () => {\n track({ component: 'MenuItem', action: 'select', id: labelOf(label), value: selected })\n onSelect()\n }\n const press = () => {\n if (!disabled) choose()\n }\n const onKeyDown = (e: KeyboardEvent) => {\n if (disabled) return\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n choose()\n }\n }\n const labelColor = destructive ? DANGER : '$color12'\n return (\n <XStack\n role=\"menuitem\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled || undefined}\n items=\"center\"\n gap={ITEM_GAP}\n px={ITEM_PX}\n minH={ITEM_MIN_HEIGHT}\n rounded={ITEM_RADIUS}\n cursor={disabled ? 'default' : 'pointer'}\n opacity={disabled ? 0.4 : 1}\n select=\"none\"\n style={{ outline: 'none' }}\n hoverStyle={disabled ? {} : { bg: ACCENT_SOFT as never }}\n pressStyle={disabled ? {} : { bg: ACCENT_SOFT as never }}\n focusStyle={disabled ? {} : { bg: ACCENT_SOFT as never }}\n onPress={press}\n onKeyDown={onKeyDown as never}\n >\n <XStack width={ICON_SLOT} height={ICON_SLOT} items=\"center\" justify=\"center\" shrink={0}>\n {icon ? (\n <XStack items=\"center\" justify=\"center\" opacity={destructive ? 1 : 0.9} style={destructive ? { color: DANGER } : undefined}>\n {icon}\n </XStack>\n ) : null}\n </XStack>\n\n <YStack flex={1} minW={0}>\n <Text fontSize={FONT_LABEL} lineHeight={18} color={labelColor as never} numberOfLines={1}>\n {label}\n </Text>\n {description ? (\n <Text fontSize={FONT_MUTED} lineHeight={14} color=\"$color11\" numberOfLines={1}>\n {description}\n </Text>\n ) : null}\n </YStack>\n\n {shortcut ? (\n <Text fontSize={FONT_SHORTCUT} color=\"$color11\" shrink={0}>\n {shortcut}\n </Text>\n ) : null}\n {selected ? <Check size={14} color={ACCENT} /> : null}\n {hasSubmenu ? <ChevronRight size={14} color=\"var(--hanzo-muted, currentColor)\" opacity={0.6} /> : null}\n </XStack>\n )\n}\n\n// ── Separator ───────────────────────────────────────────────────────────────────\nexport function MenuSeparatorView() {\n return <YStack height={1} bg=\"$borderColor\" my={SEP_MARGIN} mx={SEP_MARGIN} role=\"separator\" />\n}\n\n// ── Section label ────────────────────────────────────────────────────────────────\nexport function MenuLabelView({ children }: { children: ReactNode }) {\n return (\n <Text\n fontSize={FONT_MUTED}\n color=\"$color11\"\n px={ITEM_PX}\n py={SEP_MARGIN}\n textTransform=\"uppercase\"\n letterSpacing={0.4}\n select=\"none\"\n >\n {children}\n </Text>\n )\n}\n\n// ── The single render path shared by every menu ──────────────────────────────────\nexport function renderMenuItems(items: MenuItemSpec[], close?: () => void): ReactNode {\n return items.map((it, i) => {\n if (it.type === 'separator') return <MenuSeparatorView key={it.key ?? `sep-${i}`} />\n if (it.type === 'label') return <MenuLabelView key={it.key ?? `lbl-${i}`}>{it.label}</MenuLabelView>\n const { key, onSelect, closeOnSelect = true, ...rest } = it\n return (\n <MenuItemView\n key={key}\n {...rest}\n onSelect={() => {\n onSelect()\n if (closeOnSelect) close?.()\n }}\n />\n )\n })\n}\n","'use client'\n\n/**\n * PortalTheme — the ONE fix for \"themed content escapes its theme through a portal\".\n *\n * @hanzo/gui's `Popover.Content` and `Portal` render their children in a SEPARATE\n * React subtree (a Gorhom-style portal host mounted at the app root), NOT via\n * `ReactDOM.createPortal`. React context therefore does NOT flow from the trigger's\n * location to the host. Under a nested `<Theme name=\"dark\">` the portaled content\n * lands OUTSIDE that theme context and Tamagui throws \"Missing theme\" (or renders\n * unthemed).\n *\n * The fix is two-part and must stay two-part:\n * 1. At the TRIGGER site (theme context still available) capture the resolved\n * theme name with `useThemeName()`.\n * 2. INSIDE the portaled content re-apply it: `<PortalTheme name={captured}>`.\n *\n * Every portal-backed menu in this package (DropdownMenu, ContextMenu, SelectMenu,\n * ComboBox) uses this so a menu renders identically whether it is opened under the\n * root theme or under any nested `<Theme>`, light or dark.\n */\nimport type { ReactNode } from 'react'\nimport { Theme, useThemeName } from '@hanzo/gui'\n\n/** Capture the current resolved theme name at the trigger site. Re-exported so the\n * capture + re-apply pair reads from one module. */\nexport { useThemeName }\n\nexport function PortalTheme({ name, children }: { name: string; children: ReactNode }) {\n // `name` is the resolved theme name (e.g. \"dark\", or a compound \"dark_purple\").\n // Tamagui's `Theme` accepts the resolved name and reconstructs the context.\n return <Theme name={name as never}>{children}</Theme>\n}\n","'use client'\n\n/**\n * Roving keyboard focus for a menu panel — ArrowUp/Down move focus between enabled\n * `[role=\"menuitem\"]` children, Home/End jump to ends, Escape closes. Web/desktop\n * only (guards on `document`); native menus have no pointer-keyboard nav. Shared by\n * DropdownMenu and ContextMenu so navigation is identical.\n */\nimport type { KeyboardEvent } from 'react'\n\nexport function menuKeyDown(e: KeyboardEvent, onClose?: () => void): void {\n if (e.key === 'Escape') {\n onClose?.()\n return\n }\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp' && e.key !== 'Home' && e.key !== 'End') return\n if (typeof document === 'undefined') return\n\n const panel = e.currentTarget as HTMLElement\n const items = Array.from(\n panel.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])'),\n )\n if (items.length === 0) return\n e.preventDefault()\n\n const active = document.activeElement as HTMLElement | null\n const current = active ? items.indexOf(active) : -1\n let next: number\n if (e.key === 'Home') next = 0\n else if (e.key === 'End') next = items.length - 1\n else if (e.key === 'ArrowDown') next = current < 0 ? 0 : (current + 1) % items.length\n else next = current <= 0 ? items.length - 1 : current - 1\n items[next]?.focus()\n}\n","'use client'\n\n/**\n * FloatingMenu — the ONE floating-panel mechanism for every menu. Renders the shared\n * MenuPanel through the @hanzo/gui Portal, positioned at an anchor (a trigger rect or a\n * cursor point), theme-forwarded via PortalTheme, edge-flipped, dismiss-wired and\n * keyboard-navigable.\n *\n * Why Portal, not gui Popover: gui's Popover pulls in a SheetController that re-roots the\n * trigger subtree and reads the theme from React context — on gui-native hosts\n * (react-native-web-lite, theme in context, no CSS-class fallback) that throws\n * \"Missing theme\" AT MOUNT. The raw Portal does not re-root, so DropdownMenu, ContextMenu,\n * SelectMenu and ComboBox all ride this ONE working path.\n */\nimport type { ReactNode, KeyboardEvent as ReactKeyboardEvent } from 'react'\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { Portal } from '@hanzo/gui'\nimport { MenuPanel } from './items'\nimport { PortalTheme, useThemeName } from './portal-theme'\nimport { menuKeyDown } from './roving'\n\nconst EDGE = 8\n\nexport type RectLike = { left: number; top: number; right: number; bottom: number; width: number; height: number }\n\nexport function FloatingMenu({\n open,\n onClose,\n anchorRect,\n anchorEl,\n gap = 4,\n minWidth = 200,\n maxHeight,\n autoFocus = true,\n children,\n}: {\n open: boolean\n onClose: () => void\n /** Anchor in viewport coords — a trigger's rect, or a zero-size rect at a cursor point. */\n anchorRect: () => RectLike | null | undefined\n /** Element excluded from outside-dismiss (the trigger / input row). */\n anchorEl?: () => HTMLElement | null | undefined\n /** Space between the anchor's bottom and the panel (0 for a cursor menu). */\n gap?: number\n minWidth?: number\n maxHeight?: number\n /** Focus the panel on open (for roving keys). ComboBox keeps focus in its input. */\n autoFocus?: boolean\n children: ReactNode\n}) {\n const themeName = useThemeName()\n const panelRef = useRef<HTMLElement | null>(null)\n const [pos, setPos] = useState<{ left: number; top: number } | null>(null)\n\n const setPanel = useCallback((n: HTMLElement | null) => {\n panelRef.current = n\n }, [])\n\n const place = useCallback(() => {\n if (typeof window === 'undefined') return\n const a = anchorRect()\n if (!a) return\n const node = panelRef.current\n const w = node?.offsetWidth || minWidth\n const h = node?.offsetHeight || 0\n let left = a.left\n let top = a.bottom + gap\n if (left + w > window.innerWidth - EDGE) left = Math.max(EDGE, window.innerWidth - w - EDGE)\n if (h && top + h > window.innerHeight - EDGE) {\n const above = a.top - gap - h\n top = above >= EDGE ? above : Math.max(EDGE, window.innerHeight - h - EDGE)\n }\n setPos({ left, top })\n }, [anchorRect, gap, minWidth])\n\n // Measure + position before paint; focus the panel for keyboard nav.\n useLayoutEffect(() => {\n if (!open) {\n setPos(null)\n return\n }\n place()\n if (autoFocus) panelRef.current?.focus?.()\n }, [open, place, autoFocus])\n\n // Dismiss on outside pointer (excluding the anchor), Escape, scroll, resize, blur.\n useEffect(() => {\n if (!open || typeof document === 'undefined') return\n const onPointerDown = (e: PointerEvent) => {\n const t = e.target as Node\n if (panelRef.current?.contains(t)) return\n const anchor = anchorEl?.()\n if (anchor && anchor.contains(t)) return\n onClose()\n }\n const onKey = (e: globalThis.KeyboardEvent) => {\n if (e.key === 'Escape') onClose()\n }\n document.addEventListener('pointerdown', onPointerDown, true)\n document.addEventListener('keydown', onKey, true)\n window.addEventListener('scroll', onClose, true)\n window.addEventListener('resize', onClose)\n window.addEventListener('blur', onClose)\n return () => {\n document.removeEventListener('pointerdown', onPointerDown, true)\n document.removeEventListener('keydown', onKey, true)\n window.removeEventListener('scroll', onClose, true)\n window.removeEventListener('resize', onClose)\n window.removeEventListener('blur', onClose)\n }\n }, [open, onClose, anchorEl])\n\n if (!open) return null\n return (\n <Portal>\n <PortalTheme name={themeName}>\n <MenuPanel\n panelRef={setPanel}\n minWidth={minWidth}\n maxHeight={maxHeight}\n tabIndex={-1}\n onKeyDown={(e: ReactKeyboardEvent) => menuKeyDown(e, onClose)}\n style={{\n position: 'fixed',\n left: pos?.left ?? 0,\n top: pos?.top ?? 0,\n zIndex: 100000,\n visibility: pos ? 'visible' : 'hidden',\n }}\n >\n {children}\n </MenuPanel>\n </PortalTheme>\n </Portal>\n )\n}\n","'use client'\n\n/**\n * DropdownMenu — a portal-theme-safe click menu. Declarative `items` rendered through the\n * ONE shared menu-item spec, so it is pixel-identical to ContextMenu/SelectMenu/ComboBox.\n * Built on FloatingMenu (gui Portal), NOT the gui Popover — so it mounts without the\n * Sheet re-root that loses theme context on gui-native hosts (react-native-web-lite).\n *\n * <DropdownMenu\n * trigger={<Button>Actions</Button>}\n * items={[\n * { key: 'rename', label: 'Rename', icon: <Pencil size={16} />, onSelect: rename },\n * { type: 'separator' },\n * { key: 'del', label: 'Delete', destructive: true, onSelect: del },\n * ]}\n * />\n */\nimport type { ReactElement } from 'react'\nimport { useCallback, useRef } from 'react'\nimport { XStack, useControllableState } from '@hanzo/gui'\nimport { renderMenuItems, type MenuItemSpec } from './items'\nimport { FloatingMenu } from './FloatingMenu'\n\nexport type DropdownMenuProps = {\n /** The clickable element. Wrapped so any element opens the menu on press. */\n trigger: ReactElement\n items: MenuItemSpec[]\n /** Controlled open state. Omit for uncontrolled. */\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n minWidth?: number\n maxHeight?: number\n}\n\nexport function DropdownMenu({\n trigger,\n items,\n open,\n defaultOpen = false,\n onOpenChange,\n minWidth = 200,\n maxHeight,\n}: DropdownMenuProps) {\n const [isOpen, setOpen] = useControllableState({\n prop: open,\n defaultProp: defaultOpen,\n onChange: onOpenChange,\n })\n const anchorRef = useRef<HTMLElement | null>(null)\n const anchorRect = useCallback(() => anchorRef.current?.getBoundingClientRect() ?? null, [])\n const anchorEl = useCallback(() => anchorRef.current, [])\n\n return (\n <>\n <XStack ref={anchorRef as never} self=\"flex-start\" cursor=\"pointer\" onPress={() => setOpen(!isOpen)}>\n {trigger}\n </XStack>\n <FloatingMenu\n open={isOpen}\n onClose={() => setOpen(false)}\n anchorRect={anchorRect}\n anchorEl={anchorEl}\n minWidth={minWidth}\n maxHeight={maxHeight}\n >\n {renderMenuItems(items, () => setOpen(false))}\n </FloatingMenu>\n </>\n )\n}\n","'use client'\n\n/**\n * SelectMenu — a compact, reusable dropdown select. Renders a labelled trigger (\"All\n * types\", or the chosen option) and a menu list with a check on the active option.\n * Prop-driven + self-contained. `value === null` is the \"all\"/unfiltered state.\n *\n * `required` turns the same control into a \"pick exactly one of N\": the null row is\n * dropped and the value is never nullable. That is the SAME control, not a second\n * one — a required field picker and a filter differ only in whether \"none\" is an\n * option, so it stays one component rather than growing a fourth select.\n *\n * A thin DropdownMenu (the ONE menu mechanism) so it is pixel-identical to every other\n * menu and rides the same portal-theme-safe Portal path — no gui Popover, no Sheet\n * re-root, works on gui-native hosts.\n */\nimport type { ReactElement } from 'react'\nimport { Button, Text } from '@hanzo/gui'\nimport { ChevronDown } from '@hanzogui/lucide-icons-2'\nimport { DropdownMenu } from './menu/DropdownMenu'\nimport type { MenuItemSpec } from './menu/items'\n\nexport type SelectOption<T extends string> = { key: T; label: string }\n\nexport function SelectMenu<T extends string>({\n options,\n value,\n onChange,\n allLabel = 'All',\n icon,\n minWidth = 130,\n required,\n minHeight,\n disabled,\n ariaLabel,\n}: {\n options: SelectOption<T>[]\n value: T | null\n onChange: (v: T | null) => void\n /** Label shown (and first menu row) for the unfiltered `null` state. */\n allLabel?: string\n /** Optional leading icon in the trigger. */\n icon?: ReactElement\n minWidth?: number\n /** Exactly one of `options` is always chosen — no \"all\"/none row. */\n required?: boolean\n /** Trigger height floor — pass the 44px tap floor on a phone. */\n minHeight?: number\n disabled?: boolean\n /** Accessible name for an unlabelled trigger. */\n ariaLabel?: string\n}) {\n const active = value === null ? null : options.find((o) => o.key === value) ?? null\n const triggerLabel = active ? active.label : allLabel\n\n const items: MenuItemSpec[] = [\n ...(required\n ? []\n : [{ key: '__all__', label: allLabel, selected: value === null, onSelect: () => onChange(null) }]),\n ...options.map((o) => ({ key: o.key, label: o.label, selected: value === o.key, onSelect: () => onChange(o.key) })),\n ]\n\n return (\n <DropdownMenu\n minWidth={Math.max(minWidth, 160)}\n trigger={\n <Button\n size={minHeight && minHeight >= 40 ? '$4' : '$2'}\n minW={minWidth}\n minH={minHeight}\n disabled={disabled}\n aria-label={ariaLabel}\n justify=\"space-between\"\n borderWidth={1}\n borderColor=\"$borderColor\"\n bg={!required && value !== null ? '$color5' : 'transparent'}\n icon={icon}\n iconAfter={<ChevronDown size={14} opacity={0.6} />}\n >\n <Text fontSize=\"$2\" color=\"$color12\" numberOfLines={1} flex={1}>\n {triggerLabel}\n </Text>\n </Button>\n }\n items={items}\n />\n )\n}\n"]}
|