@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,33 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+ import * as class_variance_authority_types from 'class-variance-authority/types';
4
+ import { VariantProps } from 'class-variance-authority';
5
+
6
+ /**
7
+ * Card — content container with optional header/footer slots and tint variants.
8
+ *
9
+ * Compose with `header` / `footer` props for the common pattern, or just
10
+ * pass children for a bare card. `clickable` makes the whole card a button
11
+ * (use `onClick` + Enter/Space; pass `href` to render as `<a>` instead).
12
+ *
13
+ * Tint variants use the pastel feature tints from the palette
14
+ * (peach/rose/mint/lavender/sky/yellow) — apply sparingly, never to
15
+ * page-level surfaces.
16
+ *
17
+ * @example
18
+ * <Card header={<h3>Title</h3>}>Body content</Card>
19
+ * <Card tint="lavender" clickable onClick={open}>Pastel card</Card>
20
+ * <Card tint="mint"><a href="/x">Link wrapper</a></Card>
21
+ */
22
+ declare const cardVariants: (props?: ({
23
+ tint?: "none" | "lavender" | "yellow" | "peach" | "rose" | "mint" | "sky" | null | undefined;
24
+ clickable?: boolean | null | undefined;
25
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
26
+ interface CardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, VariantProps<typeof cardVariants> {
27
+ header?: ReactNode;
28
+ footer?: ReactNode;
29
+ children?: ReactNode;
30
+ }
31
+ declare const Card: react.ForwardRefExoticComponent<CardProps & react.RefAttributes<HTMLDivElement>>;
32
+
33
+ export { Card, type CardProps };
@@ -0,0 +1,64 @@
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 { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ // src/components/ui/Card.tsx
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+ var cardVariants = cva(
12
+ cn(
13
+ "rounded-(--radius-card) border border-(--el-border)",
14
+ "p-(--spacing-card-padding)",
15
+ "transition-shadow duration-(--transition-duration)"
16
+ ),
17
+ {
18
+ variants: {
19
+ tint: {
20
+ none: "bg-(--el-card)",
21
+ peach: "bg-(--el-tint-peach) border-transparent",
22
+ rose: "bg-(--el-tint-rose) border-transparent",
23
+ mint: "bg-(--el-tint-mint) border-transparent",
24
+ lavender: "bg-(--el-tint-lavender) border-transparent",
25
+ sky: "bg-(--el-tint-sky) border-transparent",
26
+ yellow: "bg-(--el-tint-yellow) border-transparent"
27
+ },
28
+ clickable: {
29
+ true: "cursor-pointer hover:shadow-(--shadow-card) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background",
30
+ false: ""
31
+ }
32
+ },
33
+ defaultVariants: { tint: "none", clickable: false }
34
+ }
35
+ );
36
+ var Card = forwardRef(function Card2({ tint, clickable, header, footer, className, children, ...rest }, ref) {
37
+ return /* @__PURE__ */ jsxs(
38
+ "div",
39
+ {
40
+ ref,
41
+ className: cn(cardVariants({ tint, clickable }), className),
42
+ "data-tilt": "",
43
+ ...tint && tint !== "none" ? {} : { "data-surface": "card" },
44
+ ...clickable ? { tabIndex: 0, role: "button" } : {},
45
+ ...rest,
46
+ children: [
47
+ header ? /* @__PURE__ */ jsx("div", { "data-tilt-layer": "front", className: "mb-(--spacing-md)", children: header }) : null,
48
+ /* @__PURE__ */ jsx("div", { children }),
49
+ footer ? /* @__PURE__ */ jsx(
50
+ "div",
51
+ {
52
+ "data-tilt-layer": "back",
53
+ className: "border-(--el-border) mt-(--spacing-md) border-t pt-(--spacing-md)",
54
+ children: footer
55
+ }
56
+ ) : null
57
+ ]
58
+ }
59
+ );
60
+ });
61
+
62
+ export { Card };
63
+ //# sourceMappingURL=Card.js.map
64
+ //# sourceMappingURL=Card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Card.tsx"],"names":["Card"],"mappings":";;;;;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACIA,IAAM,YAAA,GAAe,GAAA;AAAA,EACnB,EAAA;AAAA,IACE,qDAAA;AAAA,IACA,4BAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,gBAAA;AAAA,QACN,KAAA,EAAO,yCAAA;AAAA,QACP,IAAA,EAAM,wCAAA;AAAA,QACN,IAAA,EAAM,wCAAA;AAAA,QACN,QAAA,EAAU,4CAAA;AAAA,QACV,GAAA,EAAK,uCAAA;AAAA,QACL,MAAA,EAAQ;AAAA,OACV;AAAA,MACA,SAAA,EAAW;AAAA,QACT,IAAA,EAAM,sMAAA;AAAA,QACN,KAAA,EAAO;AAAA;AACT,KACF;AAAA,IACA,eAAA,EAAiB,EAAE,IAAA,EAAM,MAAA,EAAQ,WAAW,KAAA;AAAM;AAEtD,CAAA;AASO,IAAM,IAAA,GAAO,UAAA,CAAsC,SAASA,KAAAA,CACjE,EAAE,IAAA,EAAM,SAAA,EAAW,MAAA,EAAQ,MAAA,EAAQ,SAAA,EAAW,QAAA,EAAU,GAAG,IAAA,IAC3D,GAAA,EACA;AACA,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,SAAA,EAAW,GAAG,YAAA,CAAa,EAAE,MAAM,SAAA,EAAW,GAAG,SAAS,CAAA;AAAA,MAI1D,WAAA,EAAU,EAAA;AAAA,MAKT,GAAI,QAAQ,IAAA,KAAS,MAAA,GAAS,EAAC,GAAI,EAAE,gBAAgB,MAAA,EAAO;AAAA,MAC5D,GAAI,YAAY,EAAE,QAAA,EAAU,GAAG,IAAA,EAAM,QAAA,KAAa,EAAC;AAAA,MACnD,GAAG,IAAA;AAAA,MAQH,QAAA,EAAA;AAAA,QAAA,MAAA,uBACE,KAAA,EAAA,EAAI,iBAAA,EAAgB,SAAQ,SAAA,EAAU,mBAAA,EACpC,kBACH,CAAA,GACE,IAAA;AAAA,wBACJ,GAAA,CAAC,SAAK,QAAA,EAAS,CAAA;AAAA,QACd,MAAA,mBACC,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,iBAAA,EAAgB,MAAA;AAAA,YAChB,SAAA,EAAU,mEAAA;AAAA,YAET,QAAA,EAAA;AAAA;AAAA,SACH,GACE;AAAA;AAAA;AAAA,GACN;AAEJ,CAAC","file":"Card.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 { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n/**\n * Card — content container with optional header/footer slots and tint variants.\n *\n * Compose with `header` / `footer` props for the common pattern, or just\n * pass children for a bare card. `clickable` makes the whole card a button\n * (use `onClick` + Enter/Space; pass `href` to render as `<a>` instead).\n *\n * Tint variants use the pastel feature tints from the palette\n * (peach/rose/mint/lavender/sky/yellow) — apply sparingly, never to\n * page-level surfaces.\n *\n * @example\n * <Card header={<h3>Title</h3>}>Body content</Card>\n * <Card tint=\"lavender\" clickable onClick={open}>Pastel card</Card>\n * <Card tint=\"mint\"><a href=\"/x\">Link wrapper</a></Card>\n */\nconst cardVariants = cva(\n cn(\n 'rounded-(--radius-card) border border-(--el-border)',\n 'p-(--spacing-card-padding)',\n 'transition-shadow duration-(--transition-duration)',\n ),\n {\n variants: {\n tint: {\n none: 'bg-(--el-card)',\n peach: 'bg-(--el-tint-peach) border-transparent',\n rose: 'bg-(--el-tint-rose) border-transparent',\n mint: 'bg-(--el-tint-mint) border-transparent',\n lavender: 'bg-(--el-tint-lavender) border-transparent',\n sky: 'bg-(--el-tint-sky) border-transparent',\n yellow: 'bg-(--el-tint-yellow) border-transparent',\n },\n clickable: {\n true: 'cursor-pointer hover:shadow-(--shadow-card) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n false: '',\n },\n },\n defaultVariants: { tint: 'none', clickable: false },\n },\n);\n\nexport interface CardProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'children'>, VariantProps<typeof cardVariants> {\n header?: ReactNode;\n footer?: ReactNode;\n children?: ReactNode;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { tint, clickable, header, footer, className, children, ...rest },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cn(cardVariants({ tint, clickable }), className)}\n // `data-tilt` is the hook the 3D / Immersive style's pointer-parallax\n // engine (ImmersiveTilt, 7.3.39) uses to tip this tile toward the cursor.\n // Inert for every other style + under reduced motion; harmless otherwise.\n data-tilt=\"\"\n // `data-surface` is the hook a surface-MATERIAL style (e.g. glassmorphism)\n // uses to frost this panel — see globals.css's material layer. Only the\n // default (untinted) card opts in; a pastel-tint feature card keeps its\n // opaque tint, never a frosted overlay that would wash the hue out.\n {...(tint && tint !== 'none' ? {} : { 'data-surface': 'card' })}\n {...(clickable ? { tabIndex: 0, role: 'button' } : {})}\n {...rest}\n >\n {/* `data-tilt-layer` lifts the slot onto a translateZ plane under the 3D /\n Immersive style (7.3.39) so it PARALLAXES above the card face as the\n card tilts — `front` (the header/title pops most), `back` (the footer\n sits nearer the face). Inert under every other style + reduced motion.\n The body stays on the face so arbitrary content never renders on a\n skewed Z-plane. */}\n {header ? (\n <div data-tilt-layer=\"front\" className=\"mb-(--spacing-md)\">\n {header}\n </div>\n ) : null}\n <div>{children}</div>\n {footer ? (\n <div\n data-tilt-layer=\"back\"\n className=\"border-(--el-border) mt-(--spacing-md) border-t pt-(--spacing-md)\"\n >\n {footer}\n </div>\n ) : null}\n </div>\n );\n});\n"]}
@@ -0,0 +1,19 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface ColorSwatchOption {
4
+ /** Accessible name + tooltip. */
5
+ name: string;
6
+ /** The stored hex value, or null for "derive from category". */
7
+ value: string | null;
8
+ }
9
+ declare const STATUS_COLOR_SWATCHES: ReadonlyArray<ColorSwatchOption>;
10
+ interface ColorSwatchPickerProps {
11
+ /** Selected hex, or null for "derive from category". */
12
+ value: string | null;
13
+ onChange: (value: string | null) => void;
14
+ label?: string;
15
+ disabled?: boolean;
16
+ }
17
+ declare function ColorSwatchPicker({ value, onChange, label, disabled, }: ColorSwatchPickerProps): react_jsx_runtime.JSX.Element;
18
+
19
+ export { type ColorSwatchOption, ColorSwatchPicker, type ColorSwatchPickerProps, STATUS_COLOR_SWATCHES };
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+ import { useRef } from 'react';
3
+ import { Ban } from 'lucide-react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var STATUS_COLOR_SWATCHES = [
7
+ { name: "None (derive from category)", value: null },
8
+ { name: "Grey", value: "#787671" },
9
+ { name: "Blue", value: "#0075de" },
10
+ { name: "Teal", value: "#2a9d99" },
11
+ { name: "Green", value: "#1aae39" },
12
+ { name: "Orange", value: "#dd5b00" },
13
+ { name: "Red", value: "#e03131" },
14
+ { name: "Pink", value: "#ff64c8" }
15
+ ];
16
+ function ColorSwatchPicker({
17
+ value,
18
+ onChange,
19
+ label = "Color",
20
+ disabled
21
+ }) {
22
+ const refs = useRef([]);
23
+ const selectedIndex = STATUS_COLOR_SWATCHES.findIndex((s) => s.value === value);
24
+ const tabbableIndex = selectedIndex >= 0 ? selectedIndex : 0;
25
+ function select(index) {
26
+ const opt = STATUS_COLOR_SWATCHES[index];
27
+ if (!opt) return;
28
+ onChange(opt.value);
29
+ refs.current[index]?.focus();
30
+ }
31
+ function onKeyDown(e, index) {
32
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
33
+ e.preventDefault();
34
+ select((index + 1) % STATUS_COLOR_SWATCHES.length);
35
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
36
+ e.preventDefault();
37
+ select((index - 1 + STATUS_COLOR_SWATCHES.length) % STATUS_COLOR_SWATCHES.length);
38
+ }
39
+ }
40
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
41
+ /* @__PURE__ */ jsx("span", { className: "text-(--el-text) font-sans text-sm font-medium", children: label }),
42
+ /* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": label, className: "flex flex-wrap gap-2", children: STATUS_COLOR_SWATCHES.map((opt, i) => {
43
+ const checked = opt.value === value;
44
+ return /* @__PURE__ */ jsx(
45
+ "button",
46
+ {
47
+ ref: (el) => {
48
+ refs.current[i] = el;
49
+ },
50
+ type: "button",
51
+ role: "radio",
52
+ "aria-checked": checked,
53
+ "aria-label": opt.name,
54
+ title: opt.name,
55
+ tabIndex: i === tabbableIndex ? 0 : -1,
56
+ disabled,
57
+ onClick: () => onChange(opt.value),
58
+ onKeyDown: (e) => onKeyDown(e, i),
59
+ className: `flex h-7 w-7 items-center justify-center rounded-full border border-(--el-border) transition-shadow focus-visible:ring-(--el-text) focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:opacity-50 ${checked ? "ring-(--el-text) ring-2 ring-offset-2" : ""}`,
60
+ style: opt.value ? { backgroundColor: opt.value } : void 0,
61
+ children: opt.value === null && /* @__PURE__ */ jsx(Ban, { className: "text-(--el-text-muted) h-4 w-4", "aria-hidden": true })
62
+ },
63
+ opt.name
64
+ );
65
+ }) })
66
+ ] });
67
+ }
68
+
69
+ export { ColorSwatchPicker, STATUS_COLOR_SWATCHES };
70
+ //# sourceMappingURL=ColorSwatchPicker.js.map
71
+ //# sourceMappingURL=ColorSwatchPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ui/ColorSwatchPicker.tsx"],"names":[],"mappings":";;;;AAwBO,IAAM,qBAAA,GAA0D;AAAA,EACrE,EAAE,IAAA,EAAM,6BAAA,EAA+B,KAAA,EAAO,IAAA,EAAK;AAAA,EACnD,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAU;AAAA,EACjC,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAU;AAAA,EACjC,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAU;AAAA,EACjC,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,SAAA,EAAU;AAAA,EAClC,EAAE,IAAA,EAAM,QAAA,EAAU,KAAA,EAAO,SAAA,EAAU;AAAA,EACnC,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,EAAO,SAAA,EAAU;AAAA,EAChC,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,SAAA;AACzB;AAUO,SAAS,iBAAA,CAAkB;AAAA,EAChC,KAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA,GAAQ,OAAA;AAAA,EACR;AACF,CAAA,EAA2B;AACzB,EAAA,MAAM,IAAA,GAAO,MAAA,CAAwC,EAAE,CAAA;AACvD,EAAA,MAAM,gBAAgB,qBAAA,CAAsB,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,UAAU,KAAK,CAAA;AAG9E,EAAA,MAAM,aAAA,GAAgB,aAAA,IAAiB,CAAA,GAAI,aAAA,GAAgB,CAAA;AAE3D,EAAA,SAAS,OAAO,KAAA,EAAe;AAC7B,IAAA,MAAM,GAAA,GAAM,sBAAsB,KAAK,CAAA;AACvC,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,QAAA,CAAS,IAAI,KAAK,CAAA;AAClB,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,CAAA,EAAG,KAAA,EAAM;AAAA,EAC7B;AAEA,EAAA,SAAS,SAAA,CAAU,GAAkB,KAAA,EAAe;AAClD,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,YAAA,IAAgB,CAAA,CAAE,QAAQ,WAAA,EAAa;AACnD,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,MAAA,CAAA,CAAQ,KAAA,GAAQ,CAAA,IAAK,qBAAA,CAAsB,MAAM,CAAA;AAAA,IACnD,WAAW,CAAA,CAAE,GAAA,KAAQ,WAAA,IAAe,CAAA,CAAE,QAAQ,SAAA,EAAW;AACvD,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,MAAA,CAAA,CAAQ,KAAA,GAAQ,CAAA,GAAI,qBAAA,CAAsB,MAAA,IAAU,sBAAsB,MAAM,CAAA;AAAA,IAClF;AAAA,EACF;AAEA,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EACb,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gDAAA,EAAkD,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,oBACxE,GAAA,CAAC,KAAA,EAAA,EAAI,IAAA,EAAK,YAAA,EAAa,YAAA,EAAY,KAAA,EAAO,SAAA,EAAU,sBAAA,EACjD,QAAA,EAAA,qBAAA,CAAsB,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,KAAM;AACrC,MAAA,MAAM,OAAA,GAAU,IAAI,KAAA,KAAU,KAAA;AAC9B,MAAA,uBACE,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UAEC,GAAA,EAAK,CAAC,EAAA,KAAO;AACX,YAAA,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA,GAAI,EAAA;AAAA,UACpB,CAAA;AAAA,UACA,IAAA,EAAK,QAAA;AAAA,UACL,IAAA,EAAK,OAAA;AAAA,UACL,cAAA,EAAc,OAAA;AAAA,UACd,cAAY,GAAA,CAAI,IAAA;AAAA,UAChB,OAAO,GAAA,CAAI,IAAA;AAAA,UACX,QAAA,EAAU,CAAA,KAAM,aAAA,GAAgB,CAAA,GAAI,EAAA;AAAA,UACpC,QAAA;AAAA,UACA,OAAA,EAAS,MAAM,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAAA,UACjC,SAAA,EAAW,CAAC,CAAA,KAAM,SAAA,CAAU,GAAG,CAAC,CAAA;AAAA,UAChC,SAAA,EAAW,CAAA,mOAAA,EACT,OAAA,GAAU,uCAAA,GAA0C,EACtD,CAAA,CAAA;AAAA,UACA,OAAO,GAAA,CAAI,KAAA,GAAQ,EAAE,eAAA,EAAiB,GAAA,CAAI,OAAM,GAAI,MAAA;AAAA,UAEnD,QAAA,EAAA,GAAA,CAAI,UAAU,IAAA,oBAAQ,GAAA,CAAC,OAAI,SAAA,EAAU,gCAAA,EAAiC,eAAW,IAAA,EAAC;AAAA,SAAA;AAAA,QAlB9E,GAAA,CAAI;AAAA,OAmBX;AAAA,IAEJ,CAAC,CAAA,EACH;AAAA,GAAA,EACF,CAAA;AAEJ","file":"ColorSwatchPicker.js","sourcesContent":["'use client';\n\nimport { useRef, type KeyboardEvent } from 'react';\nimport { Ban } from 'lucide-react';\n\n// ColorSwatchPicker — a keyboard-navigable radiogroup of curated status colors\n// (Story 2.2 · Subtask 2.2.8). Replaces the free-text hex input in the workflow\n// status form. The palette is drawn from the design-system semantic tokens\n// (globals.css) and stored as HEX — theme-independent and durable (a status's\n// color shouldn't shift if a CSS var is renamed). `null` = \"derive from\n// category\" (the Pill/category tint decides the swatch).\n//\n// A11y: WAI-ARIA radiogroup pattern — role=\"radiogroup\" + role=\"radio\"\n// children, `aria-checked`, roving tabindex (only the selected swatch is in the\n// tab order), arrow keys move + select, and a high-contrast focus/selected ring\n// that's independent of the swatch fill (so it reads on any color, AA-safe).\n\nexport interface ColorSwatchOption {\n /** Accessible name + tooltip. */\n name: string;\n /** The stored hex value, or null for \"derive from category\". */\n value: string | null;\n}\n\nexport const STATUS_COLOR_SWATCHES: ReadonlyArray<ColorSwatchOption> = [\n { name: 'None (derive from category)', value: null },\n { name: 'Grey', value: '#787671' },\n { name: 'Blue', value: '#0075de' },\n { name: 'Teal', value: '#2a9d99' },\n { name: 'Green', value: '#1aae39' },\n { name: 'Orange', value: '#dd5b00' },\n { name: 'Red', value: '#e03131' },\n { name: 'Pink', value: '#ff64c8' },\n];\n\nexport interface ColorSwatchPickerProps {\n /** Selected hex, or null for \"derive from category\". */\n value: string | null;\n onChange: (value: string | null) => void;\n label?: string;\n disabled?: boolean;\n}\n\nexport function ColorSwatchPicker({\n value,\n onChange,\n label = 'Color',\n disabled,\n}: ColorSwatchPickerProps) {\n const refs = useRef<Array<HTMLButtonElement | null>>([]);\n const selectedIndex = STATUS_COLOR_SWATCHES.findIndex((s) => s.value === value);\n // Roving tabindex anchor: the selected swatch, or the first when nothing matches\n // (e.g. a legacy custom hex outside the curated palette).\n const tabbableIndex = selectedIndex >= 0 ? selectedIndex : 0;\n\n function select(index: number) {\n const opt = STATUS_COLOR_SWATCHES[index];\n if (!opt) return;\n onChange(opt.value);\n refs.current[index]?.focus();\n }\n\n function onKeyDown(e: KeyboardEvent, index: number) {\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n e.preventDefault();\n select((index + 1) % STATUS_COLOR_SWATCHES.length);\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n e.preventDefault();\n select((index - 1 + STATUS_COLOR_SWATCHES.length) % STATUS_COLOR_SWATCHES.length);\n }\n }\n\n return (\n <div className=\"flex flex-col gap-1\">\n <span className=\"text-(--el-text) font-sans text-sm font-medium\">{label}</span>\n <div role=\"radiogroup\" aria-label={label} className=\"flex flex-wrap gap-2\">\n {STATUS_COLOR_SWATCHES.map((opt, i) => {\n const checked = opt.value === value;\n return (\n <button\n key={opt.name}\n ref={(el) => {\n refs.current[i] = el;\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n aria-label={opt.name}\n title={opt.name}\n tabIndex={i === tabbableIndex ? 0 : -1}\n disabled={disabled}\n onClick={() => onChange(opt.value)}\n onKeyDown={(e) => onKeyDown(e, i)}\n className={`flex h-7 w-7 items-center justify-center rounded-full border border-(--el-border) transition-shadow focus-visible:ring-(--el-text) focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:opacity-50 ${\n checked ? 'ring-(--el-text) ring-2 ring-offset-2' : ''\n }`}\n style={opt.value ? { backgroundColor: opt.value } : undefined}\n >\n {opt.value === null && <Ban className=\"text-(--el-text-muted) h-4 w-4\" aria-hidden />}\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n"]}
@@ -0,0 +1,84 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Combobox — an accessible select/combobox primitive (Subtask 2.3.4). A trigger
6
+ * button (`role="combobox"`, `aria-haspopup="listbox"`) opens an anchored panel
7
+ * holding an optional type-ahead filter and a `role="listbox"` of
8
+ * `role="option"` rows. The active option is tracked with `aria-activedescendant`
9
+ * on whichever control holds focus (the filter input when `searchable`, else the
10
+ * listbox) — the CommandPalette pattern that already clears the STRICT axe sweep.
11
+ *
12
+ * Deliberately NOT built on the Radix Popover primitive: that injects
13
+ * `aria-haspopup="dialog"` + dialog focus semantics onto its trigger, which
14
+ * conflicts with the listbox combobox pattern. This is a self-contained anchored
15
+ * dropdown (click-outside + Escape + focus return handled here) so the ARIA is
16
+ * exactly the WAI-ARIA combobox shape.
17
+ *
18
+ * Composed by `components/issues/TypePicker` (searchable=false, 5 options) and
19
+ * `ParentPicker` (searchable, async candidate list).
20
+ */
21
+ interface ComboboxOption<T extends string> {
22
+ value: T;
23
+ /** Primary text — also the accessible name of the option. */
24
+ label: string;
25
+ /** Extra text matched by the filter (e.g. a PROD-N identifier). */
26
+ keywords?: string;
27
+ /** Leading visual (a kind icon); decorative — label carries the name. */
28
+ icon?: ReactNode;
29
+ /** Trailing muted text (e.g. the identifier). */
30
+ secondary?: string;
31
+ /**
32
+ * Optional section label. When consecutive options carry different `group`
33
+ * values, a non-interactive header row is rendered at each transition (the
34
+ * advanced filter's "Fields" / "Custom fields" / "Other" field menu). Pass
35
+ * options pre-sorted by group; headers follow list order, so a group whose
36
+ * every option is filtered out disappears with it. Decorative — not part of
37
+ * the listbox's option indices, so keyboard nav is unaffected.
38
+ */
39
+ group?: string;
40
+ }
41
+ interface ComboboxProps<T extends string> {
42
+ options: ComboboxOption<T>[];
43
+ value: T | null;
44
+ onChange: (value: T) => void;
45
+ /** Accessible name for the trigger + listbox. */
46
+ label: string;
47
+ /** Trigger text when nothing is selected. */
48
+ placeholder?: string;
49
+ searchable?: boolean;
50
+ searchPlaceholder?: string;
51
+ emptyText?: string;
52
+ loading?: boolean;
53
+ loadingText?: string;
54
+ disabled?: boolean;
55
+ /** id for the trigger button. */
56
+ id?: string;
57
+ className?: string;
58
+ /**
59
+ * Mount already-open (Subtask 2.5.5) — for an inline-edit cell that renders the
60
+ * picker on a single click, so the menu is open immediately (mirrors
61
+ * `DatePicker`'s `autoOpen`). Focus lands on the search input / listbox.
62
+ */
63
+ autoOpen?: boolean;
64
+ /**
65
+ * Fired whenever the menu transitions open → closed (a pick, Escape,
66
+ * click-outside, or toggling the trigger). The inline-edit cell uses it to
67
+ * leave edit mode and return to the static pill/avatar (Subtask 2.5.5).
68
+ */
69
+ onClose?: () => void;
70
+ /**
71
+ * Server-driven search mode (Subtask 6.9.2 — the link picker). When
72
+ * `onQueryChange` is provided the type-ahead query is CONTROLLED by the parent
73
+ * (`query`) and client-side filtering is SKIPPED — `options` is taken as the
74
+ * server's authoritative result for the current query (the parent debounces
75
+ * its fetch off `onQueryChange`, the same contract as `MultiSelectPicker`).
76
+ * Omit both for the default self-filtering behaviour. The parent owns the
77
+ * query lifecycle, so opening the menu does NOT reset it.
78
+ */
79
+ query?: string;
80
+ onQueryChange?: (query: string) => void;
81
+ }
82
+ declare function Combobox<T extends string>({ options, value, onChange, label, placeholder, searchable, searchPlaceholder, emptyText, loading, loadingText, disabled, id, className, autoOpen, onClose, query: controlledQuery, onQueryChange, }: ComboboxProps<T>): react_jsx_runtime.JSX.Element;
83
+
84
+ export { Combobox, type ComboboxOption, type ComboboxProps };