@motir/design-system 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +126 -0
  2. package/dist/appearance.d.ts +47 -0
  3. package/dist/appearance.js +3 -0
  4. package/dist/appearance.js.map +1 -0
  5. package/dist/components/theme/AppearancePickers.d.ts +97 -0
  6. package/dist/components/theme/AppearancePickers.js +542 -0
  7. package/dist/components/theme/AppearancePickers.js.map +1 -0
  8. package/dist/components/theme/HandDrawnFilter.d.ts +23 -0
  9. package/dist/components/theme/HandDrawnFilter.js +41 -0
  10. package/dist/components/theme/HandDrawnFilter.js.map +1 -0
  11. package/dist/components/theme/ImmersiveTilt.d.ts +3 -0
  12. package/dist/components/theme/ImmersiveTilt.js +113 -0
  13. package/dist/components/theme/ImmersiveTilt.js.map +1 -0
  14. package/dist/components/theme/StyleVignette.d.ts +60 -0
  15. package/dist/components/theme/StyleVignette.js +382 -0
  16. package/dist/components/theme/StyleVignette.js.map +1 -0
  17. package/dist/components/ui/Button.d.ts +32 -0
  18. package/dist/components/ui/Button.js +92 -0
  19. package/dist/components/ui/Button.js.map +1 -0
  20. package/dist/components/ui/Card.d.ts +33 -0
  21. package/dist/components/ui/Card.js +64 -0
  22. package/dist/components/ui/Card.js.map +1 -0
  23. package/dist/components/ui/ColorSwatchPicker.d.ts +19 -0
  24. package/dist/components/ui/ColorSwatchPicker.js +71 -0
  25. package/dist/components/ui/ColorSwatchPicker.js.map +1 -0
  26. package/dist/components/ui/Combobox.d.ts +84 -0
  27. package/dist/components/ui/Combobox.js +374 -0
  28. package/dist/components/ui/Combobox.js.map +1 -0
  29. package/dist/components/ui/EmptyState.d.ts +38 -0
  30. package/dist/components/ui/EmptyState.js +73 -0
  31. package/dist/components/ui/EmptyState.js.map +1 -0
  32. package/dist/components/ui/ErrorState.d.ts +43 -0
  33. package/dist/components/ui/ErrorState.js +169 -0
  34. package/dist/components/ui/ErrorState.js.map +1 -0
  35. package/dist/components/ui/FormField.d.ts +31 -0
  36. package/dist/components/ui/FormField.js +43 -0
  37. package/dist/components/ui/FormField.js.map +1 -0
  38. package/dist/components/ui/Input.d.ts +29 -0
  39. package/dist/components/ui/Input.js +105 -0
  40. package/dist/components/ui/Input.js.map +1 -0
  41. package/dist/components/ui/Modal.d.ts +84 -0
  42. package/dist/components/ui/Modal.js +153 -0
  43. package/dist/components/ui/Modal.js.map +1 -0
  44. package/dist/components/ui/MultiSelectPicker.d.ts +73 -0
  45. package/dist/components/ui/MultiSelectPicker.js +358 -0
  46. package/dist/components/ui/MultiSelectPicker.js.map +1 -0
  47. package/dist/components/ui/Pill.d.ts +49 -0
  48. package/dist/components/ui/Pill.js +107 -0
  49. package/dist/components/ui/Pill.js.map +1 -0
  50. package/dist/components/ui/Popover.d.ts +65 -0
  51. package/dist/components/ui/Popover.js +57 -0
  52. package/dist/components/ui/Popover.js.map +1 -0
  53. package/dist/components/ui/SectionLabel.d.ts +28 -0
  54. package/dist/components/ui/SectionLabel.js +27 -0
  55. package/dist/components/ui/SectionLabel.js.map +1 -0
  56. package/dist/components/ui/Segmented.d.ts +30 -0
  57. package/dist/components/ui/Segmented.js +65 -0
  58. package/dist/components/ui/Segmented.js.map +1 -0
  59. package/dist/components/ui/Spinner.d.ts +22 -0
  60. package/dist/components/ui/Spinner.js +39 -0
  61. package/dist/components/ui/Spinner.js.map +1 -0
  62. package/dist/components/ui/Switch.d.ts +34 -0
  63. package/dist/components/ui/Switch.js +50 -0
  64. package/dist/components/ui/Switch.js.map +1 -0
  65. package/dist/components/ui/Textarea.d.ts +19 -0
  66. package/dist/components/ui/Textarea.js +72 -0
  67. package/dist/components/ui/Textarea.js.map +1 -0
  68. package/dist/components/ui/Toast.d.ts +45 -0
  69. package/dist/components/ui/Toast.js +116 -0
  70. package/dist/components/ui/Toast.js.map +1 -0
  71. package/dist/components/ui/Tooltip.d.ts +25 -0
  72. package/dist/components/ui/Tooltip.js +43 -0
  73. package/dist/components/ui/Tooltip.js.map +1 -0
  74. package/dist/contexts/theme-context.d.ts +95 -0
  75. package/dist/contexts/theme-context.js +561 -0
  76. package/dist/contexts/theme-context.js.map +1 -0
  77. package/dist/index.d.ts +41 -0
  78. package/dist/index.js +67 -0
  79. package/dist/specimen/TokensSpecimen.d.ts +13 -0
  80. package/dist/specimen/TokensSpecimen.js +1090 -0
  81. package/dist/specimen/TokensSpecimen.js.map +1 -0
  82. package/dist/theme/appearance-resolution.d.ts +59 -0
  83. package/dist/theme/appearance-resolution.js +379 -0
  84. package/dist/theme/appearance-resolution.js.map +1 -0
  85. package/dist/theme/init-script.d.ts +48 -0
  86. package/dist/theme/init-script.js +391 -0
  87. package/dist/theme/init-script.js.map +1 -0
  88. package/dist/theme/palettes.d.ts +146 -0
  89. package/dist/theme/palettes.js +85 -0
  90. package/dist/theme/palettes.js.map +1 -0
  91. package/dist/theme/styles.d.ts +351 -0
  92. package/dist/theme/styles.js +264 -0
  93. package/dist/theme/styles.js.map +1 -0
  94. package/dist/theme/tilt.d.ts +41 -0
  95. package/dist/theme/tilt.js +18 -0
  96. package/dist/theme/tilt.js.map +1 -0
  97. package/dist/theme/types.d.ts +61 -0
  98. package/dist/theme/types.js +234 -0
  99. package/dist/theme/types.js.map +1 -0
  100. package/dist/theme/typography.d.ts +144 -0
  101. package/dist/theme/typography.js +57 -0
  102. package/dist/theme/typography.js.map +1 -0
  103. package/dist/utils/cn.d.ts +16 -0
  104. package/dist/utils/cn.js +11 -0
  105. package/dist/utils/cn.js.map +1 -0
  106. package/package.json +60 -0
  107. package/theme.css +3924 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/MultiSelectPicker.tsx"],"names":[],"mappings":"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","file":"MultiSelectPicker.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type ComponentType,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { Check, Plus, TriangleAlert, X } from 'lucide-react';\nimport { cn } from '../../utils/cn';\n\n// Run the position measurement before paint on the client; fall back to\n// useEffect during SSR (the listbox only ever opens client-side).\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\n// `false` during SSR / first hydration render, `true` on the client thereafter —\n// gates the createPortal call so document.body is guaranteed present (mirrors\n// Combobox's useMounted). Hydration-safe and avoids setState-in-effect.\nconst subscribeNoop = () => () => {};\nfunction useMounted() {\n return useSyncExternalStore(\n subscribeNoop,\n () => true,\n () => false,\n );\n}\n\n// MultiSelectPicker (Story 5.4 · Subtask 5.4.8) — the generic chip-input\n// primitive, per design/work-items/labels-components-watch.mock.html panel 1:\n// value chips (label · remove ×) + a type-to-filter input in an input-shaped\n// box, with an anchored `aria-multiselectable` listbox of OptionRow-vocabulary\n// rows (22px glyph slot · truncating label · trailing Check in --el-accent).\n// The ONE new primitive the story earns — labels, components, and the future\n// Epic-6 facet editors all compose it.\n//\n// PURE: options in, selection out — NO fetching inside. The caller owns the\n// option set (filtering / debounced autocomplete) via the controlled `query`;\n// `onCreate` adds the folksonomy create-row when the query matches nothing;\n// `cap` disables the input at the limit (chips stay removable); a value's\n// `tint` colours its chip + its option row's swatch dot (labels pass the\n// name-hash tint; components pass none and get the neutral default).\n//\n// Keyboard (complete, the mock's panel-1 legend): type filters · ↑↓ moves the\n// active row · Enter toggles it (multi-select: the menu does NOT close) ·\n// Backspace on an empty input removes the last chip · Esc closes and returns\n// focus to the input. `aria-activedescendant` tracks the active row, per the\n// shipped Combobox a11y bar.\n\n/** One of the six `--el-tint-*` pastel token names (chip + swatch colour). */\nexport type MultiSelectTint = 'peach' | 'rose' | 'mint' | 'lavender' | 'sky' | 'yellow';\n\nexport interface MultiSelectOption {\n id: string;\n label: string;\n /** Pastel tint for the chip + the option row's swatch dot; absent = neutral. */\n tint?: MultiSelectTint;\n /** Glyph for the option row's 22px slot and the chip (e.g. the component glyph). */\n glyph?: ComponentType<{ className?: string }>;\n}\n\nexport interface MultiSelectPickerProps {\n /** The selected values, rendered as chips. */\n values: MultiSelectOption[];\n /** The current option set — the caller filters/fetches; no fetching inside. */\n options: MultiSelectOption[];\n /** Toggle an option from the listbox (add when unselected, remove when selected). */\n onToggle: (option: MultiSelectOption) => void;\n /** Remove a chip (the × button / Backspace-on-empty path). */\n onRemove: (value: MultiSelectOption) => void;\n /**\n * Folksonomy create — when present, a query matching no option (and no\n * selected value) case-insensitively appends the create-row.\n */\n onCreate?: (name: string) => void;\n /** The controlled filter query (the caller debounces its fetch off this). */\n query: string;\n onQueryChange: (query: string) => void;\n /** Per-value cap — at the cap the input disables; chips stay removable. */\n cap?: number;\n /** Accessible name for the input + the listbox. */\n label: string;\n placeholder: string;\n /** Localized create-row text for the current query (e.g. \"Create 'perf-q3'\"). */\n createLabel?: (query: string) => string;\n /** Localized accessible name for a chip's remove button. */\n removeLabel: (label: string) => string;\n /** Listbox empty state (e.g. \"No components defined\"). */\n emptyText?: string;\n /** Quiet line under the box (cap notice, admin link) — the mock's field-hint. */\n hint?: ReactNode;\n /** Inline error (the typed 422), announced via role=\"alert\". */\n error?: string | null;\n /** Disables the whole control (a pending action). */\n disabled?: boolean;\n /** Extra classes for the chip-input BOX (the bordered element) — e.g. the\n * filter builder's dashed pending-row treatment (Subtask 6.1.4). */\n className?: string;\n}\n\n// Static class maps — Tailwind needs literal class strings per tint. The chip\n// background routes through the DEDICATED `--el-label-1..6` ramp (MOTIR-1274 ·\n// 1266.3): the picker's only `tint` consumer is the label chip (lib/labels/\n// labelTint.ts maps a name → one of these six in this exact order), so a palette\n// can tune label hues apart from roles/avatars. Each `--el-label-N` defaults to\n// its prior `--el-tint-*` value, so this is a zero-visual-change rename.\nconst TINT_CHIP: Record<MultiSelectTint, string> = {\n peach: 'bg-(--el-label-1)',\n rose: 'bg-(--el-label-2)',\n mint: 'bg-(--el-label-3)',\n lavender: 'bg-(--el-label-4)',\n sky: 'bg-(--el-label-5)',\n yellow: 'bg-(--el-label-6)',\n};\n\n/**\n * A value chip — exported so the rail cards' DISPLAY / read-only modes render\n * the exact same chip the picker edits (mock panels 0 / 2 / 3). Neutral by\n * default (the Pill-neutral colours); a `tint` puts the hue in the background\n * with `--el-text-strong` text (finding #35 AA). With `onRemove` the trailing\n * × renders (12px, accessible name via `removeLabel`).\n */\nexport function ValueChip({\n option,\n onRemove,\n removeLabel,\n disabled,\n}: {\n option: MultiSelectOption;\n onRemove?: (value: MultiSelectOption) => void;\n removeLabel?: (label: string) => string;\n disabled?: boolean;\n}) {\n const Glyph = option.glyph;\n return (\n <span\n className={cn(\n 'inline-flex max-w-full items-center gap-1.5 rounded-(--radius-badge) px-(--spacing-chip-x) py-(--spacing-chip-y) font-sans text-xs font-medium',\n option.tint\n ? cn(TINT_CHIP[option.tint], 'text-(--el-text-strong)')\n : 'bg-(--el-chip-bg) text-(--el-text-secondary) border border-(--el-chip-border)',\n )}\n >\n {Glyph ? <Glyph className=\"h-3 w-3 shrink-0 text-(--el-icon-muted)\" aria-hidden /> : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n {onRemove ? (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={() => onRemove(option)}\n aria-label={removeLabel ? removeLabel(option.label) : option.label}\n className={cn(\n '-mr-1 inline-flex items-center justify-center rounded-(--radius-badge) p-px focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:outline-none',\n option.tint\n ? 'text-(--el-text-strong) opacity-65 hover:bg-(--el-text-strong)/10 hover:opacity-100'\n : 'text-(--el-icon-muted) hover:bg-(--el-muted) hover:text-(--el-text)',\n )}\n >\n <X className=\"h-3 w-3\" aria-hidden />\n </button>\n ) : null}\n </span>\n );\n}\n\n/** A pulse-skeleton chip (the mock's panel-6 loading grammar, at chip size). */\nexport function ChipSkeleton({ className }: { className?: string }) {\n return (\n <span\n className={cn('h-[22px] animate-pulse rounded-(--radius-badge) bg-(--el-muted)', className)}\n aria-hidden\n />\n );\n}\n\nexport function MultiSelectPicker({\n values,\n options,\n onToggle,\n onRemove,\n onCreate,\n query,\n onQueryChange,\n cap,\n label,\n placeholder,\n createLabel,\n removeLabel,\n emptyText,\n hint,\n error,\n disabled,\n className,\n}: MultiSelectPickerProps) {\n const baseId = useId();\n const listId = `${baseId}-listbox`;\n const rootRef = useRef<HTMLDivElement>(null);\n const boxRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(0);\n // The listbox is PORTALED to <body> with viewport-anchored `position: fixed`\n // (MOTIR-1346) so it escapes the Advanced-filter popover's `overflow-hidden`\n // and its `overflow-y-auto` scroll body — an in-popover absolute panel is\n // trapped by those and gets cut off near the edge. Same mechanism as the\n // Combobox menu (the bug-inline-edit-clipped-when-table-short fix). Only render\n // the portal once mounted, since createPortal needs document.body.\n const mounted = useMounted();\n // Portal everywhere EXCEPT inside a focus-trapping modal (the Modal primitive,\n // `data-surface=\"modal\"`), where a body-portaled option click fights the trap /\n // dismisses the modal — there we render the listbox INLINE. Same rule + marker\n // as the Combobox menu. Detected in a layout effect (not during render) so it's\n // set before paint with no ref-read-in-render.\n const [inModal, setInModal] = useState(false);\n const [menuStyle, setMenuStyle] = useState<CSSProperties | null>(null);\n const [listMaxHeight, setListMaxHeight] = useState(256);\n\n const atCap = cap != null && values.length >= cap;\n const inputDisabled = disabled || atCap;\n\n // The create-row appends when the trimmed query matches no option AND no\n // selected value case-insensitively (a case-insensitive match surfaces the\n // existing casing instead — mock panel 2).\n const trimmed = query.trim();\n const lower = trimmed.toLowerCase();\n const hasExactMatch =\n options.some((o) => o.label.toLowerCase() === lower) ||\n values.some((v) => v.label.toLowerCase() === lower);\n const showCreate = !!onCreate && trimmed.length > 0 && !hasExactMatch;\n\n // The navigable rows: options first, the create-row last.\n const rowCount = options.length + (showCreate ? 1 : 0);\n const clampedActive = rowCount === 0 ? 0 : Math.min(activeIndex, rowCount - 1);\n const activeId = open && rowCount > 0 ? `${baseId}-opt-${clampedActive}` : undefined;\n\n // Close on outside pointerdown (the picker stays open across toggles). The\n // listbox is portaled out of rootRef, so a click on it must count as \"inside\"\n // (else the option click closes the menu before it registers).\n useEffect(() => {\n if (!open) return;\n function onPointerDown(e: PointerEvent) {\n const t = e.target as Node;\n if (rootRef.current?.contains(t) || menuRef.current?.contains(t)) return;\n setOpen(false);\n }\n document.addEventListener('pointerdown', onPointerDown);\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [open]);\n\n // Anchor the portaled listbox to the box in VIEWPORT coordinates (position:\n // fixed), flipping above when there's more room there and capping the height to\n // the available space so it never runs off-screen. Recomputed on open / scroll\n // / resize. (Mirrors Combobox.updatePosition — the proven, E2E-stable mechanism:\n // because the menu lives at <body> and is fully visible, a click never has to\n // scroll a clipped container to reach an option, so it stays put.)\n const updatePosition = useCallback(() => {\n const box = boxRef.current;\n if (!box) return;\n const rect = box.getBoundingClientRect();\n const gap = 4; // matches the old mt-1\n const viewportH = window.innerHeight;\n const spaceBelow = viewportH - rect.bottom - gap;\n const spaceAbove = rect.top - gap;\n const placeBelow = spaceBelow >= spaceAbove;\n const avail = Math.max(120, placeBelow ? spaceBelow : spaceAbove);\n setListMaxHeight(Math.max(80, Math.min(256, avail - 8)));\n const style: CSSProperties = {\n position: 'fixed',\n left: Math.round(rect.left),\n minWidth: Math.round(rect.width),\n };\n if (placeBelow) style.top = Math.round(rect.bottom + gap);\n else style.bottom = Math.round(viewportH - rect.top + gap);\n setMenuStyle(style);\n }, []);\n\n // Detect (before paint) whether we sit inside a focus-trapping modal, to pick\n // the inline vs portal branch. Read in an effect, not during render.\n useIsomorphicLayoutEffect(() => {\n if (!open) return;\n setInModal(!!boxRef.current?.closest('[data-surface=\"modal\"],[aria-modal=\"true\"]'));\n }, [open]);\n\n // Position before paint, and keep it glued to the box while open (capture-phase\n // scroll so a scrolling dialog body re-anchors it).\n useIsomorphicLayoutEffect(() => {\n if (!(open && mounted) || inModal) return; // inline (modal) branch positions via CSS\n updatePosition();\n const onReflow = () => updatePosition();\n window.addEventListener('scroll', onReflow, true);\n window.addEventListener('resize', onReflow);\n return () => {\n window.removeEventListener('scroll', onReflow, true);\n window.removeEventListener('resize', onReflow);\n };\n }, [open, mounted, inModal, updatePosition, rowCount]);\n\n // The caller owns the query across a create: a rejected name stays in the\n // input for correction (mock panel 2); the card clears it on success.\n function commitRow(index: number) {\n const option = options[index];\n if (option) onToggle(option);\n else if (showCreate) onCreate?.(trimmed);\n }\n\n function onKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n if (e.key === 'Escape') {\n if (open) {\n // Consume the Esc that closes OUR listbox (stopPropagation included):\n // inside an enclosing dismissable layer (the 6.1.4 filter builder's\n // Radix Popover) the same keydown would otherwise also close the\n // whole dialog. With the listbox already closed, Esc passes through\n // to the container — the layered-dismiss order users expect.\n e.preventDefault();\n e.stopPropagation();\n setOpen(false);\n }\n return;\n }\n if (e.key === 'Backspace' && query.length === 0) {\n const last = values[values.length - 1];\n if (last) onRemove(last);\n return;\n }\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n e.preventDefault();\n if (!open) {\n setOpen(true);\n return;\n }\n if (rowCount === 0) return;\n const delta = e.key === 'ArrowDown' ? 1 : -1;\n setActiveIndex((clampedActive + delta + rowCount) % rowCount);\n return;\n }\n if (e.key === 'Enter') {\n e.preventDefault();\n if (open && rowCount > 0) commitRow(clampedActive);\n }\n }\n\n // Inside a focus-trapping modal: render the listbox INLINE (in rootRef's\n // subtree). Otherwise: portal it to <body> so it escapes clipping ancestors.\n const wrapMenu = (el: ReactNode) => (inModal ? el : createPortal(el, document.body));\n\n return (\n // `data-inner-dismiss` (set while the listbox is open) lets an enclosing\n // Radix layer's `onEscapeKeyDown` defer to us: Radix listens for Escape\n // in the CAPTURE phase at the document, so our own bubble-phase handler\n // alone can't stop it from dismissing the whole popover/dialog first\n // (the 6.1.4 filter builder hit exactly this).\n <div ref={rootRef} data-inner-dismiss={open ? true : undefined} className=\"relative font-sans\">\n {/* The box — input-shaped (mock panel 1): chips + the filter input. A\n click anywhere focuses the input; at the cap the box goes quiet but\n chips stay removable. */}\n <div\n ref={boxRef}\n onClick={() => inputRef.current?.focus()}\n className={cn(\n 'flex min-h-(--height-input) cursor-text flex-wrap items-center gap-1.5 rounded-(--radius-input) border border-(--el-border) bg-(--el-page-bg) px-(--spacing-control-x) py-(--spacing-control-y)',\n 'focus-within:ring-2 focus-within:ring-(--focus-ring-color) focus-within:ring-offset-1',\n inputDisabled && 'cursor-not-allowed bg-(--el-surface-soft)',\n className,\n )}\n >\n {values.map((v) => (\n <ValueChip\n key={v.id}\n option={v}\n onRemove={onRemove}\n removeLabel={removeLabel}\n disabled={disabled}\n />\n ))}\n <input\n ref={inputRef}\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={open ? listId : undefined}\n aria-activedescendant={activeId}\n aria-autocomplete=\"list\"\n aria-label={label}\n disabled={inputDisabled}\n value={query}\n placeholder={values.length === 0 ? placeholder : undefined}\n onChange={(e) => {\n onQueryChange(e.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onFocus={() => setOpen(true)}\n onKeyDown={onKeyDown}\n className=\"min-w-[60px] flex-1 border-0 bg-transparent py-0.5 text-sm text-(--el-text) outline-none placeholder:text-(--el-text-muted) disabled:cursor-not-allowed\"\n />\n </div>\n\n {open && mounted\n ? wrapMenu(\n <div\n ref={menuRef}\n id={listId}\n role=\"listbox\"\n aria-multiselectable=\"true\"\n aria-label={label}\n data-menu-surface=\"\"\n // Portal branch: viewport-anchored `position: fixed` (hidden until\n // positioned to avoid a first-paint flash at 0,0). Inline (modal)\n // branch: absolute, glued below the box, capped at max-h-64.\n style={\n inModal\n ? { maxHeight: listMaxHeight }\n : {\n ...(menuStyle ?? { position: 'fixed', visibility: 'hidden' }),\n maxHeight: listMaxHeight,\n }\n }\n className={cn(\n 'w-max max-w-72 min-w-60 overflow-y-auto rounded-(--radius-card) border border-(--el-border) bg-(--el-page-bg) p-1 shadow-(--shadow-elevated)',\n inModal ? 'absolute top-full left-0 z-10 mt-1 max-h-64' : 'z-50',\n )}\n >\n {options.map((opt, i) => {\n const selected = values.some((v) => v.id === opt.id);\n const Glyph = opt.glyph;\n return (\n <button\n key={opt.id}\n id={`${baseId}-opt-${i}`}\n type=\"button\"\n role=\"option\"\n aria-selected={selected}\n disabled={disabled}\n // Keep focus in the input across a toggle (the menu stays open).\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => commitRow(i)}\n onMouseEnter={() => setActiveIndex(i)}\n className={cn(\n 'flex w-full items-center gap-2.5 rounded-(--radius-control) px-(--spacing-control-x) py-(--spacing-control-y) text-left text-sm text-(--el-text) hover:bg-(--el-option-active-bg) focus-visible:outline-none',\n i === clampedActive && 'bg-(--el-option-active-bg)',\n )}\n >\n <span className=\"flex w-[22px] shrink-0 items-center justify-center text-(--el-icon-muted)\">\n {opt.tint ? (\n <span\n aria-hidden\n className={cn(\n 'h-2.5 w-2.5 rounded-full border border-(--el-border)',\n TINT_CHIP[opt.tint],\n )}\n />\n ) : Glyph ? (\n <Glyph className=\"h-3.5 w-3.5\" aria-hidden />\n ) : null}\n </span>\n <span className=\"min-w-0 flex-1 truncate\">{opt.label}</span>\n <Check\n aria-hidden\n className={cn(\n 'h-4 w-4 shrink-0 text-(--el-accent-on-surface)',\n selected ? 'opacity-100' : 'opacity-0',\n )}\n />\n </button>\n );\n })}\n\n {showCreate ? (\n <button\n id={`${baseId}-opt-${options.length}`}\n type=\"button\"\n role=\"option\"\n aria-selected={false}\n disabled={disabled}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => commitRow(options.length)}\n onMouseEnter={() => setActiveIndex(options.length)}\n className={cn(\n 'flex w-full items-center gap-2.5 rounded-(--radius-control) px-(--spacing-control-x) py-(--spacing-control-y) text-left text-sm text-(--el-text) hover:bg-(--el-option-active-bg) focus-visible:outline-none',\n clampedActive === options.length && 'bg-(--el-option-active-bg)',\n )}\n >\n <span className=\"flex w-[22px] shrink-0 items-center justify-center text-(--el-accent-on-surface)\">\n <Plus className=\"h-3.5 w-3.5\" aria-hidden />\n </span>\n <span className=\"min-w-0 flex-1 truncate\">\n {createLabel ? createLabel(trimmed) : trimmed}\n </span>\n </button>\n ) : null}\n\n {options.length === 0 && !showCreate ? (\n <div className=\"px-(--spacing-control-x) py-(--spacing-control-y) text-sm text-(--el-text-muted)\">\n {emptyText}\n </div>\n ) : null}\n </div>,\n )\n : null}\n\n {error ? (\n <p\n role=\"alert\"\n className=\"mt-1.5 flex items-center gap-1.5 font-sans text-xs text-(--el-danger)\"\n >\n <TriangleAlert className=\"h-3.5 w-3.5 shrink-0\" aria-hidden />\n {error}\n </p>\n ) : null}\n {!error && hint ? (\n <div className=\"mt-1.5 font-sans text-xs text-(--el-text-helper)\">{hint}</div>\n ) : null}\n </div>\n );\n}\n"]}
