@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.
- package/README.md +126 -0
- package/dist/appearance.d.ts +47 -0
- package/dist/appearance.js +3 -0
- package/dist/appearance.js.map +1 -0
- package/dist/components/theme/AppearancePickers.d.ts +97 -0
- package/dist/components/theme/AppearancePickers.js +542 -0
- package/dist/components/theme/AppearancePickers.js.map +1 -0
- package/dist/components/theme/HandDrawnFilter.d.ts +23 -0
- package/dist/components/theme/HandDrawnFilter.js +41 -0
- package/dist/components/theme/HandDrawnFilter.js.map +1 -0
- package/dist/components/theme/ImmersiveTilt.d.ts +3 -0
- package/dist/components/theme/ImmersiveTilt.js +113 -0
- package/dist/components/theme/ImmersiveTilt.js.map +1 -0
- package/dist/components/theme/StyleVignette.d.ts +60 -0
- package/dist/components/theme/StyleVignette.js +382 -0
- package/dist/components/theme/StyleVignette.js.map +1 -0
- package/dist/components/ui/Button.d.ts +32 -0
- package/dist/components/ui/Button.js +92 -0
- package/dist/components/ui/Button.js.map +1 -0
- package/dist/components/ui/Card.d.ts +33 -0
- package/dist/components/ui/Card.js +64 -0
- package/dist/components/ui/Card.js.map +1 -0
- package/dist/components/ui/ColorSwatchPicker.d.ts +19 -0
- package/dist/components/ui/ColorSwatchPicker.js +71 -0
- package/dist/components/ui/ColorSwatchPicker.js.map +1 -0
- package/dist/components/ui/Combobox.d.ts +84 -0
- package/dist/components/ui/Combobox.js +374 -0
- package/dist/components/ui/Combobox.js.map +1 -0
- package/dist/components/ui/EmptyState.d.ts +38 -0
- package/dist/components/ui/EmptyState.js +73 -0
- package/dist/components/ui/EmptyState.js.map +1 -0
- package/dist/components/ui/ErrorState.d.ts +43 -0
- package/dist/components/ui/ErrorState.js +169 -0
- package/dist/components/ui/ErrorState.js.map +1 -0
- package/dist/components/ui/FormField.d.ts +31 -0
- package/dist/components/ui/FormField.js +43 -0
- package/dist/components/ui/FormField.js.map +1 -0
- package/dist/components/ui/Input.d.ts +29 -0
- package/dist/components/ui/Input.js +105 -0
- package/dist/components/ui/Input.js.map +1 -0
- package/dist/components/ui/Modal.d.ts +84 -0
- package/dist/components/ui/Modal.js +153 -0
- package/dist/components/ui/Modal.js.map +1 -0
- package/dist/components/ui/MultiSelectPicker.d.ts +73 -0
- package/dist/components/ui/MultiSelectPicker.js +358 -0
- package/dist/components/ui/MultiSelectPicker.js.map +1 -0
- package/dist/components/ui/Pill.d.ts +49 -0
- package/dist/components/ui/Pill.js +107 -0
- package/dist/components/ui/Pill.js.map +1 -0
- package/dist/components/ui/Popover.d.ts +65 -0
- package/dist/components/ui/Popover.js +57 -0
- package/dist/components/ui/Popover.js.map +1 -0
- package/dist/components/ui/SectionLabel.d.ts +28 -0
- package/dist/components/ui/SectionLabel.js +27 -0
- package/dist/components/ui/SectionLabel.js.map +1 -0
- package/dist/components/ui/Segmented.d.ts +30 -0
- package/dist/components/ui/Segmented.js +65 -0
- package/dist/components/ui/Segmented.js.map +1 -0
- package/dist/components/ui/Spinner.d.ts +22 -0
- package/dist/components/ui/Spinner.js +39 -0
- package/dist/components/ui/Spinner.js.map +1 -0
- package/dist/components/ui/Switch.d.ts +34 -0
- package/dist/components/ui/Switch.js +50 -0
- package/dist/components/ui/Switch.js.map +1 -0
- package/dist/components/ui/Textarea.d.ts +19 -0
- package/dist/components/ui/Textarea.js +72 -0
- package/dist/components/ui/Textarea.js.map +1 -0
- package/dist/components/ui/Toast.d.ts +45 -0
- package/dist/components/ui/Toast.js +116 -0
- package/dist/components/ui/Toast.js.map +1 -0
- package/dist/components/ui/Tooltip.d.ts +25 -0
- package/dist/components/ui/Tooltip.js +43 -0
- package/dist/components/ui/Tooltip.js.map +1 -0
- package/dist/contexts/theme-context.d.ts +95 -0
- package/dist/contexts/theme-context.js +561 -0
- package/dist/contexts/theme-context.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +67 -0
- package/dist/specimen/TokensSpecimen.d.ts +13 -0
- package/dist/specimen/TokensSpecimen.js +1090 -0
- package/dist/specimen/TokensSpecimen.js.map +1 -0
- package/dist/theme/appearance-resolution.d.ts +59 -0
- package/dist/theme/appearance-resolution.js +379 -0
- package/dist/theme/appearance-resolution.js.map +1 -0
- package/dist/theme/init-script.d.ts +48 -0
- package/dist/theme/init-script.js +391 -0
- package/dist/theme/init-script.js.map +1 -0
- package/dist/theme/palettes.d.ts +146 -0
- package/dist/theme/palettes.js +85 -0
- package/dist/theme/palettes.js.map +1 -0
- package/dist/theme/styles.d.ts +351 -0
- package/dist/theme/styles.js +264 -0
- package/dist/theme/styles.js.map +1 -0
- package/dist/theme/tilt.d.ts +41 -0
- package/dist/theme/tilt.js +18 -0
- package/dist/theme/tilt.js.map +1 -0
- package/dist/theme/types.d.ts +61 -0
- package/dist/theme/types.js +234 -0
- package/dist/theme/types.js.map +1 -0
- package/dist/theme/typography.d.ts +144 -0
- package/dist/theme/typography.js +57 -0
- package/dist/theme/typography.js.map +1 -0
- package/dist/utils/cn.d.ts +16 -0
- package/dist/utils/cn.js +11 -0
- package/dist/utils/cn.js.map +1 -0
- package/package.json +60 -0
- 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":"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","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 };
|