@epilot/spark-ui 0.23.2 → 0.24.1

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.
Files changed (58) hide show
  1. package/dist/components/attachment.d.cts +1 -1
  2. package/dist/components/attachment.d.ts +1 -1
  3. package/dist/components/autocomplete.cjs +1 -1
  4. package/dist/components/autocomplete.cjs.map +1 -1
  5. package/dist/components/autocomplete.js +1 -1
  6. package/dist/components/autocomplete.js.map +1 -1
  7. package/dist/components/avatar.js +1 -1
  8. package/dist/components/badge.js +1 -1
  9. package/dist/components/box.js +1 -1
  10. package/dist/components/bubble.d.cts +1 -1
  11. package/dist/components/bubble.d.ts +1 -1
  12. package/dist/components/button-group.js +1 -1
  13. package/dist/components/button.d.cts +3 -3
  14. package/dist/components/button.d.ts +3 -3
  15. package/dist/components/button.js +1 -1
  16. package/dist/components/card.js +1 -1
  17. package/dist/components/checkbox.js +1 -1
  18. package/dist/components/dropdown-menu.cjs +1 -1
  19. package/dist/components/dropdown-menu.cjs.map +1 -1
  20. package/dist/components/dropdown-menu.js +1 -1
  21. package/dist/components/dropdown-menu.js.map +1 -1
  22. package/dist/components/icon.js +1 -1
  23. package/dist/components/input-otp.cjs +1 -1
  24. package/dist/components/input-otp.js +1 -1
  25. package/dist/components/input.cjs +1 -1
  26. package/dist/components/input.js +1 -1
  27. package/dist/components/list.cjs +1 -1
  28. package/dist/components/list.cjs.map +1 -1
  29. package/dist/components/list.js +1 -1
  30. package/dist/components/list.js.map +1 -1
  31. package/dist/components/marker.d.cts +1 -1
  32. package/dist/components/marker.d.ts +1 -1
  33. package/dist/components/message-scroller.cjs +1 -1
  34. package/dist/components/message-scroller.js +1 -1
  35. package/dist/components/pill.js +1 -1
  36. package/dist/components/popover.cjs +1 -1
  37. package/dist/components/popover.cjs.map +1 -1
  38. package/dist/components/popover.js +1 -1
  39. package/dist/components/popover.js.map +1 -1
  40. package/dist/components/radio.js +1 -1
  41. package/dist/components/select.cjs +4 -4
  42. package/dist/components/select.cjs.map +1 -1
  43. package/dist/components/select.js +4 -4
  44. package/dist/components/select.js.map +1 -1
  45. package/dist/components/switch.js +1 -1
  46. package/dist/components/textarea.cjs +1 -1
  47. package/dist/components/textarea.js +1 -1
  48. package/dist/components/theme.js +1 -1
  49. package/dist/components/toggle-group.js +1 -1
  50. package/dist/components/toggle.cjs +1 -1
  51. package/dist/components/toggle.js +2 -2
  52. package/dist/components/trace.cjs +88 -0
  53. package/dist/components/trace.cjs.map +1 -0
  54. package/dist/components/trace.d.cts +53 -0
  55. package/dist/components/trace.d.ts +53 -0
  56. package/dist/components/trace.js +78 -0
  57. package/dist/components/trace.js.map +1 -0
  58. package/package.json +6 -2