@@ -0,0 +1,49 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+ import * as class_variance_authority_types from 'class-variance-authority/types';
4
+ import { VariantProps } from 'class-variance-authority';
5
+
6
+ /**
7
+ * Pill — compact status or severity label. Pure span, no interaction.
8
+ *
9
+ * Variant axes (use exactly one):
10
+ * - `status`: planned | in-progress | done — Motir's Subtask lifecycle states.
11
+ * - `severity`: info | success | warning | danger — semantic UI states.
12
+ * - `priority`: highest | high | medium | low | lowest — work-item priority, a
13
+ * 5-step diverging ramp (`--el-priority-*` hue tinted over the surface, plus a
14
+ * redundant direction icon). The single source of truth is `PRIORITY_META`.
15
+ * - `memberRole`: admin | member | viewer — a project membership role
16
+ * (Story 6.4), one hued tint per role. (Named `memberRole`, not `role`, so
17
+ * it can't collide with the DOM `role` ARIA attribute on the span.)
18
+ * - `orgRole`: owner | admin | member — an ORGANIZATION role (Story 6.10),
19
+ * sitting above the workspace `memberRole`. owner → lavender (the highest,
20
+ * brand-purple tier), admin → sky, member → mint — per the org-admin design.
21
+ * - `tone`: neutral — a non-semantic chip (counts, metadata). Dark text on a
22
+ * neutral surface. `private` — the epic-level-privacy "Not public" badge
23
+ * (Story 6.14 · design epic-privacy panels 1/2/6b): lavender tint + charcoal
24
+ * text, the same AA-safe recipe as `status="planned"` / `memberRole="admin"`.
25
+ *
26
+ * All tones use adaptive dark/light text (`--color-charcoal`) on a hued tint,
27
+ * so every variant clears WCAG AA contrast in both light and dark modes
28
+ * (the hue lives in the background, not the text — PRODECT_FINDINGS #35).
29
+ *
30
+ * Always uses `--radius-badge` for fully rounded ends regardless of display style.
31
+ *
32
+ * @example
33
+ * <Pill status="in-progress">In progress</Pill>
34
+ * <Pill severity="danger">Validation failed</Pill>
35
+ * <Pill tone="neutral">3 members</Pill>
36
+ */
37
+ declare const pillVariants: (props?: ({
38
+ status?: "done" | "planned" | "in-progress" | null | undefined;
39
+ severity?: "danger" | "info" | "success" | "warning" | null | undefined;
40
+ priority?: "highest" | "high" | "medium" | "low" | "lowest" | null | undefined;
41
+ memberRole?: "admin" | "member" | "viewer" | null | undefined;
42
+ orgRole?: "admin" | "member" | "owner" | null | undefined;
43
+ tone?: "neutral" | "private" | "archived" | null | undefined;
44
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
45
+ interface PillProps extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof pillVariants> {
46
+ }
47
+ declare const Pill: react.ForwardRefExoticComponent<PillProps & react.RefAttributes<HTMLSpanElement>>;
48
+
49
+ export { Pill, type PillProps };
@@ -0,0 +1,107 @@
1
+ import { forwardRef } from 'react';
2
+ import { cva } from 'class-variance-authority';
3
+ import { clsx } from 'clsx';
4
+ import { twMerge } from 'tailwind-merge';
5
+ import { jsx } from 'react/jsx-runtime';
6
+
7
+ // src/components/ui/Pill.tsx
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+ var pillVariants = cva(
12
+ cn(
13
+ "inline-flex items-center gap-1",
14
+ "rounded-(--radius-badge)",
15
+ "px-(--spacing-chip-x) py-(--spacing-chip-y)",
16
+ "font-sans text-xs font-medium",
17
+ "border"
18
+ ),
19
+ {
20
+ variants: {
21
+ // All colored tones carry the semantic hue in the TINT BACKGROUND and use
22
+ // adaptive dark/light TEXT (`--color-charcoal`: #37352f light, #e5e5e5
23
+ // dark) — the same pattern `planned` always used. The previous saturated
24
+ // hue-on-tint TEXT failed WCAG AA (≈2.5–3.8:1 light, 3.2–4.0:1 dark);
25
+ // charcoal-on-tint clears ~10:1 in both modes (PRODECT_FINDINGS #35).
26
+ status: {
27
+ planned: "bg-(--el-tint-lavender) text-(--el-text-strong) border-transparent",
28
+ "in-progress": "bg-(--el-tint-sky) text-(--el-text-strong) border-transparent",
29
+ done: "bg-(--el-tint-mint) text-(--el-text-strong) border-transparent"
30
+ },
31
+ severity: {
32
+ info: "bg-(--el-tint-sky) text-(--el-text-strong) border-transparent",
33
+ success: "bg-(--el-tint-mint) text-(--el-text-strong) border-transparent",
34
+ warning: "bg-(--el-tint-peach) text-(--el-text-strong) border-transparent",
35
+ danger: "bg-(--el-tint-rose) text-(--el-text-strong) border-transparent"
36
+ },
37
+ // Priority chips (MOTIR-1273 · 1266.2) — a 5-step diverging ramp keyed off
38
+ // the `--el-priority-*` hues, un-collapsing `medium` and `lowest` (both
39
+ // were `tone="neutral"` grey). Each hue is diluted to a 14% tint over
40
+ // `--el-surface` so charcoal `--el-text-strong` clears AA in both themes
41
+ // (the hue lives in the background — finding #35); the redundant direction
42
+ // icon (PRIORITY_META) is the non-colour cue. `medium` (slate) and
43
+ // `lowest` (stone) resolve to two distinct greys in every palette.
44
+ priority: {
45
+ highest: "bg-[color-mix(in_srgb,var(--el-priority-highest)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent",
46
+ high: "bg-[color-mix(in_srgb,var(--el-priority-high)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent",
47
+ medium: "bg-[color-mix(in_srgb,var(--el-priority-medium)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent",
48
+ low: "bg-[color-mix(in_srgb,var(--el-priority-low)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent",
49
+ lowest: "bg-[color-mix(in_srgb,var(--el-priority-lowest)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent"
50
+ },
51
+ // Project membership roles (Story 6.4 · design/projects access-members):
52
+ // admin → lavender, member → sky, viewer → mint — the hue in the tint,
53
+ // charcoal text (AA-safe, finding #35), same recipe as `status`. Routed
54
+ // through the DEDICATED `--el-role-*` tokens (MOTIR-1274 · 1266.3) so a
55
+ // palette can tune roles apart from the other --el-tint-* meanings; each
56
+ // defaults to its prior tint (zero visual change).
57
+ memberRole: {
58
+ admin: "bg-(--el-role-admin) text-(--el-text-strong) border-transparent",
59
+ member: "bg-(--el-role-member) text-(--el-text-strong) border-transparent",
60
+ viewer: "bg-(--el-role-viewer) text-(--el-text-strong) border-transparent"
61
+ },
62
+ // Organization roles (Story 6.10 · design/org-admin): owner → lavender
63
+ // (the highest, brand-purple tier), admin → sky, member → mint — the hue
64
+ // in the tint, charcoal text (AA-safe, finding #35), same recipe as above.
65
+ // Dedicated `--el-org-role-*` tokens (MOTIR-1274 · 1266.3); zero change.
66
+ orgRole: {
67
+ owner: "bg-(--el-org-role-owner) text-(--el-text-strong) border-transparent",
68
+ admin: "bg-(--el-org-role-admin) text-(--el-text-strong) border-transparent",
69
+ member: "bg-(--el-org-role-member) text-(--el-text-strong) border-transparent"
70
+ },
71
+ tone: {
72
+ neutral: "bg-(--el-chip-bg) text-(--el-text-secondary) border-(--el-chip-border)",
73
+ // Epic-level privacy "Not public" badge (Story 6.14 · design
74
+ // epic-privacy panels 1/2/6b) — lavender tint, charcoal text, the same
75
+ // AA-safe recipe as `status="planned"` / `memberRole="admin"` (≈9.7:1).
76
+ // Dedicated `--el-privacy-private` token (MOTIR-1274 · 1266.3); the
77
+ // matching `--el-privacy-public` is defined ahead of a "public" badge.
78
+ private: "bg-(--el-privacy-private) text-(--el-text-strong) border-transparent",
79
+ // Inactive "Archived" badge (MOTIR-1276 · 1266.5) — a QUIET muted fill
80
+ // with slate ink, not a hued tint: archived is an inactive state, not a
81
+ // severity. Dedicated `--el-archived-pill-*` tokens so a palette can tune
82
+ // the inactive badge apart from the neutral chip; AA-safe (slate on muted
83
+ // clears AA both themes).
84
+ archived: "bg-(--el-archived-pill-bg) text-(--el-archived-pill-text) border-transparent"
85
+ }
86
+ },
87
+ defaultVariants: {}
88
+ }
89
+ );
90
+ var Pill = forwardRef(function Pill2({ status, severity, priority, memberRole, orgRole, tone, className, children, ...rest }, ref) {
91
+ return /* @__PURE__ */ jsx(
92
+ "span",
93
+ {
94
+ ref,
95
+ className: cn(
96
+ pillVariants({ status, severity, priority, memberRole, orgRole, tone }),
97
+ className
98
+ ),
99
+ ...rest,
100
+ children
101
+ }
102
+ );
103
+ });
104
+
105
+ export { Pill };
106
+ //# sourceMappingURL=Pill.js.map
107
+ //# sourceMappingURL=Pill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Pill.tsx"],"names":["Pill"],"mappings":";;;;;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACmBA,IAAM,YAAA,GAAe,GAAA;AAAA,EACnB,EAAA;AAAA,IACE,gCAAA;AAAA,IACA,0BAAA;AAAA,IACA,6CAAA;AAAA,IACA,+BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMR,MAAA,EAAQ;AAAA,QACN,OAAA,EAAS,oEAAA;AAAA,QACT,aAAA,EAAe,+DAAA;AAAA,QACf,IAAA,EAAM;AAAA,OACR;AAAA,MACA,QAAA,EAAU;AAAA,QACR,IAAA,EAAM,+DAAA;AAAA,QACN,OAAA,EAAS,gEAAA;AAAA,QACT,OAAA,EAAS,iEAAA;AAAA,QACT,MAAA,EAAQ;AAAA,OACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA,QAAA,EAAU;AAAA,QACR,OAAA,EACE,qHAAA;AAAA,QACF,IAAA,EAAM,kHAAA;AAAA,QACN,MAAA,EACE,oHAAA;AAAA,QACF,GAAA,EAAK,iHAAA;AAAA,QACL,MAAA,EACE;AAAA,OACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,UAAA,EAAY;AAAA,QACV,KAAA,EAAO,iEAAA;AAAA,QACP,MAAA,EAAQ,kEAAA;AAAA,QACR,MAAA,EAAQ;AAAA,OACV;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,OAAA,EAAS;AAAA,QACP,KAAA,EAAO,qEAAA;AAAA,QACP,KAAA,EAAO,qEAAA;AAAA,QACP,MAAA,EAAQ;AAAA,OACV;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,wEAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMT,OAAA,EAAS,sEAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMT,QAAA,EAAU;AAAA;AACZ,KACF;AAAA,IACA,iBAAiB;AAAC;AAEtB,CAAA;AAKO,IAAM,OAAO,UAAA,CAAuC,SAASA,KAAAA,CAClE,EAAE,QAAQ,QAAA,EAAU,QAAA,EAAU,UAAA,EAAY,OAAA,EAAS,MAAM,SAAA,EAAW,QAAA,EAAU,GAAG,IAAA,IACjF,GAAA,EACA;AACA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACT,YAAA,CAAa,EAAE,MAAA,EAAQ,QAAA,EAAU,UAAU,UAAA,EAAY,OAAA,EAAS,MAAM,CAAA;AAAA,QACtE;AAAA,OACF;AAAA,MACC,GAAG,IAAA;AAAA,MAEH;AAAA;AAAA,GACH;AAEJ,CAAC","file":"Pill.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { forwardRef, type HTMLAttributes } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Pill — compact status or severity label. Pure span, no interaction.\n *\n * Variant axes (use exactly one):\n * - `status`: planned | in-progress | done — Motir's Subtask lifecycle states.\n * - `severity`: info | success | warning | danger — semantic UI states.\n * - `priority`: highest | high | medium | low | lowest — work-item priority, a\n * 5-step diverging ramp (`--el-priority-*` hue tinted over the surface, plus a\n * redundant direction icon). The single source of truth is `PRIORITY_META`.\n * - `memberRole`: admin | member | viewer — a project membership role\n * (Story 6.4), one hued tint per role. (Named `memberRole`, not `role`, so\n * it can't collide with the DOM `role` ARIA attribute on the span.)\n * - `orgRole`: owner | admin | member — an ORGANIZATION role (Story 6.10),\n * sitting above the workspace `memberRole`. owner → lavender (the highest,\n * brand-purple tier), admin → sky, member → mint — per the org-admin design.\n * - `tone`: neutral — a non-semantic chip (counts, metadata). Dark text on a\n * neutral surface. `private` — the epic-level-privacy \"Not public\" badge\n * (Story 6.14 · design epic-privacy panels 1/2/6b): lavender tint + charcoal\n * text, the same AA-safe recipe as `status=\"planned\"` / `memberRole=\"admin\"`.\n *\n * All tones use adaptive dark/light text (`--color-charcoal`) on a hued tint,\n * so every variant clears WCAG AA contrast in both light and dark modes\n * (the hue lives in the background, not the text — PRODECT_FINDINGS #35).\n *\n * Always uses `--radius-badge` for fully rounded ends regardless of display style.\n *\n * @example\n * <Pill status=\"in-progress\">In progress</Pill>\n * <Pill severity=\"danger\">Validation failed</Pill>\n * <Pill tone=\"neutral\">3 members</Pill>\n */\nconst pillVariants = cva(\n cn(\n 'inline-flex items-center gap-1',\n 'rounded-(--radius-badge)',\n 'px-(--spacing-chip-x) py-(--spacing-chip-y)',\n 'font-sans text-xs font-medium',\n 'border',\n ),\n {\n variants: {\n // All colored tones carry the semantic hue in the TINT BACKGROUND and use\n // adaptive dark/light TEXT (`--color-charcoal`: #37352f light, #e5e5e5\n // dark) — the same pattern `planned` always used. The previous saturated\n // hue-on-tint TEXT failed WCAG AA (≈2.5–3.8:1 light, 3.2–4.0:1 dark);\n // charcoal-on-tint clears ~10:1 in both modes (PRODECT_FINDINGS #35).\n status: {\n planned: 'bg-(--el-tint-lavender) text-(--el-text-strong) border-transparent',\n 'in-progress': 'bg-(--el-tint-sky) text-(--el-text-strong) border-transparent',\n done: 'bg-(--el-tint-mint) text-(--el-text-strong) border-transparent',\n },\n severity: {\n info: 'bg-(--el-tint-sky) text-(--el-text-strong) border-transparent',\n success: 'bg-(--el-tint-mint) text-(--el-text-strong) border-transparent',\n warning: 'bg-(--el-tint-peach) text-(--el-text-strong) border-transparent',\n danger: 'bg-(--el-tint-rose) text-(--el-text-strong) border-transparent',\n },\n // Priority chips (MOTIR-1273 · 1266.2) — a 5-step diverging ramp keyed off\n // the `--el-priority-*` hues, un-collapsing `medium` and `lowest` (both\n // were `tone=\"neutral\"` grey). Each hue is diluted to a 14% tint over\n // `--el-surface` so charcoal `--el-text-strong` clears AA in both themes\n // (the hue lives in the background — finding #35); the redundant direction\n // icon (PRIORITY_META) is the non-colour cue. `medium` (slate) and\n // `lowest` (stone) resolve to two distinct greys in every palette.\n priority: {\n highest:\n 'bg-[color-mix(in_srgb,var(--el-priority-highest)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n high: 'bg-[color-mix(in_srgb,var(--el-priority-high)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n medium:\n 'bg-[color-mix(in_srgb,var(--el-priority-medium)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n low: 'bg-[color-mix(in_srgb,var(--el-priority-low)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n lowest:\n 'bg-[color-mix(in_srgb,var(--el-priority-lowest)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent',\n },\n // Project membership roles (Story 6.4 · design/projects access-members):\n // admin → lavender, member → sky, viewer → mint — the hue in the tint,\n // charcoal text (AA-safe, finding #35), same recipe as `status`. Routed\n // through the DEDICATED `--el-role-*` tokens (MOTIR-1274 · 1266.3) so a\n // palette can tune roles apart from the other --el-tint-* meanings; each\n // defaults to its prior tint (zero visual change).\n memberRole: {\n admin: 'bg-(--el-role-admin) text-(--el-text-strong) border-transparent',\n member: 'bg-(--el-role-member) text-(--el-text-strong) border-transparent',\n viewer: 'bg-(--el-role-viewer) text-(--el-text-strong) border-transparent',\n },\n // Organization roles (Story 6.10 · design/org-admin): owner → lavender\n // (the highest, brand-purple tier), admin → sky, member → mint — the hue\n // in the tint, charcoal text (AA-safe, finding #35), same recipe as above.\n // Dedicated `--el-org-role-*` tokens (MOTIR-1274 · 1266.3); zero change.\n orgRole: {\n owner: 'bg-(--el-org-role-owner) text-(--el-text-strong) border-transparent',\n admin: 'bg-(--el-org-role-admin) text-(--el-text-strong) border-transparent',\n member: 'bg-(--el-org-role-member) text-(--el-text-strong) border-transparent',\n },\n tone: {\n neutral: 'bg-(--el-chip-bg) text-(--el-text-secondary) border-(--el-chip-border)',\n // Epic-level privacy \"Not public\" badge (Story 6.14 · design\n // epic-privacy panels 1/2/6b) — lavender tint, charcoal text, the same\n // AA-safe recipe as `status=\"planned\"` / `memberRole=\"admin\"` (≈9.7:1).\n // Dedicated `--el-privacy-private` token (MOTIR-1274 · 1266.3); the\n // matching `--el-privacy-public` is defined ahead of a \"public\" badge.\n private: 'bg-(--el-privacy-private) text-(--el-text-strong) border-transparent',\n // Inactive \"Archived\" badge (MOTIR-1276 · 1266.5) — a QUIET muted fill\n // with slate ink, not a hued tint: archived is an inactive state, not a\n // severity. Dedicated `--el-archived-pill-*` tokens so a palette can tune\n // the inactive badge apart from the neutral chip; AA-safe (slate on muted\n // clears AA both themes).\n archived: 'bg-(--el-archived-pill-bg) text-(--el-archived-pill-text) border-transparent',\n },\n },\n defaultVariants: {},\n },\n);\n\nexport interface PillProps\n extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof pillVariants> {}\n\nexport const Pill = forwardRef<HTMLSpanElement, PillProps>(function Pill(\n { status, severity, priority, memberRole, orgRole, tone, className, children, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n className={cn(\n pillVariants({ status, severity, priority, memberRole, orgRole, tone }),\n className,\n )}\n {...rest}\n >\n {children}\n </span>\n );\n});\n"]}
@@ -0,0 +1,65 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ComponentPropsWithoutRef } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import * as RadixPopover from '@radix-ui/react-popover';
5
+
6
+ /**
7
+ * Popover — anchored, click-outside-dismissable, focus-managed floating
8
+ * panel wrapping @radix-ui/react-popover.
9
+ *
10
+ * Same shape as Modal.tsx (Radix-wrapped, controlled open state). Unlike
11
+ * Modal, the content is anchored to a trigger rather than centered, and
12
+ * there is no overlay — clicking outside dismisses. Use it for menus and
13
+ * dropdowns where the panel holds free-form content (the workspace
14
+ * switcher's section header + membership rows, the user menu).
15
+ *
16
+ * Portal + border + shadow match Modal so the two primitives feel
17
+ * consistent. No new tokens — reuses --radius-card, --shadow-elevated,
18
+ * --color-hairline.
19
+ *
20
+ * @example
21
+ * <Popover open={open} onOpenChange={setOpen}>
22
+ * <Popover.Trigger asChild>
23
+ * <Button variant="ghost" rightIcon={<ChevronDown />}>Menu</Button>
24
+ * </Popover.Trigger>
25
+ * <Popover.Content align="start">{items}</Popover.Content>
26
+ * </Popover>
27
+ */
28
+ interface PopoverProps {
29
+ open?: boolean;
30
+ onOpenChange?: (open: boolean) => void;
31
+ modal?: boolean;
32
+ children: ReactNode;
33
+ }
34
+ declare function PopoverRoot({ open, onOpenChange, modal, children }: PopoverProps): react_jsx_runtime.JSX.Element;
35
+ interface PopoverContentProps extends ComponentPropsWithoutRef<typeof RadixPopover.Content> {
36
+ /** Panel width; defaults to the 320px the switcher mockup pins. */
37
+ width?: number | string;
38
+ /**
39
+ * Drop the panel's `overflow-hidden` so a child anchored dropdown — e.g. a
40
+ * `Combobox` listbox — can render past the panel's edge instead of being
41
+ * clipped by it. Set this whenever the popover hosts a `Combobox` (or any
42
+ * other absolutely-positioned floating child) whose menu can grow taller
43
+ * than the popover's content-sized height — a real-product Jira-style
44
+ * dropdown-inside-popover (mark-duplicate target, promote sprint picker)
45
+ * always escapes its anchor (`bug-combobox-menu-clipped-inside-popover`).
46
+ *
47
+ * Safe because the popover's own padding (`p-2`, `p-3`) keeps children off
48
+ * the rounded corners, so the corners stay clean without overflow clipping;
49
+ * and the Combobox menu stays in the popover's DOM subtree so non-modal
50
+ * Radix Popover does not treat its clicks as outside-dismissals.
51
+ *
52
+ * Leave unset for popovers that hold only static rows (the workspace
53
+ * switcher, the user menu) — the default `overflow-hidden` is fine and
54
+ * cheaper to reason about.
55
+ */
56
+ overflowVisible?: boolean;
57
+ }
58
+ declare const Popover: typeof PopoverRoot & {
59
+ Trigger: react.ForwardRefExoticComponent<RadixPopover.PopoverTriggerProps & react.RefAttributes<HTMLButtonElement>>;
60
+ Content: react.ForwardRefExoticComponent<PopoverContentProps & react.RefAttributes<HTMLDivElement>>;
61
+ Close: react.ForwardRefExoticComponent<RadixPopover.PopoverCloseProps & react.RefAttributes<HTMLButtonElement>>;
62
+ Anchor: react.ForwardRefExoticComponent<RadixPopover.PopoverAnchorProps & react.RefAttributes<HTMLDivElement>>;
63
+ };
64
+
65
+ export { Popover, type PopoverContentProps, type PopoverProps };
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+ import { forwardRef } from 'react';
3
+ import * as RadixPopover from '@radix-ui/react-popover';
4
+ import { clsx } from 'clsx';
5
+ import { twMerge } from 'tailwind-merge';
6
+ import { jsx } from 'react/jsx-runtime';
7
+
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+ function PopoverRoot({ open, onOpenChange, modal, children }) {
12
+ return /* @__PURE__ */ jsx(RadixPopover.Root, { open, onOpenChange, modal, children });
13
+ }
14
+ var PopoverContent = forwardRef(function PopoverContent2({
15
+ className,
16
+ align = "start",
17
+ sideOffset = 8,
18
+ width = 320,
19
+ style,
20
+ children,
21
+ overflowVisible = false,
22
+ ...rest
23
+ }, ref) {
24
+ return /* @__PURE__ */ jsx(RadixPopover.Portal, { children: /* @__PURE__ */ jsx(
25
+ RadixPopover.Content,
26
+ {
27
+ ref,
28
+ align,
29
+ sideOffset,
30
+ "data-surface": "popover",
31
+ className: cn(
32
+ "z-50 rounded-(--radius-card) bg-(--el-page-bg)",
33
+ overflowVisible ? "overflow-visible" : "overflow-hidden",
34
+ "shadow-(--shadow-elevated) border border-(--el-border)",
35
+ "focus:outline-none",
36
+ "data-[state=open]:animate-in data-[state=closed]:animate-out fade-in-0 fade-out-0",
37
+ className
38
+ ),
39
+ style: { width: typeof width === "number" ? `${width}px` : width, ...style },
40
+ ...rest,
41
+ children
42
+ }
43
+ ) });
44
+ });
45
+ var PopoverTrigger = RadixPopover.Trigger;
46
+ var PopoverClose = RadixPopover.Close;
47
+ var PopoverAnchor = RadixPopover.Anchor;
48
+ var Popover = Object.assign(PopoverRoot, {
49
+ Trigger: PopoverTrigger,
50
+ Content: PopoverContent,
51
+ Close: PopoverClose,
52
+ Anchor: PopoverAnchor
53
+ });
54
+
55
+ export { Popover };
56
+ //# sourceMappingURL=Popover.js.map
57
+ //# sourceMappingURL=Popover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Popover.tsx"],"names":["PopoverContent"],"mappings":";;;;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACmBA,SAAS,YAAY,EAAE,IAAA,EAAM,YAAA,EAAc,KAAA,EAAO,UAAS,EAAiB;AAC1E,EAAA,2BACgB,YAAA,CAAA,IAAA,EAAb,EAAkB,IAAA,EAAY,YAAA,EAA4B,OACxD,QAAA,EACH,CAAA;AAEJ;AA0BA,IAAM,cAAA,GAAiB,UAAA,CAGrB,SAASA,eAAAA,CACT;AAAA,EACE,SAAA;AAAA,EACA,KAAA,GAAQ,OAAA;AAAA,EACR,UAAA,GAAa,CAAA;AAAA,EACb,KAAA,GAAQ,GAAA;AAAA,EACR,KAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA,GAAkB,KAAA;AAAA,EAClB,GAAG;AACL,CAAA,EACA,GAAA,EACA;AACA,EAAA,uBACE,GAAA,CAAc,qBAAb,EACC,QAAA,kBAAA,GAAA;AAAA,IAAc,YAAA,CAAA,OAAA;AAAA,IAAb;AAAA,MACC,GAAA;AAAA,MACA,KAAA;AAAA,MACA,UAAA;AAAA,MAGA,cAAA,EAAa,SAAA;AAAA,MACb,SAAA,EAAW,EAAA;AAAA,QACT,gDAAA;AAAA,QACA,kBAAkB,kBAAA,GAAqB,iBAAA;AAAA,QACvC,wDAAA;AAAA,QACA,oBAAA;AAAA,QACA,mFAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,KAAA,EAAO,EAAE,KAAA,EAAO,OAAO,KAAA,KAAU,QAAA,GAAW,CAAA,EAAG,KAAK,CAAA,EAAA,CAAA,GAAO,KAAA,EAAO,GAAG,KAAA,EAAM;AAAA,MAC1E,GAAG,IAAA;AAAA,MAEH;AAAA;AAAA,GACH,EACF,CAAA;AAEJ,CAAC,CAAA;AAED,IAAM,cAAA,GAA8B,YAAA,CAAA,OAAA;AACpC,IAAM,YAAA,GAA4B,YAAA,CAAA,KAAA;AAClC,IAAM,aAAA,GAA6B,YAAA,CAAA,MAAA;AAE5B,IAAM,OAAA,GAAU,MAAA,CAAO,MAAA,CAAO,WAAA,EAAa;AAAA,EAChD,OAAA,EAAS,cAAA;AAAA,EACT,OAAA,EAAS,cAAA;AAAA,EACT,KAAA,EAAO,YAAA;AAAA,EACP,MAAA,EAAQ;AACV,CAAC","file":"Popover.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\nimport { forwardRef, type ComponentPropsWithoutRef, type ReactNode } from 'react';\nimport * as RadixPopover from '@radix-ui/react-popover';\nimport { cn } from '../../utils/cn';\n\n/**\n * Popover — anchored, click-outside-dismissable, focus-managed floating\n * panel wrapping @radix-ui/react-popover.\n *\n * Same shape as Modal.tsx (Radix-wrapped, controlled open state). Unlike\n * Modal, the content is anchored to a trigger rather than centered, and\n * there is no overlay — clicking outside dismisses. Use it for menus and\n * dropdowns where the panel holds free-form content (the workspace\n * switcher's section header + membership rows, the user menu).\n *\n * Portal + border + shadow match Modal so the two primitives feel\n * consistent. No new tokens — reuses --radius-card, --shadow-elevated,\n * --color-hairline.\n *\n * @example\n * <Popover open={open} onOpenChange={setOpen}>\n * <Popover.Trigger asChild>\n * <Button variant=\"ghost\" rightIcon={<ChevronDown />}>Menu</Button>\n * </Popover.Trigger>\n * <Popover.Content align=\"start\">{items}</Popover.Content>\n * </Popover>\n */\nexport interface PopoverProps {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n modal?: boolean;\n children: ReactNode;\n}\n\nfunction PopoverRoot({ open, onOpenChange, modal, children }: PopoverProps) {\n return (\n <RadixPopover.Root open={open} onOpenChange={onOpenChange} modal={modal}>\n {children}\n </RadixPopover.Root>\n );\n}\n\nexport interface PopoverContentProps extends ComponentPropsWithoutRef<typeof RadixPopover.Content> {\n /** Panel width; defaults to the 320px the switcher mockup pins. */\n width?: number | string;\n /**\n * Drop the panel's `overflow-hidden` so a child anchored dropdown — e.g. a\n * `Combobox` listbox — can render past the panel's edge instead of being\n * clipped by it. Set this whenever the popover hosts a `Combobox` (or any\n * other absolutely-positioned floating child) whose menu can grow taller\n * than the popover's content-sized height — a real-product Jira-style\n * dropdown-inside-popover (mark-duplicate target, promote sprint picker)\n * always escapes its anchor (`bug-combobox-menu-clipped-inside-popover`).\n *\n * Safe because the popover's own padding (`p-2`, `p-3`) keeps children off\n * the rounded corners, so the corners stay clean without overflow clipping;\n * and the Combobox menu stays in the popover's DOM subtree so non-modal\n * Radix Popover does not treat its clicks as outside-dismissals.\n *\n * Leave unset for popovers that hold only static rows (the workspace\n * switcher, the user menu) — the default `overflow-hidden` is fine and\n * cheaper to reason about.\n */\n overflowVisible?: boolean;\n}\n\nconst PopoverContent = forwardRef<\n React.ElementRef<typeof RadixPopover.Content>,\n PopoverContentProps\n>(function PopoverContent(\n {\n className,\n align = 'start',\n sideOffset = 8,\n width = 320,\n style,\n children,\n overflowVisible = false,\n ...rest\n },\n ref,\n) {\n return (\n <RadixPopover.Portal>\n <RadixPopover.Content\n ref={ref}\n align={align}\n sideOffset={sideOffset}\n // `data-surface` lets a surface-material style (glassmorphism) frost\n // the floating panel — see globals.css's material layer.\n data-surface=\"popover\"\n className={cn(\n 'z-50 rounded-(--radius-card) bg-(--el-page-bg)',\n overflowVisible ? 'overflow-visible' : 'overflow-hidden',\n 'shadow-(--shadow-elevated) border border-(--el-border)',\n 'focus:outline-none',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out fade-in-0 fade-out-0',\n className,\n )}\n style={{ width: typeof width === 'number' ? `${width}px` : width, ...style }}\n {...rest}\n >\n {children}\n </RadixPopover.Content>\n </RadixPopover.Portal>\n );\n});\n\nconst PopoverTrigger = RadixPopover.Trigger;\nconst PopoverClose = RadixPopover.Close;\nconst PopoverAnchor = RadixPopover.Anchor;\n\nexport const Popover = Object.assign(PopoverRoot, {\n Trigger: PopoverTrigger,\n Content: PopoverContent,\n Close: PopoverClose,\n Anchor: PopoverAnchor,\n});\n"]}
@@ -0,0 +1,28 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ /**
5
+ * SectionLabel — the small uppercase-mono header that captions a group of
6
+ * rows (a sidebar nav section, and later the cmd-K command groups).
7
+ *
8
+ * Lifted to its own primitive per PRODECT_FINDINGS #28 so the two consumers
9
+ * share ONE source of truth for the canonical typography (mono · 11px ·
10
+ * semibold · uppercase · 0.06em tracking · muted-foreground) instead of each
11
+ * memorizing four utility classes. `Sidebar` uses it for section labels now;
12
+ * 1.5.4's `CommandGroupHeader` composes the same primitive.
13
+ *
14
+ * Presentational only — renders a `<div>`. Pass `label` for the common
15
+ * string case, or `children` for richer content (e.g. a label + a count).
16
+ *
17
+ * @example
18
+ * <SectionLabel label="Workspace" />
19
+ * <SectionLabel>Results <span>{count}</span></SectionLabel>
20
+ */
21
+ interface SectionLabelProps extends HTMLAttributes<HTMLDivElement> {
22
+ /** Convenience for the string case; ignored when `children` is provided. */
23
+ label?: string;
24
+ children?: ReactNode;
25
+ }
26
+ declare const SectionLabel: react.ForwardRefExoticComponent<SectionLabelProps & react.RefAttributes<HTMLDivElement>>;
27
+
28
+ export { SectionLabel, type SectionLabelProps };
@@ -0,0 +1,27 @@
1
+ import { forwardRef } from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/components/ui/SectionLabel.tsx
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+ var SectionLabel = forwardRef(function SectionLabel2({ label, children, className, ...rest }, ref) {
11
+ return /* @__PURE__ */ jsx(
12
+ "div",
13
+ {
14
+ ref,
15
+ className: cn(
16
+ "font-mono text-[11px] font-semibold uppercase tracking-[0.06em] text-(--el-text-eyebrow)",
17
+ className
18
+ ),
19
+ ...rest,
20
+ children: children ?? label
21
+ }
22
+ );
23
+ });
24
+
25
+ export { SectionLabel };
26
+ //# sourceMappingURL=SectionLabel.js.map
27
+ //# sourceMappingURL=SectionLabel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/SectionLabel.tsx"],"names":["SectionLabel"],"mappings":";;;;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACUO,IAAM,YAAA,GAAe,UAAA,CAA8C,SAASA,aAAAA,CACjF,EAAE,KAAA,EAAO,QAAA,EAAU,SAAA,EAAW,GAAG,IAAA,EAAK,EACtC,GAAA,EACA;AACA,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACT,0FAAA;AAAA,QACA;AAAA,OACF;AAAA,MACC,GAAG,IAAA;AAAA,MAEH,QAAA,EAAA,QAAA,IAAY;AAAA;AAAA,GACf;AAEJ,CAAC","file":"SectionLabel.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';\nimport { cn } from '../../utils/cn';\n\n/**\n * SectionLabel — the small uppercase-mono header that captions a group of\n * rows (a sidebar nav section, and later the cmd-K command groups).\n *\n * Lifted to its own primitive per PRODECT_FINDINGS #28 so the two consumers\n * share ONE source of truth for the canonical typography (mono · 11px ·\n * semibold · uppercase · 0.06em tracking · muted-foreground) instead of each\n * memorizing four utility classes. `Sidebar` uses it for section labels now;\n * 1.5.4's `CommandGroupHeader` composes the same primitive.\n *\n * Presentational only — renders a `<div>`. Pass `label` for the common\n * string case, or `children` for richer content (e.g. a label + a count).\n *\n * @example\n * <SectionLabel label=\"Workspace\" />\n * <SectionLabel>Results <span>{count}</span></SectionLabel>\n */\nexport interface SectionLabelProps extends HTMLAttributes<HTMLDivElement> {\n /** Convenience for the string case; ignored when `children` is provided. */\n label?: string;\n children?: ReactNode;\n}\n\nexport const SectionLabel = forwardRef<HTMLDivElement, SectionLabelProps>(function SectionLabel(\n { label, children, className, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cn(\n 'font-mono text-[11px] font-semibold uppercase tracking-[0.06em] text-(--el-text-eyebrow)',\n className,\n )}\n {...rest}\n >\n {children ?? label}\n </div>\n );\n});\n"]}
@@ -0,0 +1,30 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface SegmentedOption<T extends string> {
5
+ value: T;
6
+ label: string;
7
+ /** Optional leading glyph (decorative — the label carries the accessible name). */
8
+ icon?: ReactNode;
9
+ /** Optional trailing content after the label (e.g. the notification drawer's
10
+ * unread count on the Direct tab, Subtask 5.7.5). Decorative — faint when the
11
+ * option is inactive, the `--el-accent` hue when active (mirrors the leading
12
+ * glyph's active treatment). */
13
+ trailing?: ReactNode;
14
+ /** Disable just this option — a forward-compatible seam (e.g. the Activity
15
+ * card's History filter, Story 5.5's slot) drawn present-but-inert. */
16
+ disabled?: boolean;
17
+ /** Tooltip for the option (e.g. why a seam option is disabled). */
18
+ title?: string;
19
+ }
20
+ declare function Segmented<T extends string>({ options, value, onChange, label, className, disabled, }: {
21
+ options: SegmentedOption<T>[];
22
+ value: T;
23
+ onChange: (value: T) => void;
24
+ /** Accessible group name (e.g. "Swimlane group by") — not rendered visually. */
25
+ label: string;
26
+ className?: string;
27
+ disabled?: boolean;
28
+ }): react_jsx_runtime.JSX.Element;
29
+
30
+ export { Segmented, type SegmentedOption };
@@ -0,0 +1,65 @@
1
+ 'use client';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ function cn(...inputs) {
7
+ return twMerge(clsx(inputs));
8
+ }
9
+ function Segmented({
10
+ options,
11
+ value,
12
+ onChange,
13
+ label,
14
+ className,
15
+ disabled
16
+ }) {
17
+ return /* @__PURE__ */ jsx(
18
+ "div",
19
+ {
20
+ role: "group",
21
+ "aria-label": label,
22
+ className: cn(
23
+ "inline-flex items-center gap-0.5 rounded-(--radius-btn) border border-(--el-border) bg-(--el-tabnav-track) p-0.5",
24
+ className
25
+ ),
26
+ children: options.map((opt) => {
27
+ const active = opt.value === value;
28
+ return /* @__PURE__ */ jsxs(
29
+ "button",
30
+ {
31
+ type: "button",
32
+ "aria-pressed": active,
33
+ disabled: disabled || opt.disabled,
34
+ title: opt.title,
35
+ onClick: () => {
36
+ if (!active) onChange(opt.value);
37
+ },
38
+ className: cn(
39
+ // The segment radius nests inside the track: the track is
40
+ // `rounded-(--radius-btn)` with a 2px (`p-0.5`) inset, so a segment
41
+ // fits the shell only at `--radius-btn - 2px`. Using a fixed
42
+ // `--radius-control` breaks when a style makes `--radius-btn` a full
43
+ // pill (soft-playful / retrofuturism: pill track, but a small-radius
44
+ // chip floating inside it). Mirrors AppearancePickers' option radius.
45
+ "inline-flex h-(--height-control) items-center gap-1.5 rounded-[calc(var(--radius-btn)-2px)] px-(--spacing-control-x) text-[13px] font-medium transition-colors",
46
+ "focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:outline-none",
47
+ "disabled:cursor-not-allowed disabled:opacity-50",
48
+ active ? "bg-(--el-page-bg) text-(--el-text-strong) shadow-(--shadow-subtle) [&_.seg-ic]:text-(--el-tabnav-active) [&_.seg-trail]:text-(--el-tabnav-active)" : "text-(--el-text-secondary) hover:text-(--el-text) [&_.seg-ic]:text-(--el-text-faint) [&_.seg-trail]:text-(--el-text-faint)"
49
+ ),
50
+ children: [
51
+ opt.icon ? /* @__PURE__ */ jsx("span", { className: "seg-ic inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center", children: opt.icon }) : null,
52
+ opt.label,
53
+ opt.trailing != null ? /* @__PURE__ */ jsx("span", { className: "seg-trail text-[11px] font-semibold tabular-nums", children: opt.trailing }) : null
54
+ ]
55
+ },
56
+ opt.value
57
+ );
58
+ })
59
+ }
60
+ );
61
+ }
62
+
63
+ export { Segmented };
64
+ //# sourceMappingURL=Segmented.js.map
65
+ //# sourceMappingURL=Segmented.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Segmented.tsx"],"names":[],"mappings":";;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACqBO,SAAS,SAAA,CAA4B;AAAA,EAC1C,OAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAQG;AACD,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,OAAA;AAAA,MACL,YAAA,EAAY,KAAA;AAAA,MACZ,SAAA,EAAW,EAAA;AAAA,QACT,kHAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEC,QAAA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAC,GAAA,KAAQ;AACpB,QAAA,MAAM,MAAA,GAAS,IAAI,KAAA,KAAU,KAAA;AAC7B,QAAA,uBACE,IAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAEC,IAAA,EAAK,QAAA;AAAA,YACL,cAAA,EAAc,MAAA;AAAA,YACd,QAAA,EAAU,YAAY,GAAA,CAAI,QAAA;AAAA,YAC1B,OAAO,GAAA,CAAI,KAAA;AAAA,YACX,SAAS,MAAM;AACb,cAAA,IAAI,CAAC,MAAA,EAAQ,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAAA,YACjC,CAAA;AAAA,YACA,SAAA,EAAW,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOT,gKAAA;AAAA,cACA,yFAAA;AAAA,cACA,iDAAA;AAAA,cACA,SACI,mJAAA,GACA;AAAA,aACN;AAAA,YAEC,QAAA,EAAA;AAAA,cAAA,GAAA,CAAI,uBACH,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,qEAAA,EACb,QAAA,EAAA,GAAA,CAAI,MACP,CAAA,GACE,IAAA;AAAA,cACH,GAAA,CAAI,KAAA;AAAA,cACJ,GAAA,CAAI,YAAY,IAAA,mBACf,GAAA,CAAC,UAAK,SAAA,EAAU,kDAAA,EACb,QAAA,EAAA,GAAA,CAAI,QAAA,EACP,CAAA,GACE;AAAA;AAAA,WAAA;AAAA,UAjCC,GAAA,CAAI;AAAA,SAkCX;AAAA,MAEJ,CAAC;AAAA;AAAA,GACH;AAEJ","file":"Segmented.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge Tailwind class names without conflicts.\n *\n * `clsx` handles conditional inclusion (booleans, undefineds, arrays);\n * `twMerge` resolves conflicting Tailwind classes (later wins, e.g.\n * `px-2 px-4` → `px-4`). Together they are the de facto standard for\n * composing Tailwind classes in React components.\n *\n * @example\n * cn('px-2 py-1', isActive && 'bg-primary', className)\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\nimport type { ReactNode } from 'react';\nimport { cn } from '../../utils/cn';\n\n// Segmented — a single-select inline control: a row of mutually-exclusive\n// buttons where the active option is raised (`aria-pressed`). The accessible,\n// space-cheap alternative to a `<select>` when the option set is small and worth\n// showing at a glance (the board group-by, Subtask 3.3.5; reusable beyond it).\n//\n// Built FROM the design system per `design/boards/swimlanes-wip.mock.html` (the\n// `.seg` block): an `--el-surface` track (`--radius-btn`) with a 2px inset, each\n// option a `calc(--radius-btn - 2px)` button so it NESTS in the track at any style\n// (a fixed `--radius-control` floats wrong when a style pills `--radius-btn`); the\n// pressed option gets the `--el-page-bg` raised fill + `--shadow-subtle` and its\n// leading glyph takes the `--el-accent` hue.\n// Colour via `--el-*`, shape via element-semantic tokens (the colour + shape\n// swap rules). A11y: a labelled `role=\"group\"`; each option is a real `<button>`\n// carrying `aria-pressed`, so it is keyboard-operable and announced as a toggle.\n\nexport interface SegmentedOption<T extends string> {\n value: T;\n label: string;\n /** Optional leading glyph (decorative — the label carries the accessible name). */\n icon?: ReactNode;\n /** Optional trailing content after the label (e.g. the notification drawer's\n * unread count on the Direct tab, Subtask 5.7.5). Decorative — faint when the\n * option is inactive, the `--el-accent` hue when active (mirrors the leading\n * glyph's active treatment). */\n trailing?: ReactNode;\n /** Disable just this option — a forward-compatible seam (e.g. the Activity\n * card's History filter, Story 5.5's slot) drawn present-but-inert. */\n disabled?: boolean;\n /** Tooltip for the option (e.g. why a seam option is disabled). */\n title?: string;\n}\n\nexport function Segmented<T extends string>({\n options,\n value,\n onChange,\n label,\n className,\n disabled,\n}: {\n options: SegmentedOption<T>[];\n value: T;\n onChange: (value: T) => void;\n /** Accessible group name (e.g. \"Swimlane group by\") — not rendered visually. */\n label: string;\n className?: string;\n disabled?: boolean;\n}) {\n return (\n <div\n role=\"group\"\n aria-label={label}\n className={cn(\n 'inline-flex items-center gap-0.5 rounded-(--radius-btn) border border-(--el-border) bg-(--el-tabnav-track) p-0.5',\n className,\n )}\n >\n {options.map((opt) => {\n const active = opt.value === value;\n return (\n <button\n key={opt.value}\n type=\"button\"\n aria-pressed={active}\n disabled={disabled || opt.disabled}\n title={opt.title}\n onClick={() => {\n if (!active) onChange(opt.value);\n }}\n className={cn(\n // The segment radius nests inside the track: the track is\n // `rounded-(--radius-btn)` with a 2px (`p-0.5`) inset, so a segment\n // fits the shell only at `--radius-btn - 2px`. Using a fixed\n // `--radius-control` breaks when a style makes `--radius-btn` a full\n // pill (soft-playful / retrofuturism: pill track, but a small-radius\n // chip floating inside it). Mirrors AppearancePickers' option radius.\n 'inline-flex h-(--height-control) items-center gap-1.5 rounded-[calc(var(--radius-btn)-2px)] px-(--spacing-control-x) text-[13px] font-medium transition-colors',\n 'focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:outline-none',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n active\n ? 'bg-(--el-page-bg) text-(--el-text-strong) shadow-(--shadow-subtle) [&_.seg-ic]:text-(--el-tabnav-active) [&_.seg-trail]:text-(--el-tabnav-active)'\n : 'text-(--el-text-secondary) hover:text-(--el-text) [&_.seg-ic]:text-(--el-text-faint) [&_.seg-trail]:text-(--el-text-faint)',\n )}\n >\n {opt.icon ? (\n <span className=\"seg-ic inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center\">\n {opt.icon}\n </span>\n ) : null}\n {opt.label}\n {opt.trailing != null ? (\n <span className=\"seg-trail text-[11px] font-semibold tabular-nums\">\n {opt.trailing}\n </span>\n ) : null}\n </button>\n );\n })}\n </div>\n );\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+ import * as class_variance_authority_types from 'class-variance-authority/types';
4
+ import { VariantProps } from 'class-variance-authority';
5
+
6
+ /**
7
+ * Spinner — pure CSS rotation indicator.
8
+ *
9
+ * Three sizes; works standalone or inside a Button's loading state.
10
+ *
11
+ * @example
12
+ * <Spinner size="md" />
13
+ * <Spinner size="sm" aria-label="Loading" />
14
+ */
15
+ declare const spinnerVariants: (props?: ({
16
+ size?: "sm" | "md" | "lg" | null | undefined;
17
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
18
+ interface SpinnerProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'role'>, VariantProps<typeof spinnerVariants> {
19
+ }
20
+ declare const Spinner: react.ForwardRefExoticComponent<SpinnerProps & react.RefAttributes<HTMLSpanElement>>;
21
+
22
+ export { Spinner, type SpinnerProps };