@motir/design-system 0.1.0 → 0.1.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.
- package/README.md +42 -10
- package/dist/components/theme/StyleVignette.js +24 -4
- package/dist/components/theme/StyleVignette.js.map +1 -1
- package/dist/components/ui/Checkbox.d.ts +84 -0
- package/dist/components/ui/Checkbox.js +70 -0
- package/dist/components/ui/Checkbox.js.map +1 -0
- package/dist/components/ui/Combobox.d.ts +7 -1
- package/dist/components/ui/Combobox.js +21 -3
- package/dist/components/ui/Combobox.js.map +1 -1
- package/dist/components/ui/ErrorState.js +1 -1
- package/dist/components/ui/ErrorState.js.map +1 -1
- package/dist/components/ui/Input.d.ts +33 -0
- package/dist/components/ui/Input.js +17 -6
- package/dist/components/ui/Input.js.map +1 -1
- package/dist/components/ui/MultiSelectPicker.js +1 -1
- package/dist/components/ui/MultiSelectPicker.js.map +1 -1
- package/dist/components/ui/Pill.d.ts +7 -4
- package/dist/components/ui/Pill.js +21 -1
- package/dist/components/ui/Pill.js.map +1 -1
- package/dist/components/ui/Segmented.js +22 -3
- package/dist/components/ui/Segmented.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/specimen/TokensSpecimen.js +41 -10
- package/dist/specimen/TokensSpecimen.js.map +1 -1
- package/package.json +7 -2
- package/theme.css +753 -48
|
@@ -1 +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"]}
|
|
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 // `bg-transparent`, so the placeholder paints on the WRAPPER's fill —\n // `--el-surface-soft` once the picker is disabled, where muted is\n // 4.34:1. Secondary is 6.51:1 there and 6.80:1 on the enabled white.\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-secondary) 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"]}
|
|
@@ -7,12 +7,15 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
7
7
|
* Pill — compact status or severity label. Pure span, no interaction.
|
|
8
8
|
*
|
|
9
9
|
* Variant axes (use exactly one):
|
|
10
|
-
* - `status`: planned | in-progress | done — Motir's Subtask
|
|
10
|
+
* - `status`: planned | in-progress | implemented | done — Motir's Subtask
|
|
11
|
+
* lifecycle states. The first three are per-CATEGORY tones; `implemented` is
|
|
12
|
+
* the one PER-STATUS tone (MOTIR-3103), because it shares a category with
|
|
13
|
+
* In Progress / Planning / In Review and must not share their chip.
|
|
11
14
|
* - `severity`: info | success | warning | danger — semantic UI states.
|
|
12
15
|
* - `priority`: highest | high | medium | low | lowest — work-item priority, a
|
|
13
16
|
* 5-step diverging ramp (`--el-priority-*` hue tinted over the surface, plus a
|
|
14
17
|
* redundant direction icon). The single source of truth is `PRIORITY_META`.
|
|
15
|
-
* - `memberRole`: admin | member | viewer — a project membership role
|
|
18
|
+
* - `memberRole`: admin | member | viewer | custom — a project membership role
|
|
16
19
|
* (Story 6.4), one hued tint per role. (Named `memberRole`, not `role`, so
|
|
17
20
|
* it can't collide with the DOM `role` ARIA attribute on the span.)
|
|
18
21
|
* - `orgRole`: owner | admin | member — an ORGANIZATION role (Story 6.10),
|
|
@@ -35,10 +38,10 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
35
38
|
* <Pill tone="neutral">3 members</Pill>
|
|
36
39
|
*/
|
|
37
40
|
declare const pillVariants: (props?: ({
|
|
38
|
-
status?: "done" | "planned" | "in-progress" | null | undefined;
|
|
41
|
+
status?: "done" | "planned" | "in-progress" | "implemented" | null | undefined;
|
|
39
42
|
severity?: "danger" | "info" | "success" | "warning" | null | undefined;
|
|
40
43
|
priority?: "highest" | "high" | "medium" | "low" | "lowest" | null | undefined;
|
|
41
|
-
memberRole?: "admin" | "member" | "viewer" | null | undefined;
|
|
44
|
+
memberRole?: "admin" | "member" | "viewer" | "custom" | null | undefined;
|
|
42
45
|
orgRole?: "admin" | "member" | "owner" | null | undefined;
|
|
43
46
|
tone?: "neutral" | "private" | "archived" | null | undefined;
|
|
44
47
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
@@ -26,6 +26,20 @@ var pillVariants = cva(
|
|
|
26
26
|
status: {
|
|
27
27
|
planned: "bg-(--el-tint-lavender) text-(--el-text-strong) border-transparent",
|
|
28
28
|
"in-progress": "bg-(--el-tint-sky) text-(--el-text-strong) border-transparent",
|
|
29
|
+
// BUILT, waiting on checks (MOTIR-3103). The first PER-STATUS tone on
|
|
30
|
+
// this axis — the other three are per-CATEGORY, and `implemented` sits in
|
|
31
|
+
// the same `in_progress` category as In Progress, Planning and In Review,
|
|
32
|
+
// so without its own tone four statuses render one chip.
|
|
33
|
+
//
|
|
34
|
+
// It uses the recipe theme.css's own `--el-status-*` header prescribes —
|
|
35
|
+
// "a status CHIP bg = color-mix(var(--el-status-X) 14%, var(--el-surface))
|
|
36
|
+
// with --el-text-strong" — which is the same dilution the `priority`
|
|
37
|
+
// variant below already renders. The hue is not spent by any other
|
|
38
|
+
// status in any palette (measured at ΔE2000 >= 10 across all ten by
|
|
39
|
+
// tests/theme/statusHueSeparation.test.ts), and the chip additionally
|
|
40
|
+
// carries a GLYPH, because a 14% tint is a subtle mark by construction
|
|
41
|
+
// and finding #35 forbids resting a state on colour alone.
|
|
42
|
+
implemented: "bg-[color-mix(in_srgb,var(--el-status-implemented)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent",
|
|
29
43
|
done: "bg-(--el-tint-mint) text-(--el-text-strong) border-transparent"
|
|
30
44
|
},
|
|
31
45
|
severity: {
|
|
@@ -54,10 +68,16 @@ var pillVariants = cva(
|
|
|
54
68
|
// through the DEDICATED `--el-role-*` tokens (MOTIR-1274 · 1266.3) so a
|
|
55
69
|
// palette can tune roles apart from the other --el-tint-* meanings; each
|
|
56
70
|
// defaults to its prior tint (zero visual change).
|
|
71
|
+
// `custom` (MOTIR-2257 · MOTIR-2485) is a project's OWN role, which has no
|
|
72
|
+
// enum value — it takes the next free tint slot (peach) because this
|
|
73
|
+
// surface had already spent lavender / sky / mint on the built-ins, so a
|
|
74
|
+
// custom chip in any of those would read as one of them. The KIND is
|
|
75
|
+
// carried in WORDS by the chip's accessible name; nothing rests on the hue.
|
|
57
76
|
memberRole: {
|
|
58
77
|
admin: "bg-(--el-role-admin) text-(--el-text-strong) border-transparent",
|
|
59
78
|
member: "bg-(--el-role-member) text-(--el-text-strong) border-transparent",
|
|
60
|
-
viewer: "bg-(--el-role-viewer) text-(--el-text-strong) border-transparent"
|
|
79
|
+
viewer: "bg-(--el-role-viewer) text-(--el-text-strong) border-transparent",
|
|
80
|
+
custom: "bg-(--el-role-custom) text-(--el-text-strong) border-transparent"
|
|
61
81
|
},
|
|
62
82
|
// Organization roles (Story 6.10 · design/org-admin): owner → lavender
|
|
63
83
|
// (the highest, brand-purple tier), admin → sky, member → mint — the hue
|
|
@@ -1 +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"]}
|
|
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;ACsBA,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAcf,WAAA,EACE,uHAAA;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;AAAA;AAAA;AAAA;AAAA;AAAA,MAYA,UAAA,EAAY;AAAA,QACV,KAAA,EAAO,iEAAA;AAAA,QACP,MAAA,EAAQ,kEAAA;AAAA,QACR,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 | implemented | done — Motir's Subtask\n * lifecycle states. The first three are per-CATEGORY tones; `implemented` is\n * the one PER-STATUS tone (MOTIR-3103), because it shares a category with\n * In Progress / Planning / In Review and must not share their chip.\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 | custom — 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 // BUILT, waiting on checks (MOTIR-3103). The first PER-STATUS tone on\n // this axis — the other three are per-CATEGORY, and `implemented` sits in\n // the same `in_progress` category as In Progress, Planning and In Review,\n // so without its own tone four statuses render one chip.\n //\n // It uses the recipe theme.css's own `--el-status-*` header prescribes —\n // \"a status CHIP bg = color-mix(var(--el-status-X) 14%, var(--el-surface))\n // with --el-text-strong\" — which is the same dilution the `priority`\n // variant below already renders. The hue is not spent by any other\n // status in any palette (measured at ΔE2000 >= 10 across all ten by\n // tests/theme/statusHueSeparation.test.ts), and the chip additionally\n // carries a GLYPH, because a 14% tint is a subtle mark by construction\n // and finding #35 forbids resting a state on colour alone.\n implemented:\n 'bg-[color-mix(in_srgb,var(--el-status-implemented)_14%,var(--el-surface))] 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 // `custom` (MOTIR-2257 · MOTIR-2485) is a project's OWN role, which has no\n // enum value — it takes the next free tint slot (peach) because this\n // surface had already spent lavender / sky / mint on the built-ins, so a\n // custom chip in any of those would read as one of them. The KIND is\n // carried in WORDS by the chip's accessible name; nothing rests on the hue.\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 custom: 'bg-(--el-role-custom) 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"]}
|
|
@@ -45,12 +45,31 @@ function Segmented({
|
|
|
45
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
46
|
"focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:outline-none",
|
|
47
47
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
48
|
-
active ? "bg-(--el-page-bg) text-(--el-text-strong) shadow-(--shadow-subtle)
|
|
48
|
+
active ? "bg-(--el-page-bg) text-(--el-text-strong) shadow-(--shadow-subtle)" : "text-(--el-text-secondary) hover:text-(--el-text)"
|
|
49
49
|
),
|
|
50
50
|
children: [
|
|
51
|
-
opt.icon ? /* @__PURE__ */ jsx(
|
|
51
|
+
opt.icon ? /* @__PURE__ */ jsx(
|
|
52
|
+
"span",
|
|
53
|
+
{
|
|
54
|
+
"aria-hidden": true,
|
|
55
|
+
className: cn(
|
|
56
|
+
"seg-ic inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center",
|
|
57
|
+
active ? "text-(--el-tabnav-active)" : "text-(--el-text-faint)"
|
|
58
|
+
),
|
|
59
|
+
children: opt.icon
|
|
60
|
+
}
|
|
61
|
+
) : null,
|
|
52
62
|
opt.label,
|
|
53
|
-
opt.trailing != null ? /* @__PURE__ */ jsx(
|
|
63
|
+
opt.trailing != null ? /* @__PURE__ */ jsx(
|
|
64
|
+
"span",
|
|
65
|
+
{
|
|
66
|
+
className: cn(
|
|
67
|
+
"seg-trail text-[11px] font-semibold tabular-nums",
|
|
68
|
+
active ? "text-(--el-tabnav-active)" : "text-(--el-text-secondary)"
|
|
69
|
+
),
|
|
70
|
+
children: opt.trailing
|
|
71
|
+
}
|
|
72
|
+
) : null
|
|
54
73
|
]
|
|
55
74
|
},
|
|
56
75
|
opt.value
|
|
@@ -1 +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,
|
|
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,oEAAA,GACA;AAAA,aACN;AAAA,YAQC,QAAA,EAAA;AAAA,cAAA,GAAA,CAAI,IAAA,mBACH,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,aAAA,EAAW,IAAA;AAAA,kBACX,SAAA,EAAW,EAAA;AAAA,oBACT,qEAAA;AAAA,oBACA,SAAS,2BAAA,GAA8B;AAAA,mBACzC;AAAA,kBAEC,QAAA,EAAA,GAAA,CAAI;AAAA;AAAA,eACP,GACE,IAAA;AAAA,cACH,GAAA,CAAI,KAAA;AAAA,cACJ,GAAA,CAAI,YAAY,IAAA,mBACf,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAW,EAAA;AAAA,oBACT,kDAAA;AAAA,oBACA,SAAS,2BAAA,GAA8B;AAAA,mBACzC;AAAA,kBAEC,QAAA,EAAA,GAAA,CAAI;AAAA;AAAA,eACP,GACE;AAAA;AAAA,WAAA;AAAA,UAlDC,GAAA,CAAI;AAAA,SAmDX;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)'\n : 'text-(--el-text-secondary) hover:text-(--el-text)',\n )}\n >\n {/* The icon and the trailing count take their ink HERE rather than\n through the segment's `[&_.seg-*]:…` descendant variants\n (MOTIR-2475). Two things follow: the glyph says it is\n `aria-hidden`, so its faint ink is one of the token's legitimate\n jobs; and the trailing COUNT — text a reader reads — is no\n longer painted at 2.4:1 on an inactive segment. */}\n {opt.icon ? (\n <span\n aria-hidden\n className={cn(\n 'seg-ic inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center',\n active ? 'text-(--el-tabnav-active)' : 'text-(--el-text-faint)',\n )}\n >\n {opt.icon}\n </span>\n ) : null}\n {opt.label}\n {opt.trailing != null ? (\n <span\n className={cn(\n 'seg-trail text-[11px] font-semibold tabular-nums',\n active ? 'text-(--el-tabnav-active)' : 'text-(--el-text-secondary)',\n )}\n >\n {opt.trailing}\n </span>\n ) : null}\n </button>\n );\n })}\n </div>\n );\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export { HandDrawnFilter } from './components/theme/HandDrawnFilter.js';
|
|
|
14
14
|
export { ImmersiveTilt } from './components/theme/ImmersiveTilt.js';
|
|
15
15
|
export { Button, ButtonProps, buttonVariants } from './components/ui/Button.js';
|
|
16
16
|
export { Card, CardProps } from './components/ui/Card.js';
|
|
17
|
+
export { Checkbox, CheckboxProps, checkboxStateLabel } from './components/ui/Checkbox.js';
|
|
17
18
|
export { Input, InputProps } from './components/ui/Input.js';
|
|
18
19
|
export { Textarea, TextareaProps } from './components/ui/Textarea.js';
|
|
19
20
|
export { FormField, FormFieldProps, describedById } from './components/ui/FormField.js';
|
package/dist/index.js
CHANGED
|
@@ -45,6 +45,7 @@ export * from './components/theme/ImmersiveTilt.js';
|
|
|
45
45
|
// ── Framework-agnostic UI primitives ─────────────────────────────────────────
|
|
46
46
|
export * from './components/ui/Button.js';
|
|
47
47
|
export * from './components/ui/Card.js';
|
|
48
|
+
export * from './components/ui/Checkbox.js';
|
|
48
49
|
export * from './components/ui/Input.js';
|
|
49
50
|
export * from './components/ui/Textarea.js';
|
|
50
51
|
export * from './components/ui/FormField.js';
|
|
@@ -722,12 +722,15 @@ var Input = forwardRef(function Input2({
|
|
|
722
722
|
id,
|
|
723
723
|
className,
|
|
724
724
|
disabled,
|
|
725
|
+
readOnly,
|
|
725
726
|
...rest
|
|
726
727
|
}, ref) {
|
|
727
728
|
const autoId = useId();
|
|
728
729
|
const inputId = id ?? autoId;
|
|
729
730
|
const describedBy = describedById(inputId, error, helperText);
|
|
730
731
|
const hasError = Boolean(error);
|
|
732
|
+
const state = disabled ? "disabled" : readOnly ? "readonly" : "editable";
|
|
733
|
+
const affixInk = state === "disabled" ? "text-(--el-input-disabled-text)" : state === "readonly" ? "text-(--el-text-secondary)" : "text-(--el-icon-field)";
|
|
731
734
|
return /* @__PURE__ */ jsx(
|
|
732
735
|
FormField,
|
|
733
736
|
{
|
|
@@ -740,16 +743,18 @@ var Input = forwardRef(function Input2({
|
|
|
740
743
|
"div",
|
|
741
744
|
{
|
|
742
745
|
"data-surface": "input",
|
|
746
|
+
"data-state": state === "editable" ? void 0 : state,
|
|
743
747
|
className: cn(
|
|
744
|
-
"flex h-(--height-input) w-full items-center gap-2 rounded-(--radius-input) border
|
|
748
|
+
"flex h-(--height-input) w-full items-center gap-2 rounded-(--radius-input) border",
|
|
745
749
|
"px-(--spacing-input-x)",
|
|
746
750
|
"transition-colors",
|
|
747
751
|
"focus-within:ring-2 focus-within:ring-(--focus-ring-color) focus-within:ring-offset-2 focus-within:ring-offset-background",
|
|
748
|
-
|
|
749
|
-
disabled
|
|
752
|
+
state === "disabled" ? "bg-(--el-input-disabled-bg)" : state === "readonly" ? "bg-(--el-input-readonly-bg)" : "bg-(--el-page-bg)",
|
|
753
|
+
hasError ? "border-(--el-danger)" : state === "disabled" ? "border-(--el-input-disabled-border)" : "border-(--el-input-border)",
|
|
754
|
+
state === "disabled" && "cursor-not-allowed"
|
|
750
755
|
),
|
|
751
756
|
children: [
|
|
752
|
-
addonStart ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "
|
|
757
|
+
addonStart ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("inline-flex", affixInk), children: addonStart }) : null,
|
|
753
758
|
/* @__PURE__ */ jsx(
|
|
754
759
|
"input",
|
|
755
760
|
{
|
|
@@ -758,15 +763,21 @@ var Input = forwardRef(function Input2({
|
|
|
758
763
|
"aria-invalid": hasError || void 0,
|
|
759
764
|
"aria-describedby": describedBy,
|
|
760
765
|
disabled,
|
|
766
|
+
readOnly,
|
|
761
767
|
className: cn(
|
|
762
|
-
"flex-1 bg-transparent font-sans text-sm
|
|
768
|
+
"flex-1 bg-transparent font-sans text-sm outline-none",
|
|
769
|
+
// The placeholder is a pseudo-element that IS text (MOTIR-2489), so
|
|
770
|
+
// it takes the same per-state ink the value does. `--el-text-muted`
|
|
771
|
+
// clears AA on the white page by 0.04 and fails on both non-editable
|
|
772
|
+
// fills, so it cannot follow the value onto them.
|
|
773
|
+
state === "disabled" ? "cursor-not-allowed text-(--el-input-disabled-text) placeholder:text-(--el-input-disabled-text)" : state === "readonly" ? "text-(--el-text) placeholder:text-(--el-text-secondary)" : "text-(--el-text) placeholder:text-(--el-text-muted)",
|
|
763
774
|
"disabled:cursor-not-allowed",
|
|
764
775
|
className
|
|
765
776
|
),
|
|
766
777
|
...rest
|
|
767
778
|
}
|
|
768
779
|
),
|
|
769
|
-
addonEnd ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "
|
|
780
|
+
addonEnd ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("inline-flex", affixInk), children: addonEnd }) : null
|
|
770
781
|
]
|
|
771
782
|
}
|
|
772
783
|
)
|
|
@@ -791,6 +802,20 @@ var pillVariants = cva(
|
|
|
791
802
|
status: {
|
|
792
803
|
planned: "bg-(--el-tint-lavender) text-(--el-text-strong) border-transparent",
|
|
793
804
|
"in-progress": "bg-(--el-tint-sky) text-(--el-text-strong) border-transparent",
|
|
805
|
+
// BUILT, waiting on checks (MOTIR-3103). The first PER-STATUS tone on
|
|
806
|
+
// this axis — the other three are per-CATEGORY, and `implemented` sits in
|
|
807
|
+
// the same `in_progress` category as In Progress, Planning and In Review,
|
|
808
|
+
// so without its own tone four statuses render one chip.
|
|
809
|
+
//
|
|
810
|
+
// It uses the recipe theme.css's own `--el-status-*` header prescribes —
|
|
811
|
+
// "a status CHIP bg = color-mix(var(--el-status-X) 14%, var(--el-surface))
|
|
812
|
+
// with --el-text-strong" — which is the same dilution the `priority`
|
|
813
|
+
// variant below already renders. The hue is not spent by any other
|
|
814
|
+
// status in any palette (measured at ΔE2000 >= 10 across all ten by
|
|
815
|
+
// tests/theme/statusHueSeparation.test.ts), and the chip additionally
|
|
816
|
+
// carries a GLYPH, because a 14% tint is a subtle mark by construction
|
|
817
|
+
// and finding #35 forbids resting a state on colour alone.
|
|
818
|
+
implemented: "bg-[color-mix(in_srgb,var(--el-status-implemented)_14%,var(--el-surface))] text-(--el-text-strong) border-transparent",
|
|
794
819
|
done: "bg-(--el-tint-mint) text-(--el-text-strong) border-transparent"
|
|
795
820
|
},
|
|
796
821
|
severity: {
|
|
@@ -819,10 +844,16 @@ var pillVariants = cva(
|
|
|
819
844
|
// through the DEDICATED `--el-role-*` tokens (MOTIR-1274 · 1266.3) so a
|
|
820
845
|
// palette can tune roles apart from the other --el-tint-* meanings; each
|
|
821
846
|
// defaults to its prior tint (zero visual change).
|
|
847
|
+
// `custom` (MOTIR-2257 · MOTIR-2485) is a project's OWN role, which has no
|
|
848
|
+
// enum value — it takes the next free tint slot (peach) because this
|
|
849
|
+
// surface had already spent lavender / sky / mint on the built-ins, so a
|
|
850
|
+
// custom chip in any of those would read as one of them. The KIND is
|
|
851
|
+
// carried in WORDS by the chip's accessible name; nothing rests on the hue.
|
|
822
852
|
memberRole: {
|
|
823
853
|
admin: "bg-(--el-role-admin) text-(--el-text-strong) border-transparent",
|
|
824
854
|
member: "bg-(--el-role-member) text-(--el-text-strong) border-transparent",
|
|
825
|
-
viewer: "bg-(--el-role-viewer) text-(--el-text-strong) border-transparent"
|
|
855
|
+
viewer: "bg-(--el-role-viewer) text-(--el-text-strong) border-transparent",
|
|
856
|
+
custom: "bg-(--el-role-custom) text-(--el-text-strong) border-transparent"
|
|
826
857
|
},
|
|
827
858
|
// Organization roles (Story 6.10 · design/org-admin): owner → lavender
|
|
828
859
|
// (the highest, brand-purple tier), admin → sky, member → mint — the hue
|
|
@@ -935,8 +966,8 @@ function StyleVignette({ styleId, palette, type, label, className }) {
|
|
|
935
966
|
/* @__PURE__ */ jsx("span", { className: "size-2.5 rounded-full bg-(--el-accent)", "aria-hidden": true }),
|
|
936
967
|
/* @__PURE__ */ jsx("span", { className: "font-serif text-[12px] font-semibold text-(--el-text)", children: "Acme" }),
|
|
937
968
|
/* @__PURE__ */ jsx("span", { className: "ml-2 rounded-(--radius-control) bg-(--el-accent) px-2 py-[3px] text-[10px] font-medium text-(--el-accent-text)", children: "Board" }),
|
|
938
|
-
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-(--el-text-
|
|
939
|
-
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-(--el-text-
|
|
969
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-(--el-text-secondary)", children: "Backlog" }),
|
|
970
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-(--el-text-secondary)", children: "Reports" }),
|
|
940
971
|
/* @__PURE__ */ jsx("span", { className: "ml-auto size-5 rounded-full bg-(--el-muted)", "aria-hidden": true })
|
|
941
972
|
]
|
|
942
973
|
}
|
|
@@ -1006,7 +1037,7 @@ function StyleVignette({ styleId, palette, type, label, className }) {
|
|
|
1006
1037
|
"div",
|
|
1007
1038
|
{
|
|
1008
1039
|
"data-surface": "input",
|
|
1009
|
-
className: "flex h-(--height-input) items-center gap-2 rounded-(--radius-input) border border-(--el-border-strong) bg-(--el-page-bg) px-(--spacing-input-x) text-[11px] text-(--el-text-
|
|
1040
|
+
className: "flex h-(--height-input) items-center gap-2 rounded-(--radius-input) border border-(--el-border-strong) bg-(--el-page-bg) px-(--spacing-input-x) text-[11px] text-(--el-text-secondary)",
|
|
1010
1041
|
children: [
|
|
1011
1042
|
/* @__PURE__ */ jsx(Search, { className: "size-3.5", "aria-hidden": true }),
|
|
1012
1043
|
/* @__PURE__ */ jsx("span", { children: "Search work items\u2026" })
|