@@ -1 +1 @@
1
- {"version":3,"file":"list.cjs","names":["React","Skeleton"],"sources":["../../src/components/list.tsx"],"sourcesContent":["import { useRender } from '@base-ui/react/use-render'\nimport * as React from 'react'\nimport type { ContainerRadius } from '../lib/container-chrome'\nimport type { SparkCardColor, SparkCardVariant } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { Skeleton } from './skeleton'\n\ntype ListVariant = SparkCardVariant\ntype ListRadius = ContainerRadius\n\n// Rounds only the first/last row of a run, regardless of variant — harmless on ghost\n// (nothing painted to clip). Keyed on `data-list-radius` alone: unlike fill (below),\n// rounding doesn't depend on variant/color, so no compound group-data selector is needed.\n// Named `list-radius`, not `radius`: the global Roundness knob (Theme) also writes a\n// bare `data-radius` via an unscoped `[data-radius=...]` selector in spark-default.css —\n// `none`/`full` are spelled the same in both scales, so a plain `data-radius` here would\n// collide and let List silently override the app-wide knob for its whole subtree.\nconst radiusRowClasses: Record<ListRadius, string> = {\n none: 'group-data-[list-radius=none]/list:first:rounded-t-none group-data-[list-radius=none]/list:last:rounded-b-none',\n sm: 'group-data-[list-radius=sm]/list:first:rounded-t-sm group-data-[list-radius=sm]/list:last:rounded-b-sm',\n md: 'group-data-[list-radius=md]/list:first:rounded-t-md group-data-[list-radius=md]/list:last:rounded-b-md',\n lg: 'group-data-[list-radius=lg]/list:first:rounded-t-lg group-data-[list-radius=lg]/list:last:rounded-b-lg',\n xl: 'group-data-[list-radius=xl]/list:first:rounded-t-xl group-data-[list-radius=xl]/list:last:rounded-b-xl',\n '2xl':\n 'group-data-[list-radius=2xl]/list:first:rounded-t-2xl group-data-[list-radius=2xl]/list:last:rounded-b-2xl',\n '3xl':\n 'group-data-[list-radius=3xl]/list:first:rounded-t-3xl group-data-[list-radius=3xl]/list:last:rounded-b-3xl',\n full: 'group-data-[list-radius=full]/list:first:rounded-t-full group-data-[list-radius=full]/list:last:rounded-b-full',\n}\n\n// ListItem publishes `recent` so ListItemTitle can render the inline recency dot; the\n// column context lets the title show it only on the primary (start) column, never the\n// value column of a two-column row.\nconst ListItemContext = React.createContext<{ recent: boolean }>({\n recent: false,\n})\nconst ListItemColumnContext = React.createContext<{ isEnd: boolean }>({\n isEnd: false,\n})\n\ninterface ListProps extends React.ComponentProps<'ul'> {\n /** A prop, not the ambient `data-style` knob — that would cascade into row content. */\n variant?: ListVariant\n /** Chrome palette. `neutral` (default) tracks the app neutral; `accent` tints with the brand accent. */\n color?: SparkCardColor\n /** Corner radius, as a named step. Unset → `lg`, today's fixed behaviour. */\n radius?: ListRadius\n /** How many `--spark-layout-gap` units apart each row sits (`soft`/`solid`/`ghost` only). */\n gap?: number\n}\n\nfunction List({\n className,\n variant = 'soft',\n color = 'neutral',\n radius = 'lg',\n gap = 0.5,\n style,\n ...props\n}: ListProps) {\n // Fill/border-colour needs BOTH axes at once (e.g. \"soft, tinted with accent\"), which a\n // single group-data selector can't express and chaining two on the same named group\n // can't either (that generates a descendant match between two *different* ancestors,\n // not one ancestor bearing both attributes). Resolving the pair to one value up front\n // sidesteps it entirely — see the `data-look` selectors on ListItem below.\n const look = color === 'accent' ? `${variant}-accent` : variant\n return (\n <ul\n // role=list keeps list semantics even under list-style:none (Safari drops them).\n role=\"list\"\n data-slot=\"list\"\n // group/list + data-variant drive every item's per-variant chrome (below).\n data-variant={variant}\n data-look={look}\n data-list-radius={radius}\n className={cn(\n 'group/list m-0 list-none p-0',\n // soft/solid = separate rows → stack with a small gap (the gap IS the divider).\n // ghost shares that same gap for spacing but no fill/border (see ListItem's\n // chrome block, which has no ghost selector). surface/outline glue rows into one\n // panel with no gap at all.\n 'data-[variant=soft]:flex data-[variant=soft]:flex-col data-[variant=soft]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n 'data-[variant=solid]:flex data-[variant=solid]:flex-col data-[variant=solid]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n 'data-[variant=ghost]:flex data-[variant=ghost]:flex-col data-[variant=ghost]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n className,\n )}\n style={\n {\n '--list-gap-steps': gap,\n // solid's fill inverts far enough that the row's own ink must invert with it\n // (unlike soft/surface/outline's translucent tints, where the default ink\n // stays legible regardless of colour) — matches Card's own solid recipe,\n // where fg and fg-muted both collapse to the one contrast-guaranteed step.\n ...(variant === 'solid' && {\n '--list-item-fg':\n color === 'accent'\n ? 'var(--spark-contrast)'\n : 'var(--neutral-12)',\n '--list-item-fg-muted':\n color === 'accent'\n ? 'var(--spark-contrast)'\n : 'var(--neutral-12)',\n }),\n ...style,\n } as React.CSSProperties\n }\n {...props}\n />\n )\n}\n\ninterface ListItemProps extends React.ComponentProps<'li'> {\n /** The \"recently changed\" accent dot. */\n recent?: boolean\n /** Persistent accent-tint (e.g. the active document). */\n selected?: boolean\n /** Loading placeholder (Figma `skeleton=true`). */\n skeleton?: boolean\n /**\n * Base UI polymorphism for the inner row. Pass `<a href/>` or `<button/>` to make\n * the row interactive (accent-on-hover/focus, cursor, focus ring). Omit → static row.\n */\n render?: useRender.RenderProp\n}\n\nfunction ListItem({\n className,\n children,\n recent,\n selected,\n skeleton,\n render,\n ...props\n}: ListItemProps) {\n // Skeleton rows are decorative placeholders — never render as a real <a>/<button>. An\n // aria-hidden anchor would still be focusable (WCAG 4.1.2), so skeleton forces a plain,\n // non-interactive <div> regardless of `render`.\n const interactive = !skeleton && render != null\n\n const recentCtx = React.useMemo(() => ({ recent: recent ?? false }), [recent])\n\n // Only a ListItemLeading actually marked `interactive` (a Checkbox/Radio/Button) needs\n // to move outside `render`'s element, and only when `render` is literally an <a>/\n // <button> — nesting a focusable control inside another interactive element is invalid\n // HTML and double-fires clicks. A `render` of anything else (a `<Card/>`, say) has no\n // such conflict: it isn't natively interactive itself, so a control inside it is valid,\n // unduplicated HTML, and hoisting would only pull the control outside whatever that\n // element paints (its fill, border, rounded corner) for no reason. A plain icon\n // (interactive unset) is the same story from the other side — either way, Leading stays\n // inside the row by default, so the whole visual row remains one click/tap target.\n const rendersAnchorOrButton =\n React.isValidElement(render) &&\n (render.type === 'a' || render.type === 'button')\n const childArray = skeleton ? undefined : React.Children.toArray(children)\n const leadingChild = childArray?.find(\n (child): child is React.ReactElement<ListItemLeadingProps> =>\n React.isValidElement(child) && child.type === ListItemLeading,\n )\n const hoistLeading =\n rendersAnchorOrButton && Boolean(leadingChild?.props.interactive)\n const leading = hoistLeading ? leadingChild : undefined\n\n const row = useRender({\n defaultTagName: 'div',\n render: skeleton ? undefined : render,\n props: {\n 'data-slot': 'list-item-row',\n // skeleton is a decorative placeholder → keep it out of the a11y tree.\n 'aria-hidden': skeleton ? true : undefined,\n className: cn(\n // row layout from Figma 2217-1667; min-h is a HARD 44px floor (WCAG 2.5.5),\n // py/gap/text still scale past it via the spacing/scaling knobs. no-underline\n // resets the default <a> decoration when the row renders as a link.\n 'flex min-h-[2.75rem] items-center gap-4 py-3 text-left no-underline',\n hoistLeading ? 'min-w-0 flex-1 pr-4' : 'w-full rounded-[inherit] px-4',\n // not hoisted → this row IS the whole visual item (no outer <li> chrome), so\n // selection tint lives here, same as a row with no leading slot at all.\n !hoistLeading && !skeleton && selected && 'bg-spark-a3',\n interactive && [\n 'group/row cursor-pointer transition-colors duration-150 motion-reduce:transition-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n hoistLeading\n ? // the custom focus ring is drawn on the outer <li> (has-focus-visible,\n // below) so it wraps the leading control too — but the browser's own\n // default outline still targets whichever element actually has focus,\n // which is this one, so it has to be suppressed here specifically.\n 'focus-visible:outline-hidden'\n : [\n 'hover:bg-spark-a3 focus-visible:bg-spark-a3',\n 'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden',\n ],\n ],\n ),\n children: skeleton ? (\n <ListItemSkeleton />\n ) : hoistLeading ? (\n childArray?.filter((child) => child !== leadingChild)\n ) : (\n children\n ),\n },\n })\n\n return (\n <li\n data-slot=\"list-item\"\n className={cn(\n 'relative has-focus-visible:z-10',\n hoistLeading && [\n 'flex min-w-0 items-center gap-4 pl-4',\n !skeleton && selected && 'bg-spark-a3',\n interactive && [\n // neutral at rest → subtle accent tint on hover/focus (the affordance goes\n // accent too, in ListItemTrailing; title + leading stay neutral). The click\n // target itself is `row`, so focus-visible (unlike hover) has to be read off\n // a descendant via has() — it never lands on this <li> directly.\n 'cursor-pointer',\n 'hover:bg-spark-a3 has-focus-visible:bg-spark-a3',\n 'has-focus-visible:ring-2 has-focus-visible:ring-offset-2',\n ],\n ],\n // soft/solid rows sit in a gapped stack, not a glued panel, but still read as one\n // rounded group: only the first/last row round the outer corners. surface/\n // outline glue rows into one bordered panel instead — border SIDES are variant-\n // only (no colour axis), so they stay on data-variant; fill and border COLOUR\n // depend on variant *and* colour together, so those live on data-look instead\n // (see the comment on `look` in List, above).\n 'group-data-[variant=surface]/list:border-x group-data-[variant=surface]/list:border-t group-data-[variant=surface]/list:last:border-b',\n 'group-data-[variant=outline]/list:border-x group-data-[variant=outline]/list:border-t group-data-[variant=outline]/list:last:border-b',\n 'group-data-[look=soft]/list:bg-gray-a2 dark:group-data-[look=soft]/list:bg-gray-a3',\n // arbitrary-var form, not the bg-spark-a3 utility: identical colour, but avoids a\n // literal substring collision with the unconditional selection/hover tint below,\n // which a couple of tests distinguish with a plain className.includes check.\n 'group-data-[look=soft-accent]/list:bg-(--spark-a3)',\n 'group-data-[look=surface]/list:border-gray-a6 group-data-[look=surface]/list:bg-gray-2',\n 'group-data-[look=surface-accent]/list:border-spark-a6 group-data-[look=surface-accent]/list:bg-(--spark-surface)',\n 'group-data-[look=outline]/list:border-gray-a6',\n 'group-data-[look=outline-accent]/list:border-spark-a6',\n 'group-data-[look=solid]/list:bg-(--neutral-1)',\n 'group-data-[look=solid-accent]/list:bg-spark-9',\n Object.values(radiusRowClasses),\n className,\n )}\n {...props}\n >\n <ListItemContext.Provider value={recentCtx}>\n {leading}\n {row}\n </ListItemContext.Provider>\n </li>\n )\n}\n\nfunction ListItemSkeleton() {\n // Compose the shared Skeleton primitive so the placeholder matches every other skeleton\n // (token + radius + animation + motion-reduce).\n return (\n <>\n <Skeleton className=\"size-10 shrink-0 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Skeleton className=\"h-3.5 w-1/3\" />\n <Skeleton className=\"h-3.5 w-2/3\" />\n </div>\n <div className=\"flex min-w-0 flex-1 flex-col items-end gap-2\">\n <Skeleton className=\"h-3.5 w-1/4\" />\n <Skeleton className=\"h-3.5 w-1/2\" />\n </div>\n </>\n )\n}\n\ninterface ListItemLeadingProps extends React.ComponentProps<'div'> {\n /**\n * Set when this slot holds a focusable control (Checkbox, Radio, Button). ListItem\n * then hoists it outside `render`'s element — nesting a real control inside the row's\n * own <a>/<button> is invalid HTML and double-fires clicks. Leave unset for decorative\n * content (an icon): it stays inside the row, so the whole visual row is one\n * click/tap target, same as a row with no leading slot at all.\n */\n interactive?: boolean\n}\n\nfunction ListItemLeading({\n className,\n interactive: _interactive,\n ...props\n}: ListItemLeadingProps) {\n return (\n <div\n data-slot=\"list-item-leading\"\n className={cn('flex shrink-0 items-center', className)}\n {...props}\n />\n )\n}\n\ninterface ListItemContentProps extends React.ComponentProps<'div'> {\n /** `end` right-aligns the column (the value column). */\n align?: 'start' | 'end'\n}\n\nfunction ListItemContent({\n className,\n align = 'start',\n ...props\n}: ListItemContentProps) {\n const columnCtx = React.useMemo(() => ({ isEnd: align === 'end' }), [align])\n return (\n <ListItemColumnContext.Provider value={columnCtx}>\n <div\n data-slot=\"list-item-content\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col gap-1',\n align === 'end' ? 'items-end text-right' : 'items-start text-left',\n className,\n )}\n {...props}\n />\n </ListItemColumnContext.Provider>\n )\n}\n\nfunction ListItemPreline({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"list-item-preline\"\n className={cn(\n 'block w-full truncate text-sm text-(--list-item-fg-muted,var(--neutral-11))',\n className,\n )}\n {...props}\n />\n )\n}\n\ninterface ListItemTitleProps extends React.ComponentProps<'span'> {\n /** `strong` keeps the title bold, e.g. for menu rows; `default` is medium, bold on a `recent` row. */\n emphasis?: 'default' | 'strong'\n}\n\nfunction ListItemTitle({\n className,\n children,\n emphasis = 'default',\n ...props\n}: ListItemTitleProps) {\n const { recent } = React.useContext(ListItemContext)\n const { isEnd } = React.useContext(ListItemColumnContext)\n // recency dot sits inline, immediately left of the title (Figma 1310-977) — only on the\n // primary (start) column, so a two-column value row's value never carries it.\n const showDot = recent && !isEnd\n\n // className/{...props} always land on THIS span (the one that truncates), so a caller's\n // override (e.g. `title`, `line-clamp-2 whitespace-normal`) behaves identically whether\n // or not the dot renders — only the wrapping structure differs below.\n const title = (\n <span\n data-slot=\"list-item-title\"\n className={cn(\n showDot ? 'min-w-0 truncate' : 'block w-full truncate',\n 'text-base text-(--list-item-fg,var(--neutral-12))',\n recent || emphasis === 'strong' ? 'font-bold' : 'font-medium',\n className,\n )}\n {...props}\n >\n {children}\n </span>\n )\n\n if (!showDot) {\n // No dot: stay a plain block so the column's text alignment (e.g. an end column's\n // text-right) governs the title directly.\n return title\n }\n\n return (\n <span className=\"flex w-full items-center gap-2\">\n <span\n aria-hidden=\"true\"\n data-slot=\"list-item-indicator\"\n className=\"size-2 shrink-0 rounded-full bg-spark-9\"\n />\n {title}\n </span>\n )\n}\n\nfunction ListItemSubline({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"list-item-subline\"\n className={cn(\n 'block w-full truncate text-sm text-(--list-item-fg-muted,var(--neutral-11))',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction ListItemTrailing({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"list-item-trailing\"\n className={cn(\n 'flex shrink-0 items-center text-gray-11',\n // interactive rows tint the affordance accent on hover/focus (group/row is set on\n // the interactive inner row; absent on static rows, so this simply never fires).\n // Relies on the child reading currentColor (Spark icons do) — the colour-\n // independent Icon component hardcodes its own ink and won't pick this up; use\n // a Spark icon here, not Icon.\n 'transition-colors duration-150 motion-reduce:transition-none',\n 'group-hover/row:text-spark-a11 group-focus-visible/row:text-spark-a11',\n className,\n )}\n {...props}\n />\n )\n}\n\nexport {\n List,\n ListItem,\n ListItemContent,\n ListItemLeading,\n ListItemPreline,\n ListItemSubline,\n ListItemTitle,\n ListItemTrailing,\n}\nexport type {\n ListProps,\n ListItemProps,\n ListItemContentProps,\n ListItemLeadingProps,\n ListItemTitleProps,\n ListVariant,\n ListRadius,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"list.cjs","names":["React","Skeleton"],"sources":["../../src/components/list.tsx"],"sourcesContent":["import { useRender } from '@base-ui/react/use-render'\nimport * as React from 'react'\nimport type { ContainerRadius } from '../lib/container-chrome'\nimport type { SparkCardColor, SparkCardVariant } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { Skeleton } from './skeleton'\n\ntype ListVariant = SparkCardVariant\ntype ListRadius = ContainerRadius\n\n// Rounds only the first/last row of a run, regardless of variant — harmless on ghost\n// (nothing painted to clip). Keyed on `data-list-radius` alone: unlike fill (below),\n// rounding doesn't depend on variant/color, so no compound group-data selector is needed.\n// Named `list-radius`, not `radius`: the global Roundness knob (Theme) also writes a\n// bare `data-radius` via an unscoped `[data-radius=...]` selector in spark-default.css —\n// `none`/`full` are spelled the same in both scales, so a plain `data-radius` here would\n// collide and let List silently override the app-wide knob for its whole subtree.\nconst radiusRowClasses: Record<ListRadius, string> = {\n none: 'group-data-[list-radius=none]/list:first:rounded-t-none group-data-[list-radius=none]/list:last:rounded-b-none',\n sm: 'group-data-[list-radius=sm]/list:first:rounded-t-sm group-data-[list-radius=sm]/list:last:rounded-b-sm',\n md: 'group-data-[list-radius=md]/list:first:rounded-t-md group-data-[list-radius=md]/list:last:rounded-b-md',\n lg: 'group-data-[list-radius=lg]/list:first:rounded-t-lg group-data-[list-radius=lg]/list:last:rounded-b-lg',\n xl: 'group-data-[list-radius=xl]/list:first:rounded-t-xl group-data-[list-radius=xl]/list:last:rounded-b-xl',\n '2xl':\n 'group-data-[list-radius=2xl]/list:first:rounded-t-2xl group-data-[list-radius=2xl]/list:last:rounded-b-2xl',\n '3xl':\n 'group-data-[list-radius=3xl]/list:first:rounded-t-3xl group-data-[list-radius=3xl]/list:last:rounded-b-3xl',\n full: 'group-data-[list-radius=full]/list:first:rounded-t-full group-data-[list-radius=full]/list:last:rounded-b-full',\n}\n\n// ListItem publishes `recent` so ListItemTitle can render the inline recency dot; the\n// column context lets the title show it only on the primary (start) column, never the\n// value column of a two-column row.\nconst ListItemContext = React.createContext<{ recent: boolean }>({\n recent: false,\n})\nconst ListItemColumnContext = React.createContext<{ isEnd: boolean }>({\n isEnd: false,\n})\n\ninterface ListProps extends React.ComponentProps<'ul'> {\n /** A prop, not the ambient `data-style` knob — that would cascade into row content. */\n variant?: ListVariant\n /** Chrome palette. `neutral` (default) tracks the app neutral; `accent` tints with the brand accent. */\n color?: SparkCardColor\n /** Corner radius, as a named step. Unset → `lg`, today's fixed behaviour. */\n radius?: ListRadius\n /** How many `--spark-layout-gap` units apart each row sits (`soft`/`solid`/`ghost` only). */\n gap?: number\n}\n\nfunction List({\n className,\n variant = 'soft',\n color = 'neutral',\n radius = 'lg',\n gap = 0.5,\n style,\n ...props\n}: ListProps) {\n // Fill/border-colour needs BOTH axes at once (e.g. \"soft, tinted with accent\"), which a\n // single group-data selector can't express and chaining two on the same named group\n // can't either (that generates a descendant match between two *different* ancestors,\n // not one ancestor bearing both attributes). Resolving the pair to one value up front\n // sidesteps it entirely — see the `data-look` selectors on ListItem below.\n const look = color === 'accent' ? `${variant}-accent` : variant\n return (\n <ul\n // role=list keeps list semantics even under list-style:none (Safari drops them).\n role=\"list\"\n data-slot=\"list\"\n // group/list + data-variant drive every item's per-variant chrome (below).\n data-variant={variant}\n data-look={look}\n data-list-radius={radius}\n className={cn(\n 'group/list m-0 list-none p-0',\n // soft/solid = separate rows → stack with a small gap (the gap IS the divider).\n // ghost shares that same gap for spacing but no fill/border (see ListItem's\n // chrome block, which has no ghost selector). surface/outline glue rows into one\n // panel with no gap at all.\n 'data-[variant=soft]:flex data-[variant=soft]:flex-col data-[variant=soft]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n 'data-[variant=solid]:flex data-[variant=solid]:flex-col data-[variant=solid]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n 'data-[variant=ghost]:flex data-[variant=ghost]:flex-col data-[variant=ghost]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n className,\n )}\n style={\n {\n '--list-gap-steps': gap,\n // solid's fill inverts far enough that the row's own ink must invert with it\n // (unlike soft/surface/outline's translucent tints, where the default ink\n // stays legible regardless of colour) — matches Card's own solid recipe,\n // where fg and fg-muted both collapse to the one contrast-guaranteed step.\n ...(variant === 'solid' && {\n '--list-item-fg':\n color === 'accent'\n ? 'var(--spark-contrast)'\n : 'var(--neutral-12)',\n '--list-item-fg-muted':\n color === 'accent'\n ? 'var(--spark-contrast)'\n : 'var(--neutral-12)',\n }),\n ...style,\n } as React.CSSProperties\n }\n {...props}\n />\n )\n}\n\ninterface ListItemProps extends React.ComponentProps<'li'> {\n /** The \"recently changed\" accent dot. */\n recent?: boolean\n /** Persistent accent-tint (e.g. the active document). */\n selected?: boolean\n /** Loading placeholder (Figma `skeleton=true`). */\n skeleton?: boolean\n /**\n * Base UI polymorphism for the inner row. Pass `<a href/>` or `<button/>` to make\n * the row interactive (accent-on-hover/focus, cursor, focus ring). Omit → static row.\n */\n render?: useRender.RenderProp\n}\n\nfunction ListItem({\n className,\n children,\n recent,\n selected,\n skeleton,\n render,\n ...props\n}: ListItemProps) {\n // Skeleton rows are decorative placeholders — never render as a real <a>/<button>. An\n // aria-hidden anchor would still be focusable (WCAG 4.1.2), so skeleton forces a plain,\n // non-interactive <div> regardless of `render`.\n const interactive = !skeleton && render != null\n\n const recentCtx = React.useMemo(() => ({ recent: recent ?? false }), [recent])\n\n // Only a ListItemLeading actually marked `interactive` (a Checkbox/Radio/Button) needs\n // to move outside `render`'s element, and only when `render` is literally an <a>/\n // <button> — nesting a focusable control inside another interactive element is invalid\n // HTML and double-fires clicks. A `render` of anything else (a `<Card/>`, say) has no\n // such conflict: it isn't natively interactive itself, so a control inside it is valid,\n // unduplicated HTML, and hoisting would only pull the control outside whatever that\n // element paints (its fill, border, rounded corner) for no reason. A plain icon\n // (interactive unset) is the same story from the other side — either way, Leading stays\n // inside the row by default, so the whole visual row remains one click/tap target.\n const rendersAnchorOrButton =\n React.isValidElement(render) &&\n (render.type === 'a' || render.type === 'button')\n const childArray = skeleton ? undefined : React.Children.toArray(children)\n const leadingChild = childArray?.find(\n (child): child is React.ReactElement<ListItemLeadingProps> =>\n React.isValidElement(child) && child.type === ListItemLeading,\n )\n const hoistLeading =\n rendersAnchorOrButton && Boolean(leadingChild?.props.interactive)\n const leading = hoistLeading ? leadingChild : undefined\n\n const row = useRender({\n defaultTagName: 'div',\n render: skeleton ? undefined : render,\n props: {\n 'data-slot': 'list-item-row',\n // skeleton is a decorative placeholder → keep it out of the a11y tree.\n 'aria-hidden': skeleton ? true : undefined,\n className: cn(\n // row layout from Figma 2217-1667; min-h is a HARD 44px floor (WCAG 2.5.5),\n // py/gap/text still scale past it via the spacing/scaling knobs. no-underline\n // resets the default <a> decoration when the row renders as a link.\n 'flex min-h-[2.75rem] items-center gap-4 py-3 text-left no-underline',\n hoistLeading ? 'min-w-0 flex-1 pr-4' : 'w-full rounded-[inherit] px-4',\n // not hoisted → this row IS the whole visual item (no outer <li> chrome), so\n // selection tint lives here, same as a row with no leading slot at all.\n !hoistLeading && !skeleton && selected && 'bg-spark-a3',\n interactive && [\n 'group/row cursor-pointer transition-colors duration-150 motion-reduce:transition-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n hoistLeading\n ? // the custom focus ring is drawn on the outer <li> (has-focus-visible,\n // below) so it wraps the leading control too — but the browser's own\n // default outline still targets whichever element actually has focus,\n // which is this one, so it has to be suppressed here specifically.\n 'focus-visible:outline-hidden'\n : [\n 'hover:bg-spark-a3 focus-visible:bg-spark-a3',\n 'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden',\n ],\n ],\n ),\n children: skeleton ? (\n <ListItemSkeleton />\n ) : hoistLeading ? (\n childArray?.filter((child) => child !== leadingChild)\n ) : (\n children\n ),\n },\n })\n\n return (\n <li\n data-slot=\"list-item\"\n className={cn(\n 'relative has-focus-visible:z-10',\n hoistLeading && [\n 'flex min-w-0 items-center gap-4 pl-4',\n !skeleton && selected && 'bg-spark-a3',\n interactive && [\n // neutral at rest → subtle accent tint on hover/focus (the affordance goes\n // accent too, in ListItemTrailing; title + leading stay neutral). The click\n // target itself is `row`, so focus-visible (unlike hover) has to be read off\n // a descendant via has() — it never lands on this <li> directly.\n 'cursor-pointer',\n 'hover:bg-spark-a3 has-focus-visible:bg-spark-a3',\n 'has-focus-visible:ring-2 has-focus-visible:ring-offset-2',\n ],\n ],\n // soft/solid rows sit in a gapped stack, not a glued panel, but still read as one\n // rounded group: only the first/last row round the outer corners. surface/\n // outline glue rows into one bordered panel instead — border SIDES are variant-\n // only (no colour axis), so they stay on data-variant; fill and border COLOUR\n // depend on variant *and* colour together, so those live on data-look instead\n // (see the comment on `look` in List, above).\n 'group-data-[variant=surface]/list:border-x group-data-[variant=surface]/list:border-t group-data-[variant=surface]/list:last:border-b',\n 'group-data-[variant=outline]/list:border-x group-data-[variant=outline]/list:border-t group-data-[variant=outline]/list:last:border-b',\n 'group-data-[look=soft]/list:bg-gray-3 dark:group-data-[look=soft]/list:bg-gray-a3',\n // arbitrary-var form, not the bg-spark-a3 utility: identical colour, but avoids a\n // literal substring collision with the unconditional selection/hover tint below,\n // which a couple of tests distinguish with a plain className.includes check.\n 'group-data-[look=soft-accent]/list:bg-(--spark-a3)',\n 'group-data-[look=surface]/list:border-gray-a6 group-data-[look=surface]/list:bg-gray-3 dark:group-data-[look=surface]/list:bg-gray-a3',\n 'group-data-[look=surface-accent]/list:border-spark-a6 group-data-[look=surface-accent]/list:bg-(--spark-surface)',\n 'group-data-[look=outline]/list:border-gray-a6',\n 'group-data-[look=outline-accent]/list:border-spark-a6',\n 'group-data-[look=solid]/list:bg-gray-1',\n 'group-data-[look=solid-accent]/list:bg-spark-9',\n Object.values(radiusRowClasses),\n className,\n )}\n {...props}\n >\n <ListItemContext.Provider value={recentCtx}>\n {leading}\n {row}\n </ListItemContext.Provider>\n </li>\n )\n}\n\nfunction ListItemSkeleton() {\n // Compose the shared Skeleton primitive so the placeholder matches every other skeleton\n // (token + radius + animation + motion-reduce).\n return (\n <>\n <Skeleton className=\"size-10 shrink-0 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Skeleton className=\"h-3.5 w-1/3\" />\n <Skeleton className=\"h-3.5 w-2/3\" />\n </div>\n <div className=\"flex min-w-0 flex-1 flex-col items-end gap-2\">\n <Skeleton className=\"h-3.5 w-1/4\" />\n <Skeleton className=\"h-3.5 w-1/2\" />\n </div>\n </>\n )\n}\n\ninterface ListItemLeadingProps extends React.ComponentProps<'div'> {\n /**\n * Set when this slot holds a focusable control (Checkbox, Radio, Button). ListItem\n * then hoists it outside `render`'s element — nesting a real control inside the row's\n * own <a>/<button> is invalid HTML and double-fires clicks. Leave unset for decorative\n * content (an icon): it stays inside the row, so the whole visual row is one\n * click/tap target, same as a row with no leading slot at all.\n */\n interactive?: boolean\n}\n\nfunction ListItemLeading({\n className,\n interactive: _interactive,\n ...props\n}: ListItemLeadingProps) {\n return (\n <div\n data-slot=\"list-item-leading\"\n className={cn('flex shrink-0 items-center', className)}\n {...props}\n />\n )\n}\n\ninterface ListItemContentProps extends React.ComponentProps<'div'> {\n /** `end` right-aligns the column (the value column). */\n align?: 'start' | 'end'\n}\n\nfunction ListItemContent({\n className,\n align = 'start',\n ...props\n}: ListItemContentProps) {\n const columnCtx = React.useMemo(() => ({ isEnd: align === 'end' }), [align])\n return (\n <ListItemColumnContext.Provider value={columnCtx}>\n <div\n data-slot=\"list-item-content\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col gap-1',\n align === 'end' ? 'items-end text-right' : 'items-start text-left',\n className,\n )}\n {...props}\n />\n </ListItemColumnContext.Provider>\n )\n}\n\nfunction ListItemPreline({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"list-item-preline\"\n className={cn(\n 'block w-full truncate text-sm text-(--list-item-fg-muted,var(--neutral-11))',\n className,\n )}\n {...props}\n />\n )\n}\n\ninterface ListItemTitleProps extends React.ComponentProps<'span'> {\n /** `strong` keeps the title bold, e.g. for menu rows; `default` is medium, bold on a `recent` row. */\n emphasis?: 'default' | 'strong'\n}\n\nfunction ListItemTitle({\n className,\n children,\n emphasis = 'default',\n ...props\n}: ListItemTitleProps) {\n const { recent } = React.useContext(ListItemContext)\n const { isEnd } = React.useContext(ListItemColumnContext)\n // recency dot sits inline, immediately left of the title (Figma 1310-977) — only on the\n // primary (start) column, so a two-column value row's value never carries it.\n const showDot = recent && !isEnd\n\n // className/{...props} always land on THIS span (the one that truncates), so a caller's\n // override (e.g. `title`, `line-clamp-2 whitespace-normal`) behaves identically whether\n // or not the dot renders — only the wrapping structure differs below.\n const title = (\n <span\n data-slot=\"list-item-title\"\n className={cn(\n showDot ? 'min-w-0 truncate' : 'block w-full truncate',\n 'text-base text-(--list-item-fg,var(--neutral-12))',\n recent || emphasis === 'strong' ? 'font-bold' : 'font-medium',\n className,\n )}\n {...props}\n >\n {children}\n </span>\n )\n\n if (!showDot) {\n // No dot: stay a plain block so the column's text alignment (e.g. an end column's\n // text-right) governs the title directly.\n return title\n }\n\n return (\n <span className=\"flex w-full items-center gap-2\">\n <span\n aria-hidden=\"true\"\n data-slot=\"list-item-indicator\"\n className=\"size-2 shrink-0 rounded-full bg-spark-9\"\n />\n {title}\n </span>\n )\n}\n\nfunction ListItemSubline({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"list-item-subline\"\n className={cn(\n 'block w-full truncate text-sm text-(--list-item-fg-muted,var(--neutral-11))',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction ListItemTrailing({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"list-item-trailing\"\n className={cn(\n 'flex shrink-0 items-center text-gray-11',\n // interactive rows tint the affordance accent on hover/focus (group/row is set on\n // the interactive inner row; absent on static rows, so this simply never fires).\n // Relies on the child reading currentColor (Spark icons do) — the colour-\n // independent Icon component hardcodes its own ink and won't pick this up; use\n // a Spark icon here, not Icon.\n 'transition-colors duration-150 motion-reduce:transition-none',\n 'group-hover/row:text-spark-a11 group-focus-visible/row:text-spark-a11',\n className,\n )}\n {...props}\n />\n )\n}\n\nexport {\n List,\n ListItem,\n ListItemContent,\n ListItemLeading,\n ListItemPreline,\n ListItemSubline,\n ListItemTitle,\n ListItemTrailing,\n}\nexport type {\n ListProps,\n ListItemProps,\n ListItemContentProps,\n ListItemLeadingProps,\n ListItemTitleProps,\n ListVariant,\n ListRadius,\n}\n"],"mappings":"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"}
@@ -68,7 +68,7 @@ function ListItem({ className, children, recent, selected, skeleton, render, ...
68
68
  "hover:bg-spark-a3 has-focus-visible:bg-spark-a3",
69
69
  "has-focus-visible:ring-2 has-focus-visible:ring-offset-2"
70
70
  ]
71
- ], "group-data-[variant=surface]/list:border-x group-data-[variant=surface]/list:border-t group-data-[variant=surface]/list:last:border-b", "group-data-[variant=outline]/list:border-x group-data-[variant=outline]/list:border-t group-data-[variant=outline]/list:last:border-b", "group-data-[look=soft]/list:bg-gray-a2 dark:group-data-[look=soft]/list:bg-gray-a3", "group-data-[look=soft-accent]/list:bg-(--spark-a3)", "group-data-[look=surface]/list:border-gray-a6 group-data-[look=surface]/list:bg-gray-2", "group-data-[look=surface-accent]/list:border-spark-a6 group-data-[look=surface-accent]/list:bg-(--spark-surface)", "group-data-[look=outline]/list:border-gray-a6", "group-data-[look=outline-accent]/list:border-spark-a6", "group-data-[look=solid]/list:bg-(--neutral-1)", "group-data-[look=solid-accent]/list:bg-spark-9", Object.values(radiusRowClasses), className),
71
+ ], "group-data-[variant=surface]/list:border-x group-data-[variant=surface]/list:border-t group-data-[variant=surface]/list:last:border-b", "group-data-[variant=outline]/list:border-x group-data-[variant=outline]/list:border-t group-data-[variant=outline]/list:last:border-b", "group-data-[look=soft]/list:bg-gray-3 dark:group-data-[look=soft]/list:bg-gray-a3", "group-data-[look=soft-accent]/list:bg-(--spark-a3)", "group-data-[look=surface]/list:border-gray-a6 group-data-[look=surface]/list:bg-gray-3 dark:group-data-[look=surface]/list:bg-gray-a3", "group-data-[look=surface-accent]/list:border-spark-a6 group-data-[look=surface-accent]/list:bg-(--spark-surface)", "group-data-[look=outline]/list:border-gray-a6", "group-data-[look=outline-accent]/list:border-spark-a6", "group-data-[look=solid]/list:bg-gray-1", "group-data-[look=solid-accent]/list:bg-spark-9", Object.values(radiusRowClasses), className),
72
72
  ...props,
73
73
  children: /* @__PURE__ */ jsxs(ListItemContext.Provider, {
74
74
  value: recentCtx,
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","names":["React"],"sources":["../../src/components/list.tsx"],"sourcesContent":["import { useRender } from '@base-ui/react/use-render'\nimport * as React from 'react'\nimport type { ContainerRadius } from '../lib/container-chrome'\nimport type { SparkCardColor, SparkCardVariant } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { Skeleton } from './skeleton'\n\ntype ListVariant = SparkCardVariant\ntype ListRadius = ContainerRadius\n\n// Rounds only the first/last row of a run, regardless of variant — harmless on ghost\n// (nothing painted to clip). Keyed on `data-list-radius` alone: unlike fill (below),\n// rounding doesn't depend on variant/color, so no compound group-data selector is needed.\n// Named `list-radius`, not `radius`: the global Roundness knob (Theme) also writes a\n// bare `data-radius` via an unscoped `[data-radius=...]` selector in spark-default.css —\n// `none`/`full` are spelled the same in both scales, so a plain `data-radius` here would\n// collide and let List silently override the app-wide knob for its whole subtree.\nconst radiusRowClasses: Record<ListRadius, string> = {\n none: 'group-data-[list-radius=none]/list:first:rounded-t-none group-data-[list-radius=none]/list:last:rounded-b-none',\n sm: 'group-data-[list-radius=sm]/list:first:rounded-t-sm group-data-[list-radius=sm]/list:last:rounded-b-sm',\n md: 'group-data-[list-radius=md]/list:first:rounded-t-md group-data-[list-radius=md]/list:last:rounded-b-md',\n lg: 'group-data-[list-radius=lg]/list:first:rounded-t-lg group-data-[list-radius=lg]/list:last:rounded-b-lg',\n xl: 'group-data-[list-radius=xl]/list:first:rounded-t-xl group-data-[list-radius=xl]/list:last:rounded-b-xl',\n '2xl':\n 'group-data-[list-radius=2xl]/list:first:rounded-t-2xl group-data-[list-radius=2xl]/list:last:rounded-b-2xl',\n '3xl':\n 'group-data-[list-radius=3xl]/list:first:rounded-t-3xl group-data-[list-radius=3xl]/list:last:rounded-b-3xl',\n full: 'group-data-[list-radius=full]/list:first:rounded-t-full group-data-[list-radius=full]/list:last:rounded-b-full',\n}\n\n// ListItem publishes `recent` so ListItemTitle can render the inline recency dot; the\n// column context lets the title show it only on the primary (start) column, never the\n// value column of a two-column row.\nconst ListItemContext = React.createContext<{ recent: boolean }>({\n recent: false,\n})\nconst ListItemColumnContext = React.createContext<{ isEnd: boolean }>({\n isEnd: false,\n})\n\ninterface ListProps extends React.ComponentProps<'ul'> {\n /** A prop, not the ambient `data-style` knob — that would cascade into row content. */\n variant?: ListVariant\n /** Chrome palette. `neutral` (default) tracks the app neutral; `accent` tints with the brand accent. */\n color?: SparkCardColor\n /** Corner radius, as a named step. Unset → `lg`, today's fixed behaviour. */\n radius?: ListRadius\n /** How many `--spark-layout-gap` units apart each row sits (`soft`/`solid`/`ghost` only). */\n gap?: number\n}\n\nfunction List({\n className,\n variant = 'soft',\n color = 'neutral',\n radius = 'lg',\n gap = 0.5,\n style,\n ...props\n}: ListProps) {\n // Fill/border-colour needs BOTH axes at once (e.g. \"soft, tinted with accent\"), which a\n // single group-data selector can't express and chaining two on the same named group\n // can't either (that generates a descendant match between two *different* ancestors,\n // not one ancestor bearing both attributes). Resolving the pair to one value up front\n // sidesteps it entirely — see the `data-look` selectors on ListItem below.\n const look = color === 'accent' ? `${variant}-accent` : variant\n return (\n <ul\n // role=list keeps list semantics even under list-style:none (Safari drops them).\n role=\"list\"\n data-slot=\"list\"\n // group/list + data-variant drive every item's per-variant chrome (below).\n data-variant={variant}\n data-look={look}\n data-list-radius={radius}\n className={cn(\n 'group/list m-0 list-none p-0',\n // soft/solid = separate rows → stack with a small gap (the gap IS the divider).\n // ghost shares that same gap for spacing but no fill/border (see ListItem's\n // chrome block, which has no ghost selector). surface/outline glue rows into one\n // panel with no gap at all.\n 'data-[variant=soft]:flex data-[variant=soft]:flex-col data-[variant=soft]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n 'data-[variant=solid]:flex data-[variant=solid]:flex-col data-[variant=solid]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n 'data-[variant=ghost]:flex data-[variant=ghost]:flex-col data-[variant=ghost]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n className,\n )}\n style={\n {\n '--list-gap-steps': gap,\n // solid's fill inverts far enough that the row's own ink must invert with it\n // (unlike soft/surface/outline's translucent tints, where the default ink\n // stays legible regardless of colour) — matches Card's own solid recipe,\n // where fg and fg-muted both collapse to the one contrast-guaranteed step.\n ...(variant === 'solid' && {\n '--list-item-fg':\n color === 'accent'\n ? 'var(--spark-contrast)'\n : 'var(--neutral-12)',\n '--list-item-fg-muted':\n color === 'accent'\n ? 'var(--spark-contrast)'\n : 'var(--neutral-12)',\n }),\n ...style,\n } as React.CSSProperties\n }\n {...props}\n />\n )\n}\n\ninterface ListItemProps extends React.ComponentProps<'li'> {\n /** The \"recently changed\" accent dot. */\n recent?: boolean\n /** Persistent accent-tint (e.g. the active document). */\n selected?: boolean\n /** Loading placeholder (Figma `skeleton=true`). */\n skeleton?: boolean\n /**\n * Base UI polymorphism for the inner row. Pass `<a href/>` or `<button/>` to make\n * the row interactive (accent-on-hover/focus, cursor, focus ring). Omit → static row.\n */\n render?: useRender.RenderProp\n}\n\nfunction ListItem({\n className,\n children,\n recent,\n selected,\n skeleton,\n render,\n ...props\n}: ListItemProps) {\n // Skeleton rows are decorative placeholders — never render as a real <a>/<button>. An\n // aria-hidden anchor would still be focusable (WCAG 4.1.2), so skeleton forces a plain,\n // non-interactive <div> regardless of `render`.\n const interactive = !skeleton && render != null\n\n const recentCtx = React.useMemo(() => ({ recent: recent ?? false }), [recent])\n\n // Only a ListItemLeading actually marked `interactive` (a Checkbox/Radio/Button) needs\n // to move outside `render`'s element, and only when `render` is literally an <a>/\n // <button> — nesting a focusable control inside another interactive element is invalid\n // HTML and double-fires clicks. A `render` of anything else (a `<Card/>`, say) has no\n // such conflict: it isn't natively interactive itself, so a control inside it is valid,\n // unduplicated HTML, and hoisting would only pull the control outside whatever that\n // element paints (its fill, border, rounded corner) for no reason. A plain icon\n // (interactive unset) is the same story from the other side — either way, Leading stays\n // inside the row by default, so the whole visual row remains one click/tap target.\n const rendersAnchorOrButton =\n React.isValidElement(render) &&\n (render.type === 'a' || render.type === 'button')\n const childArray = skeleton ? undefined : React.Children.toArray(children)\n const leadingChild = childArray?.find(\n (child): child is React.ReactElement<ListItemLeadingProps> =>\n React.isValidElement(child) && child.type === ListItemLeading,\n )\n const hoistLeading =\n rendersAnchorOrButton && Boolean(leadingChild?.props.interactive)\n const leading = hoistLeading ? leadingChild : undefined\n\n const row = useRender({\n defaultTagName: 'div',\n render: skeleton ? undefined : render,\n props: {\n 'data-slot': 'list-item-row',\n // skeleton is a decorative placeholder → keep it out of the a11y tree.\n 'aria-hidden': skeleton ? true : undefined,\n className: cn(\n // row layout from Figma 2217-1667; min-h is a HARD 44px floor (WCAG 2.5.5),\n // py/gap/text still scale past it via the spacing/scaling knobs. no-underline\n // resets the default <a> decoration when the row renders as a link.\n 'flex min-h-[2.75rem] items-center gap-4 py-3 text-left no-underline',\n hoistLeading ? 'min-w-0 flex-1 pr-4' : 'w-full rounded-[inherit] px-4',\n // not hoisted → this row IS the whole visual item (no outer <li> chrome), so\n // selection tint lives here, same as a row with no leading slot at all.\n !hoistLeading && !skeleton && selected && 'bg-spark-a3',\n interactive && [\n 'group/row cursor-pointer transition-colors duration-150 motion-reduce:transition-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n hoistLeading\n ? // the custom focus ring is drawn on the outer <li> (has-focus-visible,\n // below) so it wraps the leading control too — but the browser's own\n // default outline still targets whichever element actually has focus,\n // which is this one, so it has to be suppressed here specifically.\n 'focus-visible:outline-hidden'\n : [\n 'hover:bg-spark-a3 focus-visible:bg-spark-a3',\n 'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden',\n ],\n ],\n ),\n children: skeleton ? (\n <ListItemSkeleton />\n ) : hoistLeading ? (\n childArray?.filter((child) => child !== leadingChild)\n ) : (\n children\n ),\n },\n })\n\n return (\n <li\n data-slot=\"list-item\"\n className={cn(\n 'relative has-focus-visible:z-10',\n hoistLeading && [\n 'flex min-w-0 items-center gap-4 pl-4',\n !skeleton && selected && 'bg-spark-a3',\n interactive && [\n // neutral at rest → subtle accent tint on hover/focus (the affordance goes\n // accent too, in ListItemTrailing; title + leading stay neutral). The click\n // target itself is `row`, so focus-visible (unlike hover) has to be read off\n // a descendant via has() — it never lands on this <li> directly.\n 'cursor-pointer',\n 'hover:bg-spark-a3 has-focus-visible:bg-spark-a3',\n 'has-focus-visible:ring-2 has-focus-visible:ring-offset-2',\n ],\n ],\n // soft/solid rows sit in a gapped stack, not a glued panel, but still read as one\n // rounded group: only the first/last row round the outer corners. surface/\n // outline glue rows into one bordered panel instead — border SIDES are variant-\n // only (no colour axis), so they stay on data-variant; fill and border COLOUR\n // depend on variant *and* colour together, so those live on data-look instead\n // (see the comment on `look` in List, above).\n 'group-data-[variant=surface]/list:border-x group-data-[variant=surface]/list:border-t group-data-[variant=surface]/list:last:border-b',\n 'group-data-[variant=outline]/list:border-x group-data-[variant=outline]/list:border-t group-data-[variant=outline]/list:last:border-b',\n 'group-data-[look=soft]/list:bg-gray-a2 dark:group-data-[look=soft]/list:bg-gray-a3',\n // arbitrary-var form, not the bg-spark-a3 utility: identical colour, but avoids a\n // literal substring collision with the unconditional selection/hover tint below,\n // which a couple of tests distinguish with a plain className.includes check.\n 'group-data-[look=soft-accent]/list:bg-(--spark-a3)',\n 'group-data-[look=surface]/list:border-gray-a6 group-data-[look=surface]/list:bg-gray-2',\n 'group-data-[look=surface-accent]/list:border-spark-a6 group-data-[look=surface-accent]/list:bg-(--spark-surface)',\n 'group-data-[look=outline]/list:border-gray-a6',\n 'group-data-[look=outline-accent]/list:border-spark-a6',\n 'group-data-[look=solid]/list:bg-(--neutral-1)',\n 'group-data-[look=solid-accent]/list:bg-spark-9',\n Object.values(radiusRowClasses),\n className,\n )}\n {...props}\n >\n <ListItemContext.Provider value={recentCtx}>\n {leading}\n {row}\n </ListItemContext.Provider>\n </li>\n )\n}\n\nfunction ListItemSkeleton() {\n // Compose the shared Skeleton primitive so the placeholder matches every other skeleton\n // (token + radius + animation + motion-reduce).\n return (\n <>\n <Skeleton className=\"size-10 shrink-0 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Skeleton className=\"h-3.5 w-1/3\" />\n <Skeleton className=\"h-3.5 w-2/3\" />\n </div>\n <div className=\"flex min-w-0 flex-1 flex-col items-end gap-2\">\n <Skeleton className=\"h-3.5 w-1/4\" />\n <Skeleton className=\"h-3.5 w-1/2\" />\n </div>\n </>\n )\n}\n\ninterface ListItemLeadingProps extends React.ComponentProps<'div'> {\n /**\n * Set when this slot holds a focusable control (Checkbox, Radio, Button). ListItem\n * then hoists it outside `render`'s element — nesting a real control inside the row's\n * own <a>/<button> is invalid HTML and double-fires clicks. Leave unset for decorative\n * content (an icon): it stays inside the row, so the whole visual row is one\n * click/tap target, same as a row with no leading slot at all.\n */\n interactive?: boolean\n}\n\nfunction ListItemLeading({\n className,\n interactive: _interactive,\n ...props\n}: ListItemLeadingProps) {\n return (\n <div\n data-slot=\"list-item-leading\"\n className={cn('flex shrink-0 items-center', className)}\n {...props}\n />\n )\n}\n\ninterface ListItemContentProps extends React.ComponentProps<'div'> {\n /** `end` right-aligns the column (the value column). */\n align?: 'start' | 'end'\n}\n\nfunction ListItemContent({\n className,\n align = 'start',\n ...props\n}: ListItemContentProps) {\n const columnCtx = React.useMemo(() => ({ isEnd: align === 'end' }), [align])\n return (\n <ListItemColumnContext.Provider value={columnCtx}>\n <div\n data-slot=\"list-item-content\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col gap-1',\n align === 'end' ? 'items-end text-right' : 'items-start text-left',\n className,\n )}\n {...props}\n />\n </ListItemColumnContext.Provider>\n )\n}\n\nfunction ListItemPreline({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"list-item-preline\"\n className={cn(\n 'block w-full truncate text-sm text-(--list-item-fg-muted,var(--neutral-11))',\n className,\n )}\n {...props}\n />\n )\n}\n\ninterface ListItemTitleProps extends React.ComponentProps<'span'> {\n /** `strong` keeps the title bold, e.g. for menu rows; `default` is medium, bold on a `recent` row. */\n emphasis?: 'default' | 'strong'\n}\n\nfunction ListItemTitle({\n className,\n children,\n emphasis = 'default',\n ...props\n}: ListItemTitleProps) {\n const { recent } = React.useContext(ListItemContext)\n const { isEnd } = React.useContext(ListItemColumnContext)\n // recency dot sits inline, immediately left of the title (Figma 1310-977) — only on the\n // primary (start) column, so a two-column value row's value never carries it.\n const showDot = recent && !isEnd\n\n // className/{...props} always land on THIS span (the one that truncates), so a caller's\n // override (e.g. `title`, `line-clamp-2 whitespace-normal`) behaves identically whether\n // or not the dot renders — only the wrapping structure differs below.\n const title = (\n <span\n data-slot=\"list-item-title\"\n className={cn(\n showDot ? 'min-w-0 truncate' : 'block w-full truncate',\n 'text-base text-(--list-item-fg,var(--neutral-12))',\n recent || emphasis === 'strong' ? 'font-bold' : 'font-medium',\n className,\n )}\n {...props}\n >\n {children}\n </span>\n )\n\n if (!showDot) {\n // No dot: stay a plain block so the column's text alignment (e.g. an end column's\n // text-right) governs the title directly.\n return title\n }\n\n return (\n <span className=\"flex w-full items-center gap-2\">\n <span\n aria-hidden=\"true\"\n data-slot=\"list-item-indicator\"\n className=\"size-2 shrink-0 rounded-full bg-spark-9\"\n />\n {title}\n </span>\n )\n}\n\nfunction ListItemSubline({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"list-item-subline\"\n className={cn(\n 'block w-full truncate text-sm text-(--list-item-fg-muted,var(--neutral-11))',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction ListItemTrailing({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"list-item-trailing\"\n className={cn(\n 'flex shrink-0 items-center text-gray-11',\n // interactive rows tint the affordance accent on hover/focus (group/row is set on\n // the interactive inner row; absent on static rows, so this simply never fires).\n // Relies on the child reading currentColor (Spark icons do) — the colour-\n // independent Icon component hardcodes its own ink and won't pick this up; use\n // a Spark icon here, not Icon.\n 'transition-colors duration-150 motion-reduce:transition-none',\n 'group-hover/row:text-spark-a11 group-focus-visible/row:text-spark-a11',\n className,\n )}\n {...props}\n />\n )\n}\n\nexport {\n List,\n ListItem,\n ListItemContent,\n ListItemLeading,\n ListItemPreline,\n ListItemSubline,\n ListItemTitle,\n ListItemTrailing,\n}\nexport type {\n ListProps,\n ListItemProps,\n ListItemContentProps,\n ListItemLeadingProps,\n ListItemTitleProps,\n ListVariant,\n ListRadius,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"list.js","names":["React"],"sources":["../../src/components/list.tsx"],"sourcesContent":["import { useRender } from '@base-ui/react/use-render'\nimport * as React from 'react'\nimport type { ContainerRadius } from '../lib/container-chrome'\nimport type { SparkCardColor, SparkCardVariant } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { Skeleton } from './skeleton'\n\ntype ListVariant = SparkCardVariant\ntype ListRadius = ContainerRadius\n\n// Rounds only the first/last row of a run, regardless of variant — harmless on ghost\n// (nothing painted to clip). Keyed on `data-list-radius` alone: unlike fill (below),\n// rounding doesn't depend on variant/color, so no compound group-data selector is needed.\n// Named `list-radius`, not `radius`: the global Roundness knob (Theme) also writes a\n// bare `data-radius` via an unscoped `[data-radius=...]` selector in spark-default.css —\n// `none`/`full` are spelled the same in both scales, so a plain `data-radius` here would\n// collide and let List silently override the app-wide knob for its whole subtree.\nconst radiusRowClasses: Record<ListRadius, string> = {\n none: 'group-data-[list-radius=none]/list:first:rounded-t-none group-data-[list-radius=none]/list:last:rounded-b-none',\n sm: 'group-data-[list-radius=sm]/list:first:rounded-t-sm group-data-[list-radius=sm]/list:last:rounded-b-sm',\n md: 'group-data-[list-radius=md]/list:first:rounded-t-md group-data-[list-radius=md]/list:last:rounded-b-md',\n lg: 'group-data-[list-radius=lg]/list:first:rounded-t-lg group-data-[list-radius=lg]/list:last:rounded-b-lg',\n xl: 'group-data-[list-radius=xl]/list:first:rounded-t-xl group-data-[list-radius=xl]/list:last:rounded-b-xl',\n '2xl':\n 'group-data-[list-radius=2xl]/list:first:rounded-t-2xl group-data-[list-radius=2xl]/list:last:rounded-b-2xl',\n '3xl':\n 'group-data-[list-radius=3xl]/list:first:rounded-t-3xl group-data-[list-radius=3xl]/list:last:rounded-b-3xl',\n full: 'group-data-[list-radius=full]/list:first:rounded-t-full group-data-[list-radius=full]/list:last:rounded-b-full',\n}\n\n// ListItem publishes `recent` so ListItemTitle can render the inline recency dot; the\n// column context lets the title show it only on the primary (start) column, never the\n// value column of a two-column row.\nconst ListItemContext = React.createContext<{ recent: boolean }>({\n recent: false,\n})\nconst ListItemColumnContext = React.createContext<{ isEnd: boolean }>({\n isEnd: false,\n})\n\ninterface ListProps extends React.ComponentProps<'ul'> {\n /** A prop, not the ambient `data-style` knob — that would cascade into row content. */\n variant?: ListVariant\n /** Chrome palette. `neutral` (default) tracks the app neutral; `accent` tints with the brand accent. */\n color?: SparkCardColor\n /** Corner radius, as a named step. Unset → `lg`, today's fixed behaviour. */\n radius?: ListRadius\n /** How many `--spark-layout-gap` units apart each row sits (`soft`/`solid`/`ghost` only). */\n gap?: number\n}\n\nfunction List({\n className,\n variant = 'soft',\n color = 'neutral',\n radius = 'lg',\n gap = 0.5,\n style,\n ...props\n}: ListProps) {\n // Fill/border-colour needs BOTH axes at once (e.g. \"soft, tinted with accent\"), which a\n // single group-data selector can't express and chaining two on the same named group\n // can't either (that generates a descendant match between two *different* ancestors,\n // not one ancestor bearing both attributes). Resolving the pair to one value up front\n // sidesteps it entirely — see the `data-look` selectors on ListItem below.\n const look = color === 'accent' ? `${variant}-accent` : variant\n return (\n <ul\n // role=list keeps list semantics even under list-style:none (Safari drops them).\n role=\"list\"\n data-slot=\"list\"\n // group/list + data-variant drive every item's per-variant chrome (below).\n data-variant={variant}\n data-look={look}\n data-list-radius={radius}\n className={cn(\n 'group/list m-0 list-none p-0',\n // soft/solid = separate rows → stack with a small gap (the gap IS the divider).\n // ghost shares that same gap for spacing but no fill/border (see ListItem's\n // chrome block, which has no ghost selector). surface/outline glue rows into one\n // panel with no gap at all.\n 'data-[variant=soft]:flex data-[variant=soft]:flex-col data-[variant=soft]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n 'data-[variant=solid]:flex data-[variant=solid]:flex-col data-[variant=solid]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n 'data-[variant=ghost]:flex data-[variant=ghost]:flex-col data-[variant=ghost]:gap-[calc(var(--spark-layout-gap)*var(--list-gap-steps,0.5))]',\n className,\n )}\n style={\n {\n '--list-gap-steps': gap,\n // solid's fill inverts far enough that the row's own ink must invert with it\n // (unlike soft/surface/outline's translucent tints, where the default ink\n // stays legible regardless of colour) — matches Card's own solid recipe,\n // where fg and fg-muted both collapse to the one contrast-guaranteed step.\n ...(variant === 'solid' && {\n '--list-item-fg':\n color === 'accent'\n ? 'var(--spark-contrast)'\n : 'var(--neutral-12)',\n '--list-item-fg-muted':\n color === 'accent'\n ? 'var(--spark-contrast)'\n : 'var(--neutral-12)',\n }),\n ...style,\n } as React.CSSProperties\n }\n {...props}\n />\n )\n}\n\ninterface ListItemProps extends React.ComponentProps<'li'> {\n /** The \"recently changed\" accent dot. */\n recent?: boolean\n /** Persistent accent-tint (e.g. the active document). */\n selected?: boolean\n /** Loading placeholder (Figma `skeleton=true`). */\n skeleton?: boolean\n /**\n * Base UI polymorphism for the inner row. Pass `<a href/>` or `<button/>` to make\n * the row interactive (accent-on-hover/focus, cursor, focus ring). Omit → static row.\n */\n render?: useRender.RenderProp\n}\n\nfunction ListItem({\n className,\n children,\n recent,\n selected,\n skeleton,\n render,\n ...props\n}: ListItemProps) {\n // Skeleton rows are decorative placeholders — never render as a real <a>/<button>. An\n // aria-hidden anchor would still be focusable (WCAG 4.1.2), so skeleton forces a plain,\n // non-interactive <div> regardless of `render`.\n const interactive = !skeleton && render != null\n\n const recentCtx = React.useMemo(() => ({ recent: recent ?? false }), [recent])\n\n // Only a ListItemLeading actually marked `interactive` (a Checkbox/Radio/Button) needs\n // to move outside `render`'s element, and only when `render` is literally an <a>/\n // <button> — nesting a focusable control inside another interactive element is invalid\n // HTML and double-fires clicks. A `render` of anything else (a `<Card/>`, say) has no\n // such conflict: it isn't natively interactive itself, so a control inside it is valid,\n // unduplicated HTML, and hoisting would only pull the control outside whatever that\n // element paints (its fill, border, rounded corner) for no reason. A plain icon\n // (interactive unset) is the same story from the other side — either way, Leading stays\n // inside the row by default, so the whole visual row remains one click/tap target.\n const rendersAnchorOrButton =\n React.isValidElement(render) &&\n (render.type === 'a' || render.type === 'button')\n const childArray = skeleton ? undefined : React.Children.toArray(children)\n const leadingChild = childArray?.find(\n (child): child is React.ReactElement<ListItemLeadingProps> =>\n React.isValidElement(child) && child.type === ListItemLeading,\n )\n const hoistLeading =\n rendersAnchorOrButton && Boolean(leadingChild?.props.interactive)\n const leading = hoistLeading ? leadingChild : undefined\n\n const row = useRender({\n defaultTagName: 'div',\n render: skeleton ? undefined : render,\n props: {\n 'data-slot': 'list-item-row',\n // skeleton is a decorative placeholder → keep it out of the a11y tree.\n 'aria-hidden': skeleton ? true : undefined,\n className: cn(\n // row layout from Figma 2217-1667; min-h is a HARD 44px floor (WCAG 2.5.5),\n // py/gap/text still scale past it via the spacing/scaling knobs. no-underline\n // resets the default <a> decoration when the row renders as a link.\n 'flex min-h-[2.75rem] items-center gap-4 py-3 text-left no-underline',\n hoistLeading ? 'min-w-0 flex-1 pr-4' : 'w-full rounded-[inherit] px-4',\n // not hoisted → this row IS the whole visual item (no outer <li> chrome), so\n // selection tint lives here, same as a row with no leading slot at all.\n !hoistLeading && !skeleton && selected && 'bg-spark-a3',\n interactive && [\n 'group/row cursor-pointer transition-colors duration-150 motion-reduce:transition-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n hoistLeading\n ? // the custom focus ring is drawn on the outer <li> (has-focus-visible,\n // below) so it wraps the leading control too — but the browser's own\n // default outline still targets whichever element actually has focus,\n // which is this one, so it has to be suppressed here specifically.\n 'focus-visible:outline-hidden'\n : [\n 'hover:bg-spark-a3 focus-visible:bg-spark-a3',\n 'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden',\n ],\n ],\n ),\n children: skeleton ? (\n <ListItemSkeleton />\n ) : hoistLeading ? (\n childArray?.filter((child) => child !== leadingChild)\n ) : (\n children\n ),\n },\n })\n\n return (\n <li\n data-slot=\"list-item\"\n className={cn(\n 'relative has-focus-visible:z-10',\n hoistLeading && [\n 'flex min-w-0 items-center gap-4 pl-4',\n !skeleton && selected && 'bg-spark-a3',\n interactive && [\n // neutral at rest → subtle accent tint on hover/focus (the affordance goes\n // accent too, in ListItemTrailing; title + leading stay neutral). The click\n // target itself is `row`, so focus-visible (unlike hover) has to be read off\n // a descendant via has() — it never lands on this <li> directly.\n 'cursor-pointer',\n 'hover:bg-spark-a3 has-focus-visible:bg-spark-a3',\n 'has-focus-visible:ring-2 has-focus-visible:ring-offset-2',\n ],\n ],\n // soft/solid rows sit in a gapped stack, not a glued panel, but still read as one\n // rounded group: only the first/last row round the outer corners. surface/\n // outline glue rows into one bordered panel instead — border SIDES are variant-\n // only (no colour axis), so they stay on data-variant; fill and border COLOUR\n // depend on variant *and* colour together, so those live on data-look instead\n // (see the comment on `look` in List, above).\n 'group-data-[variant=surface]/list:border-x group-data-[variant=surface]/list:border-t group-data-[variant=surface]/list:last:border-b',\n 'group-data-[variant=outline]/list:border-x group-data-[variant=outline]/list:border-t group-data-[variant=outline]/list:last:border-b',\n 'group-data-[look=soft]/list:bg-gray-3 dark:group-data-[look=soft]/list:bg-gray-a3',\n // arbitrary-var form, not the bg-spark-a3 utility: identical colour, but avoids a\n // literal substring collision with the unconditional selection/hover tint below,\n // which a couple of tests distinguish with a plain className.includes check.\n 'group-data-[look=soft-accent]/list:bg-(--spark-a3)',\n 'group-data-[look=surface]/list:border-gray-a6 group-data-[look=surface]/list:bg-gray-3 dark:group-data-[look=surface]/list:bg-gray-a3',\n 'group-data-[look=surface-accent]/list:border-spark-a6 group-data-[look=surface-accent]/list:bg-(--spark-surface)',\n 'group-data-[look=outline]/list:border-gray-a6',\n 'group-data-[look=outline-accent]/list:border-spark-a6',\n 'group-data-[look=solid]/list:bg-gray-1',\n 'group-data-[look=solid-accent]/list:bg-spark-9',\n Object.values(radiusRowClasses),\n className,\n )}\n {...props}\n >\n <ListItemContext.Provider value={recentCtx}>\n {leading}\n {row}\n </ListItemContext.Provider>\n </li>\n )\n}\n\nfunction ListItemSkeleton() {\n // Compose the shared Skeleton primitive so the placeholder matches every other skeleton\n // (token + radius + animation + motion-reduce).\n return (\n <>\n <Skeleton className=\"size-10 shrink-0 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Skeleton className=\"h-3.5 w-1/3\" />\n <Skeleton className=\"h-3.5 w-2/3\" />\n </div>\n <div className=\"flex min-w-0 flex-1 flex-col items-end gap-2\">\n <Skeleton className=\"h-3.5 w-1/4\" />\n <Skeleton className=\"h-3.5 w-1/2\" />\n </div>\n </>\n )\n}\n\ninterface ListItemLeadingProps extends React.ComponentProps<'div'> {\n /**\n * Set when this slot holds a focusable control (Checkbox, Radio, Button). ListItem\n * then hoists it outside `render`'s element — nesting a real control inside the row's\n * own <a>/<button> is invalid HTML and double-fires clicks. Leave unset for decorative\n * content (an icon): it stays inside the row, so the whole visual row is one\n * click/tap target, same as a row with no leading slot at all.\n */\n interactive?: boolean\n}\n\nfunction ListItemLeading({\n className,\n interactive: _interactive,\n ...props\n}: ListItemLeadingProps) {\n return (\n <div\n data-slot=\"list-item-leading\"\n className={cn('flex shrink-0 items-center', className)}\n {...props}\n />\n )\n}\n\ninterface ListItemContentProps extends React.ComponentProps<'div'> {\n /** `end` right-aligns the column (the value column). */\n align?: 'start' | 'end'\n}\n\nfunction ListItemContent({\n className,\n align = 'start',\n ...props\n}: ListItemContentProps) {\n const columnCtx = React.useMemo(() => ({ isEnd: align === 'end' }), [align])\n return (\n <ListItemColumnContext.Provider value={columnCtx}>\n <div\n data-slot=\"list-item-content\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col gap-1',\n align === 'end' ? 'items-end text-right' : 'items-start text-left',\n className,\n )}\n {...props}\n />\n </ListItemColumnContext.Provider>\n )\n}\n\nfunction ListItemPreline({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"list-item-preline\"\n className={cn(\n 'block w-full truncate text-sm text-(--list-item-fg-muted,var(--neutral-11))',\n className,\n )}\n {...props}\n />\n )\n}\n\ninterface ListItemTitleProps extends React.ComponentProps<'span'> {\n /** `strong` keeps the title bold, e.g. for menu rows; `default` is medium, bold on a `recent` row. */\n emphasis?: 'default' | 'strong'\n}\n\nfunction ListItemTitle({\n className,\n children,\n emphasis = 'default',\n ...props\n}: ListItemTitleProps) {\n const { recent } = React.useContext(ListItemContext)\n const { isEnd } = React.useContext(ListItemColumnContext)\n // recency dot sits inline, immediately left of the title (Figma 1310-977) — only on the\n // primary (start) column, so a two-column value row's value never carries it.\n const showDot = recent && !isEnd\n\n // className/{...props} always land on THIS span (the one that truncates), so a caller's\n // override (e.g. `title`, `line-clamp-2 whitespace-normal`) behaves identically whether\n // or not the dot renders — only the wrapping structure differs below.\n const title = (\n <span\n data-slot=\"list-item-title\"\n className={cn(\n showDot ? 'min-w-0 truncate' : 'block w-full truncate',\n 'text-base text-(--list-item-fg,var(--neutral-12))',\n recent || emphasis === 'strong' ? 'font-bold' : 'font-medium',\n className,\n )}\n {...props}\n >\n {children}\n </span>\n )\n\n if (!showDot) {\n // No dot: stay a plain block so the column's text alignment (e.g. an end column's\n // text-right) governs the title directly.\n return title\n }\n\n return (\n <span className=\"flex w-full items-center gap-2\">\n <span\n aria-hidden=\"true\"\n data-slot=\"list-item-indicator\"\n className=\"size-2 shrink-0 rounded-full bg-spark-9\"\n />\n {title}\n </span>\n )\n}\n\nfunction ListItemSubline({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"list-item-subline\"\n className={cn(\n 'block w-full truncate text-sm text-(--list-item-fg-muted,var(--neutral-11))',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction ListItemTrailing({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"list-item-trailing\"\n className={cn(\n 'flex shrink-0 items-center text-gray-11',\n // interactive rows tint the affordance accent on hover/focus (group/row is set on\n // the interactive inner row; absent on static rows, so this simply never fires).\n // Relies on the child reading currentColor (Spark icons do) — the colour-\n // independent Icon component hardcodes its own ink and won't pick this up; use\n // a Spark icon here, not Icon.\n 'transition-colors duration-150 motion-reduce:transition-none',\n 'group-hover/row:text-spark-a11 group-focus-visible/row:text-spark-a11',\n className,\n )}\n {...props}\n />\n )\n}\n\nexport {\n List,\n ListItem,\n ListItemContent,\n ListItemLeading,\n ListItemPreline,\n ListItemSubline,\n ListItemTitle,\n ListItemTrailing,\n}\nexport type {\n ListProps,\n ListItemProps,\n ListItemContentProps,\n ListItemLeadingProps,\n ListItemTitleProps,\n ListVariant,\n ListRadius,\n}\n"],"mappings":"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"}
@@ -6,7 +6,7 @@ import { useRender } from "@base-ui/react/use-render";
6
6
 
7
7
  //#region src/components/marker.d.ts
8
8
  declare const markerVariants: (props?: ({
9
- variant?: "default" | "separator" | "border" | null | undefined;
9
+ variant?: "separator" | "default" | "border" | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
11
  declare function Marker({
12
12
  className,
@@ -6,7 +6,7 @@ import { useRender } from "@base-ui/react/use-render";
6
6
 
7
7
  //#region src/components/marker.d.ts
8
8
  declare const markerVariants: (props?: ({
9
- variant?: "default" | "separator" | "border" | null | undefined;
9
+ variant?: "separator" | "default" | "border" | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
11
  declare function Marker({
12
12
  className,
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_chunk = require("../chunk-DY84GLCh.cjs");
4
4
  const require_hooks_use_reduced_motion = require("../hooks/use-reduced-motion.cjs");
5
- const require_lib_message_animations = require("../lib/message-animations.cjs");
6
5
  const require_components_button = require("./button.cjs");
6
+ const require_lib_message_animations = require("../lib/message-animations.cjs");
7
7
  let react = require("react");
8
8
  react = require_chunk.__toESM(react);
9
9
  let _epilot_spark_icons = require("@epilot/spark-icons");
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { useReducedMotion } from "../hooks/use-reduced-motion.js";
3
3
  import { cn } from "../utils/cn.js";
4
- import { MESSAGE_ANIMATIONS } from "../lib/message-animations.js";
5
4
  import { Button } from "./button.js";
5
+ import { MESSAGE_ANIMATIONS } from "../lib/message-animations.js";
6
6
  import * as React$1 from "react";
7
7
  import { ArrowDownward, ArrowUpward } from "@epilot/spark-icons";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { accentByColor } from "../lib/accent-colors.js";
3
2
  import { cn } from "../utils/cn.js";
3
+ import { accentByColor } from "../lib/accent-colors.js";
4
4
  import * as React$1 from "react";
5
5
  import { Close } from "@epilot/spark-icons";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -57,7 +57,7 @@ function PopoverContent({ className, align = "center", sideOffset = 4, side, ali
57
57
  sideOffset,
58
58
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_popover.Popover.Popup, {
59
59
  "data-slot": "popover-content",
60
- className: (0, cn.cn)("z-50 w-72 origin-(--transform-origin) rounded-lg border border-gray-a1 bg-gray-1 p-4 text-gray-a12 shadow-md outline-hidden transition-[opacity,scale,translate] duration-100 motion-reduce:transition-none", "data-starting-style:scale-95 data-starting-style:opacity-0", "data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2", "data-ending-style:scale-95 data-ending-style:opacity-0", className),
60
+ className: (0, cn.cn)("z-50 w-72 origin-(--transform-origin) rounded-lg border border-gray-a1 bg-gray-1 p-4 text-gray-a12 shadow-md outline-hidden transition-[opacity,scale,translate] duration-100 motion-reduce:transition-none dark:bg-gray-3", "data-starting-style:scale-95 data-starting-style:opacity-0", "data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2", "data-ending-style:scale-95 data-ending-style:opacity-0", className),
61
61
  ...props
62
62
  })
63
63
  })
@@ -1 +1 @@
1
- {"version":3,"file":"popover.cjs","names":["React","PopoverPrimitive"],"sources":["../../src/components/popover.tsx"],"sourcesContent":["import { mergeProps } from '@base-ui/react/merge-props'\nimport { Popover as PopoverPrimitive } from '@base-ui/react/popover'\nimport { useRender } from '@base-ui/react/use-render'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\n\n// Base UI dropped the Anchor part; the anchor element registers itself here and\n// PopoverContent feeds it to the Positioner.\nconst PopoverAnchorContext = React.createContext<{\n anchor: HTMLElement | null\n setAnchor: (element: HTMLElement | null) => void\n} | null>(null)\n\nfunction Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n const [anchor, setAnchor] = React.useState<HTMLElement | null>(null)\n const value = React.useMemo(() => ({ anchor, setAnchor }), [anchor])\n return (\n <PopoverAnchorContext.Provider value={value}>\n <PopoverPrimitive.Root {...props} />\n </PopoverAnchorContext.Provider>\n )\n}\n\nfunction PopoverTrigger(\n props: React.ComponentProps<typeof PopoverPrimitive.Trigger>,\n) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\n// A Close part inside the popup is also what enables Base UI's focus trap for modal popovers.\nfunction PopoverClose(\n props: React.ComponentProps<typeof PopoverPrimitive.Close>,\n) {\n return <PopoverPrimitive.Close data-slot=\"popover-close\" {...props} />\n}\n\n/** Anchors the popover to this element instead of the trigger. Pass `render` to\n * wrap an existing element (was radix `asChild`). */\nfunction PopoverAnchor({ render, ...props }: useRender.ComponentProps<'div'>) {\n const context = React.useContext(PopoverAnchorContext)\n return useRender({\n defaultTagName: 'div',\n render,\n ref: context?.setAnchor,\n props: mergeProps<'div'>(\n { 'data-slot': 'popover-anchor' } as React.ComponentProps<'div'>,\n props,\n ),\n })\n}\n\ntype PopoverContentProps = React.ComponentProps<typeof PopoverPrimitive.Popup> &\n Pick<\n PopoverPrimitive.Positioner.Props,\n | 'align'\n | 'alignOffset'\n | 'side'\n | 'sideOffset'\n | 'anchor'\n | 'collisionAvoidance'\n > &\n Pick<PopoverPrimitive.Portal.Props, 'container'>\n\nfunction PopoverContent({\n className,\n align = 'center',\n sideOffset = 4,\n side,\n alignOffset,\n anchor,\n collisionAvoidance,\n container,\n ...props\n}: PopoverContentProps) {\n const context = React.useContext(PopoverAnchorContext)\n return (\n <PopoverPrimitive.Portal container={container}>\n <PopoverPrimitive.Positioner\n align={align}\n alignOffset={alignOffset}\n anchor={anchor ?? context?.anchor ?? undefined}\n className=\"isolate z-50\"\n collisionAvoidance={collisionAvoidance}\n side={side}\n sideOffset={sideOffset}\n >\n <PopoverPrimitive.Popup\n data-slot=\"popover-content\"\n className={cn(\n 'z-50 w-72 origin-(--transform-origin) rounded-lg border border-gray-a1 bg-gray-1 p-4 text-gray-a12 shadow-md outline-hidden transition-[opacity,scale,translate] duration-100 motion-reduce:transition-none',\n // enter: fade + zoom + a slight per-side slide (was animate-in)\n 'data-starting-style:scale-95 data-starting-style:opacity-0',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n // exit: fade + zoom (was animate-out)\n 'data-ending-style:scale-95 data-ending-style:opacity-0',\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Positioner>\n </PopoverPrimitive.Portal>\n )\n}\n\nexport { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger }\n"],"mappings":";;;;;;;;;;;AAQA,MAAM,uBAAuBA,MAAM,cAGzB,IAAI;AAEd,SAAS,QAAQ,OAA2D;CAC1E,MAAM,CAAC,QAAQ,aAAaA,MAAM,SAA6B,IAAI;CACnE,MAAM,QAAQA,MAAM,eAAe;EAAE;EAAQ;CAAU,IAAI,CAAC,MAAM,CAAC;CACnE,OACE,iBAAA,GAAA,kBAAA,KAAC,qBAAqB,UAAtB;EAAsC;YACpC,iBAAA,GAAA,kBAAA,KAACC,uBAAAA,QAAiB,MAAlB,EAAuB,GAAI,MAAQ,CAAA;CACN,CAAA;AAEnC;AAEA,SAAS,eACP,OACA;CACA,OAAO,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,QAAiB,SAAlB;EAA0B,aAAU;EAAkB,GAAI;CAAQ,CAAA;AAC3E;AAGA,SAAS,aACP,OACA;CACA,OAAO,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,QAAiB,OAAlB;EAAwB,aAAU;EAAgB,GAAI;CAAQ,CAAA;AACvE;;;AAIA,SAAS,cAAc,EAAE,QAAQ,GAAG,SAA0C;CAE5E,QAAA,GAAA,0BAAA,WAAiB;EACf,gBAAgB;EAChB;EACA,KAJcD,MAAM,WAAW,oBAIpB,GAAG;EACd,QAAA,GAAA,2BAAA,YACE,EAAE,aAAa,iBAAiB,GAChC,KACF;CACF,CAAC;AACH;AAcA,SAAS,eAAe,EACtB,WACA,QAAQ,UACR,aAAa,GACb,MACA,aACA,QACA,oBACA,WACA,GAAG,SACmB;CACtB,MAAM,UAAUA,MAAM,WAAW,oBAAoB;CACrD,OACE,iBAAA,GAAA,kBAAA,KAACC,uBAAAA,QAAiB,QAAlB;EAAoC;YAClC,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,QAAiB,YAAlB;GACS;GACM;GACb,QAAQ,UAAU,SAAS,UAAU,KAAA;GACrC,WAAU;GACU;GACd;GACM;aAEZ,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,QAAiB,OAAlB;IACE,aAAU;IACV,YAAA,GAAA,GAAA,IACE,+MAEA,8DACA,mNAEA,0DACA,SACF;IACA,GAAI;GACL,CAAA;EAC0B,CAAA;CACN,CAAA;AAE7B"}
1
+ {"version":3,"file":"popover.cjs","names":["React","PopoverPrimitive"],"sources":["../../src/components/popover.tsx"],"sourcesContent":["import { mergeProps } from '@base-ui/react/merge-props'\nimport { Popover as PopoverPrimitive } from '@base-ui/react/popover'\nimport { useRender } from '@base-ui/react/use-render'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\n\n// Base UI dropped the Anchor part; the anchor element registers itself here and\n// PopoverContent feeds it to the Positioner.\nconst PopoverAnchorContext = React.createContext<{\n anchor: HTMLElement | null\n setAnchor: (element: HTMLElement | null) => void\n} | null>(null)\n\nfunction Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n const [anchor, setAnchor] = React.useState<HTMLElement | null>(null)\n const value = React.useMemo(() => ({ anchor, setAnchor }), [anchor])\n return (\n <PopoverAnchorContext.Provider value={value}>\n <PopoverPrimitive.Root {...props} />\n </PopoverAnchorContext.Provider>\n )\n}\n\nfunction PopoverTrigger(\n props: React.ComponentProps<typeof PopoverPrimitive.Trigger>,\n) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\n// A Close part inside the popup is also what enables Base UI's focus trap for modal popovers.\nfunction PopoverClose(\n props: React.ComponentProps<typeof PopoverPrimitive.Close>,\n) {\n return <PopoverPrimitive.Close data-slot=\"popover-close\" {...props} />\n}\n\n/** Anchors the popover to this element instead of the trigger. Pass `render` to\n * wrap an existing element (was radix `asChild`). */\nfunction PopoverAnchor({ render, ...props }: useRender.ComponentProps<'div'>) {\n const context = React.useContext(PopoverAnchorContext)\n return useRender({\n defaultTagName: 'div',\n render,\n ref: context?.setAnchor,\n props: mergeProps<'div'>(\n { 'data-slot': 'popover-anchor' } as React.ComponentProps<'div'>,\n props,\n ),\n })\n}\n\ntype PopoverContentProps = React.ComponentProps<typeof PopoverPrimitive.Popup> &\n Pick<\n PopoverPrimitive.Positioner.Props,\n | 'align'\n | 'alignOffset'\n | 'side'\n | 'sideOffset'\n | 'anchor'\n | 'collisionAvoidance'\n > &\n Pick<PopoverPrimitive.Portal.Props, 'container'>\n\nfunction PopoverContent({\n className,\n align = 'center',\n sideOffset = 4,\n side,\n alignOffset,\n anchor,\n collisionAvoidance,\n container,\n ...props\n}: PopoverContentProps) {\n const context = React.useContext(PopoverAnchorContext)\n return (\n <PopoverPrimitive.Portal container={container}>\n <PopoverPrimitive.Positioner\n align={align}\n alignOffset={alignOffset}\n anchor={anchor ?? context?.anchor ?? undefined}\n className=\"isolate z-50\"\n collisionAvoidance={collisionAvoidance}\n side={side}\n sideOffset={sideOffset}\n >\n <PopoverPrimitive.Popup\n data-slot=\"popover-content\"\n className={cn(\n 'z-50 w-72 origin-(--transform-origin) rounded-lg border border-gray-a1 bg-gray-1 p-4 text-gray-a12 shadow-md outline-hidden transition-[opacity,scale,translate] duration-100 motion-reduce:transition-none dark:bg-gray-3',\n // enter: fade + zoom + a slight per-side slide (was animate-in)\n 'data-starting-style:scale-95 data-starting-style:opacity-0',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n // exit: fade + zoom (was animate-out)\n 'data-ending-style:scale-95 data-ending-style:opacity-0',\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Positioner>\n </PopoverPrimitive.Portal>\n )\n}\n\nexport { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger }\n"],"mappings":";;;;;;;;;;;AAQA,MAAM,uBAAuBA,MAAM,cAGzB,IAAI;AAEd,SAAS,QAAQ,OAA2D;CAC1E,MAAM,CAAC,QAAQ,aAAaA,MAAM,SAA6B,IAAI;CACnE,MAAM,QAAQA,MAAM,eAAe;EAAE;EAAQ;CAAU,IAAI,CAAC,MAAM,CAAC;CACnE,OACE,iBAAA,GAAA,kBAAA,KAAC,qBAAqB,UAAtB;EAAsC;YACpC,iBAAA,GAAA,kBAAA,KAACC,uBAAAA,QAAiB,MAAlB,EAAuB,GAAI,MAAQ,CAAA;CACN,CAAA;AAEnC;AAEA,SAAS,eACP,OACA;CACA,OAAO,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,QAAiB,SAAlB;EAA0B,aAAU;EAAkB,GAAI;CAAQ,CAAA;AAC3E;AAGA,SAAS,aACP,OACA;CACA,OAAO,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,QAAiB,OAAlB;EAAwB,aAAU;EAAgB,GAAI;CAAQ,CAAA;AACvE;;;AAIA,SAAS,cAAc,EAAE,QAAQ,GAAG,SAA0C;CAE5E,QAAA,GAAA,0BAAA,WAAiB;EACf,gBAAgB;EAChB;EACA,KAJcD,MAAM,WAAW,oBAIpB,GAAG;EACd,QAAA,GAAA,2BAAA,YACE,EAAE,aAAa,iBAAiB,GAChC,KACF;CACF,CAAC;AACH;AAcA,SAAS,eAAe,EACtB,WACA,QAAQ,UACR,aAAa,GACb,MACA,aACA,QACA,oBACA,WACA,GAAG,SACmB;CACtB,MAAM,UAAUA,MAAM,WAAW,oBAAoB;CACrD,OACE,iBAAA,GAAA,kBAAA,KAACC,uBAAAA,QAAiB,QAAlB;EAAoC;YAClC,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,QAAiB,YAAlB;GACS;GACM;GACb,QAAQ,UAAU,SAAS,UAAU,KAAA;GACrC,WAAU;GACU;GACd;GACM;aAEZ,iBAAA,GAAA,kBAAA,KAACA,uBAAAA,QAAiB,OAAlB;IACE,aAAU;IACV,YAAA,GAAA,GAAA,IACE,8NAEA,8DACA,mNAEA,0DACA,SACF;IACA,GAAI;GACL,CAAA;EAC0B,CAAA;CACN,CAAA;AAE7B"}
@@ -54,7 +54,7 @@ function PopoverContent({ className, align = "center", sideOffset = 4, side, ali
54
54
  sideOffset,
55
55
  children: /* @__PURE__ */ jsx(Popover$1.Popup, {
56
56
  "data-slot": "popover-content",
57
- className: cn("z-50 w-72 origin-(--transform-origin) rounded-lg border border-gray-a1 bg-gray-1 p-4 text-gray-a12 shadow-md outline-hidden transition-[opacity,scale,translate] duration-100 motion-reduce:transition-none", "data-starting-style:scale-95 data-starting-style:opacity-0", "data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2", "data-ending-style:scale-95 data-ending-style:opacity-0", className),
57
+ className: cn("z-50 w-72 origin-(--transform-origin) rounded-lg border border-gray-a1 bg-gray-1 p-4 text-gray-a12 shadow-md outline-hidden transition-[opacity,scale,translate] duration-100 motion-reduce:transition-none dark:bg-gray-3", "data-starting-style:scale-95 data-starting-style:opacity-0", "data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2", "data-ending-style:scale-95 data-ending-style:opacity-0", className),
58
58
  ...props
59
59
  })
60
60
  })
@@ -1 +1 @@
1
- {"version":3,"file":"popover.js","names":["React","PopoverPrimitive"],"sources":["../../src/components/popover.tsx"],"sourcesContent":["import { mergeProps } from '@base-ui/react/merge-props'\nimport { Popover as PopoverPrimitive } from '@base-ui/react/popover'\nimport { useRender } from '@base-ui/react/use-render'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\n\n// Base UI dropped the Anchor part; the anchor element registers itself here and\n// PopoverContent feeds it to the Positioner.\nconst PopoverAnchorContext = React.createContext<{\n anchor: HTMLElement | null\n setAnchor: (element: HTMLElement | null) => void\n} | null>(null)\n\nfunction Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n const [anchor, setAnchor] = React.useState<HTMLElement | null>(null)\n const value = React.useMemo(() => ({ anchor, setAnchor }), [anchor])\n return (\n <PopoverAnchorContext.Provider value={value}>\n <PopoverPrimitive.Root {...props} />\n </PopoverAnchorContext.Provider>\n )\n}\n\nfunction PopoverTrigger(\n props: React.ComponentProps<typeof PopoverPrimitive.Trigger>,\n) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\n// A Close part inside the popup is also what enables Base UI's focus trap for modal popovers.\nfunction PopoverClose(\n props: React.ComponentProps<typeof PopoverPrimitive.Close>,\n) {\n return <PopoverPrimitive.Close data-slot=\"popover-close\" {...props} />\n}\n\n/** Anchors the popover to this element instead of the trigger. Pass `render` to\n * wrap an existing element (was radix `asChild`). */\nfunction PopoverAnchor({ render, ...props }: useRender.ComponentProps<'div'>) {\n const context = React.useContext(PopoverAnchorContext)\n return useRender({\n defaultTagName: 'div',\n render,\n ref: context?.setAnchor,\n props: mergeProps<'div'>(\n { 'data-slot': 'popover-anchor' } as React.ComponentProps<'div'>,\n props,\n ),\n })\n}\n\ntype PopoverContentProps = React.ComponentProps<typeof PopoverPrimitive.Popup> &\n Pick<\n PopoverPrimitive.Positioner.Props,\n | 'align'\n | 'alignOffset'\n | 'side'\n | 'sideOffset'\n | 'anchor'\n | 'collisionAvoidance'\n > &\n Pick<PopoverPrimitive.Portal.Props, 'container'>\n\nfunction PopoverContent({\n className,\n align = 'center',\n sideOffset = 4,\n side,\n alignOffset,\n anchor,\n collisionAvoidance,\n container,\n ...props\n}: PopoverContentProps) {\n const context = React.useContext(PopoverAnchorContext)\n return (\n <PopoverPrimitive.Portal container={container}>\n <PopoverPrimitive.Positioner\n align={align}\n alignOffset={alignOffset}\n anchor={anchor ?? context?.anchor ?? undefined}\n className=\"isolate z-50\"\n collisionAvoidance={collisionAvoidance}\n side={side}\n sideOffset={sideOffset}\n >\n <PopoverPrimitive.Popup\n data-slot=\"popover-content\"\n className={cn(\n 'z-50 w-72 origin-(--transform-origin) rounded-lg border border-gray-a1 bg-gray-1 p-4 text-gray-a12 shadow-md outline-hidden transition-[opacity,scale,translate] duration-100 motion-reduce:transition-none',\n // enter: fade + zoom + a slight per-side slide (was animate-in)\n 'data-starting-style:scale-95 data-starting-style:opacity-0',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n // exit: fade + zoom (was animate-out)\n 'data-ending-style:scale-95 data-ending-style:opacity-0',\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Positioner>\n </PopoverPrimitive.Portal>\n )\n}\n\nexport { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger }\n"],"mappings":";;;;;;;;AAQA,MAAM,uBAAuBA,QAAM,cAGzB,IAAI;AAEd,SAAS,QAAQ,OAA2D;CAC1E,MAAM,CAAC,QAAQ,aAAaA,QAAM,SAA6B,IAAI;CACnE,MAAM,QAAQA,QAAM,eAAe;EAAE;EAAQ;CAAU,IAAI,CAAC,MAAM,CAAC;CACnE,OACE,oBAAC,qBAAqB,UAAtB;EAAsC;YACpC,oBAACC,UAAiB,MAAlB,EAAuB,GAAI,MAAQ,CAAA;CACN,CAAA;AAEnC;AAEA,SAAS,eACP,OACA;CACA,OAAO,oBAACA,UAAiB,SAAlB;EAA0B,aAAU;EAAkB,GAAI;CAAQ,CAAA;AAC3E;AAGA,SAAS,aACP,OACA;CACA,OAAO,oBAACA,UAAiB,OAAlB;EAAwB,aAAU;EAAgB,GAAI;CAAQ,CAAA;AACvE;;;AAIA,SAAS,cAAc,EAAE,QAAQ,GAAG,SAA0C;CAE5E,OAAO,UAAU;EACf,gBAAgB;EAChB;EACA,KAJcD,QAAM,WAAW,oBAIpB,GAAG;EACd,OAAO,WACL,EAAE,aAAa,iBAAiB,GAChC,KACF;CACF,CAAC;AACH;AAcA,SAAS,eAAe,EACtB,WACA,QAAQ,UACR,aAAa,GACb,MACA,aACA,QACA,oBACA,WACA,GAAG,SACmB;CACtB,MAAM,UAAUA,QAAM,WAAW,oBAAoB;CACrD,OACE,oBAACC,UAAiB,QAAlB;EAAoC;YAClC,oBAACA,UAAiB,YAAlB;GACS;GACM;GACb,QAAQ,UAAU,SAAS,UAAU,KAAA;GACrC,WAAU;GACU;GACd;GACM;aAEZ,oBAACA,UAAiB,OAAlB;IACE,aAAU;IACV,WAAW,GACT,+MAEA,8DACA,mNAEA,0DACA,SACF;IACA,GAAI;GACL,CAAA;EAC0B,CAAA;CACN,CAAA;AAE7B"}
1
+ {"version":3,"file":"popover.js","names":["React","PopoverPrimitive"],"sources":["../../src/components/popover.tsx"],"sourcesContent":["import { mergeProps } from '@base-ui/react/merge-props'\nimport { Popover as PopoverPrimitive } from '@base-ui/react/popover'\nimport { useRender } from '@base-ui/react/use-render'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\n\n// Base UI dropped the Anchor part; the anchor element registers itself here and\n// PopoverContent feeds it to the Positioner.\nconst PopoverAnchorContext = React.createContext<{\n anchor: HTMLElement | null\n setAnchor: (element: HTMLElement | null) => void\n} | null>(null)\n\nfunction Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n const [anchor, setAnchor] = React.useState<HTMLElement | null>(null)\n const value = React.useMemo(() => ({ anchor, setAnchor }), [anchor])\n return (\n <PopoverAnchorContext.Provider value={value}>\n <PopoverPrimitive.Root {...props} />\n </PopoverAnchorContext.Provider>\n )\n}\n\nfunction PopoverTrigger(\n props: React.ComponentProps<typeof PopoverPrimitive.Trigger>,\n) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\n// A Close part inside the popup is also what enables Base UI's focus trap for modal popovers.\nfunction PopoverClose(\n props: React.ComponentProps<typeof PopoverPrimitive.Close>,\n) {\n return <PopoverPrimitive.Close data-slot=\"popover-close\" {...props} />\n}\n\n/** Anchors the popover to this element instead of the trigger. Pass `render` to\n * wrap an existing element (was radix `asChild`). */\nfunction PopoverAnchor({ render, ...props }: useRender.ComponentProps<'div'>) {\n const context = React.useContext(PopoverAnchorContext)\n return useRender({\n defaultTagName: 'div',\n render,\n ref: context?.setAnchor,\n props: mergeProps<'div'>(\n { 'data-slot': 'popover-anchor' } as React.ComponentProps<'div'>,\n props,\n ),\n })\n}\n\ntype PopoverContentProps = React.ComponentProps<typeof PopoverPrimitive.Popup> &\n Pick<\n PopoverPrimitive.Positioner.Props,\n | 'align'\n | 'alignOffset'\n | 'side'\n | 'sideOffset'\n | 'anchor'\n | 'collisionAvoidance'\n > &\n Pick<PopoverPrimitive.Portal.Props, 'container'>\n\nfunction PopoverContent({\n className,\n align = 'center',\n sideOffset = 4,\n side,\n alignOffset,\n anchor,\n collisionAvoidance,\n container,\n ...props\n}: PopoverContentProps) {\n const context = React.useContext(PopoverAnchorContext)\n return (\n <PopoverPrimitive.Portal container={container}>\n <PopoverPrimitive.Positioner\n align={align}\n alignOffset={alignOffset}\n anchor={anchor ?? context?.anchor ?? undefined}\n className=\"isolate z-50\"\n collisionAvoidance={collisionAvoidance}\n side={side}\n sideOffset={sideOffset}\n >\n <PopoverPrimitive.Popup\n data-slot=\"popover-content\"\n className={cn(\n 'z-50 w-72 origin-(--transform-origin) rounded-lg border border-gray-a1 bg-gray-1 p-4 text-gray-a12 shadow-md outline-hidden transition-[opacity,scale,translate] duration-100 motion-reduce:transition-none dark:bg-gray-3',\n // enter: fade + zoom + a slight per-side slide (was animate-in)\n 'data-starting-style:scale-95 data-starting-style:opacity-0',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n // exit: fade + zoom (was animate-out)\n 'data-ending-style:scale-95 data-ending-style:opacity-0',\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Positioner>\n </PopoverPrimitive.Portal>\n )\n}\n\nexport { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger }\n"],"mappings":";;;;;;;;AAQA,MAAM,uBAAuBA,QAAM,cAGzB,IAAI;AAEd,SAAS,QAAQ,OAA2D;CAC1E,MAAM,CAAC,QAAQ,aAAaA,QAAM,SAA6B,IAAI;CACnE,MAAM,QAAQA,QAAM,eAAe;EAAE;EAAQ;CAAU,IAAI,CAAC,MAAM,CAAC;CACnE,OACE,oBAAC,qBAAqB,UAAtB;EAAsC;YACpC,oBAACC,UAAiB,MAAlB,EAAuB,GAAI,MAAQ,CAAA;CACN,CAAA;AAEnC;AAEA,SAAS,eACP,OACA;CACA,OAAO,oBAACA,UAAiB,SAAlB;EAA0B,aAAU;EAAkB,GAAI;CAAQ,CAAA;AAC3E;AAGA,SAAS,aACP,OACA;CACA,OAAO,oBAACA,UAAiB,OAAlB;EAAwB,aAAU;EAAgB,GAAI;CAAQ,CAAA;AACvE;;;AAIA,SAAS,cAAc,EAAE,QAAQ,GAAG,SAA0C;CAE5E,OAAO,UAAU;EACf,gBAAgB;EAChB;EACA,KAJcD,QAAM,WAAW,oBAIpB,GAAG;EACd,OAAO,WACL,EAAE,aAAa,iBAAiB,GAChC,KACF;CACF,CAAC;AACH;AAcA,SAAS,eAAe,EACtB,WACA,QAAQ,UACR,aAAa,GACb,MACA,aACA,QACA,oBACA,WACA,GAAG,SACmB;CACtB,MAAM,UAAUA,QAAM,WAAW,oBAAoB;CACrD,OACE,oBAACC,UAAiB,QAAlB;EAAoC;YAClC,oBAACA,UAAiB,YAAlB;GACS;GACM;GACb,QAAQ,UAAU,SAAS,UAAU,KAAA;GACrC,WAAU;GACU;GACd;GACM;aAEZ,oBAACA,UAAiB,OAAlB;IACE,aAAU;IACV,WAAW,GACT,8NAEA,8DACA,mNAEA,0DACA,SACF;IACA,GAAI;GACL,CAAA;EAC0B,CAAA;CACN,CAAA;AAE7B"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { accentByColor } from "../lib/accent-colors.js";
3
2
  import { cn } from "../utils/cn.js";
3
+ import { accentByColor } from "../lib/accent-colors.js";
4
4
  import "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import { Radio as Radio$1 } from "@base-ui/react/radio";
@@ -2,8 +2,8 @@
2
2
  "use client";
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  const require_chunk = require("../chunk-DY84GLCh.cjs");
5
- const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
6
5
  const require_components_field = require("./field.cjs");
6
+ const require_lib_input_box_variants = require("../lib/input-box-variants.cjs");
7
7
  let react = require("react");
8
8
  react = require_chunk.__toESM(react);
9
9
  let _epilot_spark_icons = require("@epilot/spark-icons");
@@ -88,7 +88,7 @@ function SelectContent({ className, children, align = "start", alignOffset, side
88
88
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_base_ui_react_select.Select.Popup, {
89
89
  "data-slot": "select-content",
90
90
  "data-align-trigger": alignItemWithTrigger,
91
- className: (0, cn.cn)("relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg [[data-high-contrast]_&]:border-gray-a11", "transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none", "data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2", "data-[align-trigger=true]:transition-none", className),
91
+ className: (0, cn.cn)("relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg dark:bg-gray-3 [[data-high-contrast]_&]:border-gray-a11", "transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none", "data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2", "data-[align-trigger=true]:transition-none", className),
92
92
  ...props,
93
93
  children: [
94
94
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectScrollUpButton, {}),
@@ -128,7 +128,7 @@ function SelectSeparator({ className, ...props }) {
128
128
  function SelectScrollUpButton({ className, ...props }) {
129
129
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_select.Select.ScrollUpArrow, {
130
130
  "data-slot": "select-scroll-up-button",
131
- className: (0, cn.cn)("top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4", className),
131
+ className: (0, cn.cn)("top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 dark:bg-gray-3 [&_svg:not([class*=size-])]:size-4", className),
132
132
  ...props,
133
133
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_epilot_spark_icons.KeyboardArrowDown, { className: "rotate-180 [--spark-icon-size:14px]" })
134
134
  });
@@ -136,7 +136,7 @@ function SelectScrollUpButton({ className, ...props }) {
136
136
  function SelectScrollDownButton({ className, ...props }) {
137
137
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_select.Select.ScrollDownArrow, {
138
138
  "data-slot": "select-scroll-down-button",
139
- className: (0, cn.cn)("bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4", className),
139
+ className: (0, cn.cn)("bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 dark:bg-gray-3 [&_svg:not([class*=size-])]:size-4", className),
140
140
  ...props,
141
141
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_epilot_spark_icons.KeyboardArrowDown, { className: "[--spark-icon-size:14px]" })
142
142
  });
@@ -1 +1 @@
1
- {"version":3,"file":"select.cjs","names":["SelectPrimitive","useFieldControl","boxChrome","KeyboardArrowDown","Close","Check"],"sources":["../../src/components/select.tsx"],"sourcesContent":["'use client'\n\nimport { Select as SelectPrimitive } from '@base-ui/react/select'\nimport { Check, Close, KeyboardArrowDown } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { boxChrome } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// bare re-export: Root renders no element and its props are generic over Value\nconst Select = SelectPrimitive.Root\n\nfunction SelectGroup({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return (\n <SelectPrimitive.Group\n data-slot=\"select-group\"\n className={cn('scroll-my-1 p-1', className)}\n {...props}\n />\n )\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\nconst TRIGGER_BASE =\n 'relative flex items-center justify-between gap-1.5 rounded-md px-3 text-base whitespace-nowrap text-gray outline-hidden transition-colors select-none data-placeholder:text-gray-placeholder disabled:cursor-not-allowed disabled:text-gray-disabled disabled:data-placeholder:text-gray-disabled *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4'\n\n// box-chrome sizing only — a ghost/inset trigger sizes via boxChrome.ghost's\n// GHOST_UNDERLINE (h-10), matching Input's underline row exactly (no competing\n// height utility). `min-h-[2.75rem]` is a literal, non-knob-scaled WCAG 2.5.5\n// floor; `sm` stays visually compact but restores the floor via an invisible\n// `before:` hit overlay rather than growing the box, with `my-1` to keep that\n// overlay out of an ancestor scroller's overflow.\nconst TRIGGER_SIZE = {\n default: 'h-11 min-h-[2.75rem]',\n sm: \"my-1 h-9 before:absolute before:inset-x-0 before:top-1/2 before:min-h-[2.75rem] before:-translate-y-1/2 before:content-['']\",\n} as const\n\nfunction SelectTrigger({\n className,\n size = 'default',\n variant,\n id: idProp,\n children,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: 'sm' | 'default'\n // input-style override; falls back to Field / <Theme>, then soft (like Input)\n variant?: SparkInputStyle\n}) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant,\n })\n // inside an inset field the shell is the box, so the trigger goes bare\n // (underline row), matching Input's inset chrome; an explicit variant keeps\n // the box even inside an inset field (mirrors Input)\n const isGhost =\n inputStyle === 'ghost' || (!variant && field?.labelPosition === 'inset')\n\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n id={id}\n className={cn(\n TRIGGER_BASE,\n // fills the field shell like an Input; sizes to content when standalone\n field ? 'w-full' : 'w-fit',\n !isGhost && TRIGGER_SIZE[size],\n boxChrome[isGhost ? 'ghost' : inputStyle][inputColor],\n className,\n )}\n aria-invalid={ariaInvalid}\n {...props}\n aria-describedby={ariaDescribedBy}\n >\n {children}\n <SelectPrimitive.Icon\n className=\"pointer-events-none flex text-gray-light\"\n data-slot=\"select-chevron\"\n >\n <KeyboardArrowDown className=\"[--spark-icon-size:14px]\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n}\n\n// px floor: axe target-size flags it below 24px at small scalings because it overlays\n// the trigger; autocomplete.tsx ICON_BUTTON sits in flow and does not need one\nconst CLEAR_BUTTON =\n \"relative inline-flex size-6 min-h-[24px] min-w-[24px] shrink-0 items-center justify-center rounded-sm p-0 text-gray-light outline-hidden transition-colors hover:text-gray focus-visible:ring-2 focus-visible:ring-(--color-focus-root)! focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 before:absolute before:top-1/2 before:left-1/2 before:size-10 before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']\"\n\n// wraps SelectTrigger since Base UI ships no Select.Clear and the trigger is a\n// native <button> — the clear sits beside it, not nested inside it\nfunction SelectTriggerGroup({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"select-trigger-group\"\n className={cn(\n 'relative inline-flex w-fit items-center [&_[data-slot=select-chevron]]:absolute [&_[data-slot=select-chevron]]:right-3 has-[[data-slot=select-trigger][data-disabled]]:[&_[data-slot=select-clear]]:hidden has-[[data-slot=select-trigger][data-placeholder]]:[&_[data-slot=select-clear]]:hidden has-[[data-slot=select-trigger][data-readonly]]:[&_[data-slot=select-clear]]:hidden [&_[data-slot=select-trigger]]:pr-[calc(--spacing(8)+max(--spacing(6),24px))]',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectClear({\n className,\n children,\n onClick,\n ...props\n}: React.ComponentProps<'button'>) {\n return (\n <button\n data-slot=\"select-clear\"\n type=\"button\"\n className={cn(CLEAR_BUTTON, 'absolute right-8', className)}\n onClick={(event) => {\n onClick?.(event)\n // clearing hides this button (data-placeholder returns), so keyboard\n // focus would otherwise drop to <body> — return it to the trigger\n event.currentTarget\n .closest('[data-slot=\"select-trigger-group\"]')\n ?.querySelector<HTMLElement>('[data-slot=\"select-trigger\"]')\n ?.focus()\n }}\n {...props}\n >\n {children ?? <Close aria-hidden className=\"[--spark-icon-size:14px]\" />}\n </button>\n )\n}\n\ntype SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Popup> &\n Pick<\n SelectPrimitive.Positioner.Props,\n 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'\n > &\n Pick<SelectPrimitive.Portal.Props, 'container'>\n\nfunction SelectContent({\n className,\n children,\n align = 'start',\n alignOffset,\n side,\n sideOffset = 4,\n // drop the menu below the trigger (left-aligned) like a conventional form\n // dropdown; the native-<select>-style overlay (Base UI's inherited default)\n // shifts the popup to align item text with trigger text, exposing a sliver of\n // the trigger's fill at the edge. Opt back in per instance if you want it.\n alignItemWithTrigger = false,\n container,\n ...props\n}: SelectContentProps) {\n return (\n <SelectPrimitive.Portal container={container}>\n <SelectPrimitive.Positioner\n align={align}\n alignItemWithTrigger={alignItemWithTrigger}\n alignOffset={alignOffset}\n side={side}\n sideOffset={sideOffset}\n >\n <SelectPrimitive.Popup\n data-slot=\"select-content\"\n data-align-trigger={alignItemWithTrigger}\n className={cn(\n // match the trigger's width (never narrower) so the drop-below menu\n // lines up under it; --anchor-width is the trigger width, floored at\n // 9rem for very narrow triggers\n 'relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg [[data-high-contrast]_&]:border-gray-a11',\n 'transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n 'data-[align-trigger=true]:transition-none',\n className,\n )}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.List>{children}</SelectPrimitive.List>\n <SelectScrollDownButton />\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.GroupLabel>) {\n return (\n <SelectPrimitive.GroupLabel\n data-slot=\"select-label\"\n className={cn(\n 'px-1.5 py-1 text-xs text-gray-light [[data-high-contrast]_&]:text-gray-a12',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n 'relative flex w-full cursor-default items-center gap-1.5 rounded-sm py-1.5 pr-8 pl-1.5 text-sm text-gray outline-hidden transition-colors select-none touch-target-row data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-gray-a3 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',\n className,\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"[--spark-icon-size:11px]\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\n// plain div, not Select.Separator: role=\"separator\" isn't a permitted child of a\n// listbox (aria-required-children), and the divider is decorative — the groups\n// carry the semantics. Hidden from assistive tech.\nfunction SelectSeparator({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n aria-hidden=\"true\"\n data-slot=\"select-separator\"\n className={cn(\n 'pointer-events-none -mx-1 my-1 h-px bg-gray-a3 [[data-high-contrast]_&]:bg-gray-a6',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {\n return (\n <SelectPrimitive.ScrollUpArrow\n data-slot=\"select-scroll-up-button\"\n className={cn(\n 'top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <KeyboardArrowDown className=\"rotate-180 [--spark-icon-size:14px]\" />\n </SelectPrimitive.ScrollUpArrow>\n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {\n return (\n <SelectPrimitive.ScrollDownArrow\n data-slot=\"select-scroll-down-button\"\n className={cn(\n 'bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <KeyboardArrowDown className=\"[--spark-icon-size:14px]\" />\n </SelectPrimitive.ScrollDownArrow>\n )\n}\n\nexport {\n Select,\n SelectClear,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectTriggerGroup,\n SelectValue,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.cjs","names":["SelectPrimitive","useFieldControl","boxChrome","KeyboardArrowDown","Close","Check"],"sources":["../../src/components/select.tsx"],"sourcesContent":["'use client'\n\nimport { Select as SelectPrimitive } from '@base-ui/react/select'\nimport { Check, Close, KeyboardArrowDown } from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { boxChrome } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// bare re-export: Root renders no element and its props are generic over Value\nconst Select = SelectPrimitive.Root\n\nfunction SelectGroup({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return (\n <SelectPrimitive.Group\n data-slot=\"select-group\"\n className={cn('scroll-my-1 p-1', className)}\n {...props}\n />\n )\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\nconst TRIGGER_BASE =\n 'relative flex items-center justify-between gap-1.5 rounded-md px-3 text-base whitespace-nowrap text-gray outline-hidden transition-colors select-none data-placeholder:text-gray-placeholder disabled:cursor-not-allowed disabled:text-gray-disabled disabled:data-placeholder:text-gray-disabled *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4'\n\n// box-chrome sizing only — a ghost/inset trigger sizes via boxChrome.ghost's\n// GHOST_UNDERLINE (h-10), matching Input's underline row exactly (no competing\n// height utility). `min-h-[2.75rem]` is a literal, non-knob-scaled WCAG 2.5.5\n// floor; `sm` stays visually compact but restores the floor via an invisible\n// `before:` hit overlay rather than growing the box, with `my-1` to keep that\n// overlay out of an ancestor scroller's overflow.\nconst TRIGGER_SIZE = {\n default: 'h-11 min-h-[2.75rem]',\n sm: \"my-1 h-9 before:absolute before:inset-x-0 before:top-1/2 before:min-h-[2.75rem] before:-translate-y-1/2 before:content-['']\",\n} as const\n\nfunction SelectTrigger({\n className,\n size = 'default',\n variant,\n id: idProp,\n children,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: 'sm' | 'default'\n // input-style override; falls back to Field / <Theme>, then soft (like Input)\n variant?: SparkInputStyle\n}) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant,\n })\n // inside an inset field the shell is the box, so the trigger goes bare\n // (underline row), matching Input's inset chrome; an explicit variant keeps\n // the box even inside an inset field (mirrors Input)\n const isGhost =\n inputStyle === 'ghost' || (!variant && field?.labelPosition === 'inset')\n\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n id={id}\n className={cn(\n TRIGGER_BASE,\n // fills the field shell like an Input; sizes to content when standalone\n field ? 'w-full' : 'w-fit',\n !isGhost && TRIGGER_SIZE[size],\n boxChrome[isGhost ? 'ghost' : inputStyle][inputColor],\n className,\n )}\n aria-invalid={ariaInvalid}\n {...props}\n aria-describedby={ariaDescribedBy}\n >\n {children}\n <SelectPrimitive.Icon\n className=\"pointer-events-none flex text-gray-light\"\n data-slot=\"select-chevron\"\n >\n <KeyboardArrowDown className=\"[--spark-icon-size:14px]\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n}\n\n// px floor: axe target-size flags it below 24px at small scalings because it overlays\n// the trigger; autocomplete.tsx ICON_BUTTON sits in flow and does not need one\nconst CLEAR_BUTTON =\n \"relative inline-flex size-6 min-h-[24px] min-w-[24px] shrink-0 items-center justify-center rounded-sm p-0 text-gray-light outline-hidden transition-colors hover:text-gray focus-visible:ring-2 focus-visible:ring-(--color-focus-root)! focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 before:absolute before:top-1/2 before:left-1/2 before:size-10 before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']\"\n\n// wraps SelectTrigger since Base UI ships no Select.Clear and the trigger is a\n// native <button> — the clear sits beside it, not nested inside it\nfunction SelectTriggerGroup({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"select-trigger-group\"\n className={cn(\n 'relative inline-flex w-fit items-center [&_[data-slot=select-chevron]]:absolute [&_[data-slot=select-chevron]]:right-3 has-[[data-slot=select-trigger][data-disabled]]:[&_[data-slot=select-clear]]:hidden has-[[data-slot=select-trigger][data-placeholder]]:[&_[data-slot=select-clear]]:hidden has-[[data-slot=select-trigger][data-readonly]]:[&_[data-slot=select-clear]]:hidden [&_[data-slot=select-trigger]]:pr-[calc(--spacing(8)+max(--spacing(6),24px))]',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectClear({\n className,\n children,\n onClick,\n ...props\n}: React.ComponentProps<'button'>) {\n return (\n <button\n data-slot=\"select-clear\"\n type=\"button\"\n className={cn(CLEAR_BUTTON, 'absolute right-8', className)}\n onClick={(event) => {\n onClick?.(event)\n // clearing hides this button (data-placeholder returns), so keyboard\n // focus would otherwise drop to <body> — return it to the trigger\n event.currentTarget\n .closest('[data-slot=\"select-trigger-group\"]')\n ?.querySelector<HTMLElement>('[data-slot=\"select-trigger\"]')\n ?.focus()\n }}\n {...props}\n >\n {children ?? <Close aria-hidden className=\"[--spark-icon-size:14px]\" />}\n </button>\n )\n}\n\ntype SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Popup> &\n Pick<\n SelectPrimitive.Positioner.Props,\n 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'\n > &\n Pick<SelectPrimitive.Portal.Props, 'container'>\n\nfunction SelectContent({\n className,\n children,\n align = 'start',\n alignOffset,\n side,\n sideOffset = 4,\n // drop the menu below the trigger (left-aligned) like a conventional form\n // dropdown; the native-<select>-style overlay (Base UI's inherited default)\n // shifts the popup to align item text with trigger text, exposing a sliver of\n // the trigger's fill at the edge. Opt back in per instance if you want it.\n alignItemWithTrigger = false,\n container,\n ...props\n}: SelectContentProps) {\n return (\n <SelectPrimitive.Portal container={container}>\n <SelectPrimitive.Positioner\n align={align}\n alignItemWithTrigger={alignItemWithTrigger}\n alignOffset={alignOffset}\n side={side}\n sideOffset={sideOffset}\n >\n <SelectPrimitive.Popup\n data-slot=\"select-content\"\n data-align-trigger={alignItemWithTrigger}\n className={cn(\n // match the trigger's width (never narrower) so the drop-below menu\n // lines up under it; --anchor-width is the trigger width, floored at\n // 9rem for very narrow triggers\n 'relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg dark:bg-gray-3 [[data-high-contrast]_&]:border-gray-a11',\n 'transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n 'data-[align-trigger=true]:transition-none',\n className,\n )}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.List>{children}</SelectPrimitive.List>\n <SelectScrollDownButton />\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.GroupLabel>) {\n return (\n <SelectPrimitive.GroupLabel\n data-slot=\"select-label\"\n className={cn(\n 'px-1.5 py-1 text-xs text-gray-light [[data-high-contrast]_&]:text-gray-a12',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n 'relative flex w-full cursor-default items-center gap-1.5 rounded-sm py-1.5 pr-8 pl-1.5 text-sm text-gray outline-hidden transition-colors select-none touch-target-row data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-gray-a3 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',\n className,\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"[--spark-icon-size:11px]\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\n// plain div, not Select.Separator: role=\"separator\" isn't a permitted child of a\n// listbox (aria-required-children), and the divider is decorative — the groups\n// carry the semantics. Hidden from assistive tech.\nfunction SelectSeparator({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n aria-hidden=\"true\"\n data-slot=\"select-separator\"\n className={cn(\n 'pointer-events-none -mx-1 my-1 h-px bg-gray-a3 [[data-high-contrast]_&]:bg-gray-a6',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {\n return (\n <SelectPrimitive.ScrollUpArrow\n data-slot=\"select-scroll-up-button\"\n className={cn(\n 'top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 dark:bg-gray-3 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <KeyboardArrowDown className=\"rotate-180 [--spark-icon-size:14px]\" />\n </SelectPrimitive.ScrollUpArrow>\n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {\n return (\n <SelectPrimitive.ScrollDownArrow\n data-slot=\"select-scroll-down-button\"\n className={cn(\n 'bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 dark:bg-gray-3 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <KeyboardArrowDown className=\"[--spark-icon-size:14px]\" />\n </SelectPrimitive.ScrollDownArrow>\n )\n}\n\nexport {\n Select,\n SelectClear,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectTriggerGroup,\n SelectValue,\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  "use client";
3
3
  import { cn } from "../utils/cn.js";
4
- import { boxChrome } from "../lib/input-box-variants.js";
5
4
  import { useFieldControl } from "./field.js";
5
+ import { boxChrome } from "../lib/input-box-variants.js";
6
6
  import "react";
7
7
  import { Check, Close, KeyboardArrowDown } from "@epilot/spark-icons";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -85,7 +85,7 @@ function SelectContent({ className, children, align = "start", alignOffset, side
85
85
  children: /* @__PURE__ */ jsxs(Select$1.Popup, {
86
86
  "data-slot": "select-content",
87
87
  "data-align-trigger": alignItemWithTrigger,
88
- className: cn("relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg [[data-high-contrast]_&]:border-gray-a11", "transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none", "data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2", "data-[align-trigger=true]:transition-none", className),
88
+ className: cn("relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg dark:bg-gray-3 [[data-high-contrast]_&]:border-gray-a11", "transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none", "data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2", "data-[align-trigger=true]:transition-none", className),
89
89
  ...props,
90
90
  children: [
91
91
  /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
@@ -125,7 +125,7 @@ function SelectSeparator({ className, ...props }) {
125
125
  function SelectScrollUpButton({ className, ...props }) {
126
126
  return /* @__PURE__ */ jsx(Select$1.ScrollUpArrow, {
127
127
  "data-slot": "select-scroll-up-button",
128
- className: cn("top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4", className),
128
+ className: cn("top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 dark:bg-gray-3 [&_svg:not([class*=size-])]:size-4", className),
129
129
  ...props,
130
130
  children: /* @__PURE__ */ jsx(KeyboardArrowDown, { className: "rotate-180 [--spark-icon-size:14px]" })
131
131
  });
@@ -133,7 +133,7 @@ function SelectScrollUpButton({ className, ...props }) {
133
133
  function SelectScrollDownButton({ className, ...props }) {
134
134
  return /* @__PURE__ */ jsx(Select$1.ScrollDownArrow, {
135
135
  "data-slot": "select-scroll-down-button",
136
- className: cn("bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4", className),
136
+ className: cn("bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 dark:bg-gray-3 [&_svg:not([class*=size-])]:size-4", className),
137
137
  ...props,
138
138
  children: /* @__PURE__ */ jsx(KeyboardArrowDown, { className: "[--spark-icon-size:14px]" })
139
139
  });