@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,169 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
2
|
+
import { AlertTriangle } from 'lucide-react';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { clsx } from 'clsx';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
// src/components/ui/ErrorState.tsx
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return twMerge(clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
var spinnerVariants = cva(
|
|
13
|
+
"inline-block animate-spin rounded-full border-current border-t-transparent",
|
|
14
|
+
{
|
|
15
|
+
variants: {
|
|
16
|
+
size: {
|
|
17
|
+
sm: "h-4 w-4 border-[1.5px]",
|
|
18
|
+
md: "h-5 w-5 border-2",
|
|
19
|
+
lg: "h-8 w-8 border-[3px]"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: { size: "md" }
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
var Spinner = forwardRef(function Spinner2({ className, size, ...rest }, ref) {
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
"span",
|
|
28
|
+
{
|
|
29
|
+
ref,
|
|
30
|
+
role: "status",
|
|
31
|
+
"aria-label": rest["aria-label"] ?? "Loading",
|
|
32
|
+
className: cn(spinnerVariants({ size }), className),
|
|
33
|
+
...rest
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
var buttonVariants = cva(
|
|
38
|
+
cn(
|
|
39
|
+
// Base layout
|
|
40
|
+
"inline-flex items-center justify-center gap-2",
|
|
41
|
+
// Typography
|
|
42
|
+
"font-sans text-sm font-medium leading-none",
|
|
43
|
+
// Shape — semantic tokens that flip with the active style
|
|
44
|
+
"rounded-(--radius-btn)",
|
|
45
|
+
// Interaction
|
|
46
|
+
"transition-[transform,background-color,border-color,color] duration-(--transition-duration)",
|
|
47
|
+
"active:scale-(--active-scale)",
|
|
48
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--focus-ring-color) focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
49
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
50
|
+
),
|
|
51
|
+
{
|
|
52
|
+
variants: {
|
|
53
|
+
variant: {
|
|
54
|
+
primary: "bg-(--el-accent) text-(--el-accent-text) hover:opacity-90",
|
|
55
|
+
secondary: "bg-transparent text-(--el-text) border border-(--el-button-border) hover:bg-(--el-surface)",
|
|
56
|
+
ghost: "bg-transparent text-(--el-text) hover:bg-(--el-surface)",
|
|
57
|
+
danger: "bg-(--el-danger) text-(--el-danger-text) hover:opacity-90"
|
|
58
|
+
},
|
|
59
|
+
size: {
|
|
60
|
+
sm: "h-(--height-btn-sm) px-3 text-xs",
|
|
61
|
+
md: "h-(--height-btn-md) px-(--spacing-btn-x)",
|
|
62
|
+
lg: "h-(--height-btn-lg) px-6 text-base"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
defaultVariants: {
|
|
66
|
+
variant: "primary",
|
|
67
|
+
size: "md"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
var Button = forwardRef(function Button2({ variant, size, loading, leftIcon, rightIcon, disabled, className, children, ...rest }, ref) {
|
|
72
|
+
return /* @__PURE__ */ jsxs(
|
|
73
|
+
"button",
|
|
74
|
+
{
|
|
75
|
+
ref,
|
|
76
|
+
type: "button",
|
|
77
|
+
"data-variant": variant ?? "primary",
|
|
78
|
+
className: cn(buttonVariants({ variant, size }), className),
|
|
79
|
+
disabled: disabled || loading,
|
|
80
|
+
"aria-busy": loading || void 0,
|
|
81
|
+
...rest,
|
|
82
|
+
children: [
|
|
83
|
+
loading ? /* @__PURE__ */ jsx(Spinner, { size: size === "lg" ? "md" : "sm", "aria-hidden": true }) : leftIcon ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "inline-flex", children: leftIcon }) : null,
|
|
84
|
+
/* @__PURE__ */ jsx("span", { children }),
|
|
85
|
+
!loading && rightIcon ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "inline-flex", children: rightIcon }) : null
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
var cardVariants = cva(
|
|
91
|
+
cn(
|
|
92
|
+
"rounded-(--radius-card) border border-(--el-border)",
|
|
93
|
+
"p-(--spacing-card-padding)",
|
|
94
|
+
"transition-shadow duration-(--transition-duration)"
|
|
95
|
+
),
|
|
96
|
+
{
|
|
97
|
+
variants: {
|
|
98
|
+
tint: {
|
|
99
|
+
none: "bg-(--el-card)",
|
|
100
|
+
peach: "bg-(--el-tint-peach) border-transparent",
|
|
101
|
+
rose: "bg-(--el-tint-rose) border-transparent",
|
|
102
|
+
mint: "bg-(--el-tint-mint) border-transparent",
|
|
103
|
+
lavender: "bg-(--el-tint-lavender) border-transparent",
|
|
104
|
+
sky: "bg-(--el-tint-sky) border-transparent",
|
|
105
|
+
yellow: "bg-(--el-tint-yellow) border-transparent"
|
|
106
|
+
},
|
|
107
|
+
clickable: {
|
|
108
|
+
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",
|
|
109
|
+
false: ""
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
defaultVariants: { tint: "none", clickable: false }
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
var Card = forwardRef(function Card2({ tint, clickable, header, footer, className, children, ...rest }, ref) {
|
|
116
|
+
return /* @__PURE__ */ jsxs(
|
|
117
|
+
"div",
|
|
118
|
+
{
|
|
119
|
+
ref,
|
|
120
|
+
className: cn(cardVariants({ tint, clickable }), className),
|
|
121
|
+
"data-tilt": "",
|
|
122
|
+
...tint && tint !== "none" ? {} : { "data-surface": "card" },
|
|
123
|
+
...clickable ? { tabIndex: 0, role: "button" } : {},
|
|
124
|
+
...rest,
|
|
125
|
+
children: [
|
|
126
|
+
header ? /* @__PURE__ */ jsx("div", { "data-tilt-layer": "front", className: "mb-(--spacing-md)", children: header }) : null,
|
|
127
|
+
/* @__PURE__ */ jsx("div", { children }),
|
|
128
|
+
footer ? /* @__PURE__ */ jsx(
|
|
129
|
+
"div",
|
|
130
|
+
{
|
|
131
|
+
"data-tilt-layer": "back",
|
|
132
|
+
className: "border-(--el-border) mt-(--spacing-md) border-t pt-(--spacing-md)",
|
|
133
|
+
children: footer
|
|
134
|
+
}
|
|
135
|
+
) : null
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
var ErrorState = forwardRef(function ErrorState2({ title, description, error, retry, className, retryLabel = "Try again", ...rest }, ref) {
|
|
141
|
+
const showErrorDetail = error && process.env.NODE_ENV !== "production";
|
|
142
|
+
return /* @__PURE__ */ jsxs(
|
|
143
|
+
Card,
|
|
144
|
+
{
|
|
145
|
+
ref,
|
|
146
|
+
role: "alert",
|
|
147
|
+
className: cn("flex flex-col items-center text-center", className),
|
|
148
|
+
...rest,
|
|
149
|
+
children: [
|
|
150
|
+
/* @__PURE__ */ jsx(
|
|
151
|
+
"div",
|
|
152
|
+
{
|
|
153
|
+
className: "mb-(--spacing-md) inline-flex h-12 w-12 items-center justify-center",
|
|
154
|
+
style: { color: "var(--el-danger)" },
|
|
155
|
+
children: /* @__PURE__ */ jsx(AlertTriangle, { className: "h-12 w-12", "aria-hidden": true })
|
|
156
|
+
}
|
|
157
|
+
),
|
|
158
|
+
/* @__PURE__ */ jsx("h2", { className: "font-serif text-xl text-(--el-text)", children: title }),
|
|
159
|
+
description ? /* @__PURE__ */ jsx("p", { className: "text-(--el-text-muted) mt-(--spacing-sm) max-w-prose font-sans text-sm", children: description }) : null,
|
|
160
|
+
showErrorDetail ? /* @__PURE__ */ jsx("pre", { className: "bg-(--el-surface) text-(--el-text-muted) mt-(--spacing-md) max-w-full overflow-x-auto rounded-(--radius-control) px-(--spacing-tooltip-x) py-(--spacing-tooltip-y) text-left font-mono text-xs", children: error.message }) : null,
|
|
161
|
+
retry ? /* @__PURE__ */ jsx("div", { className: "mt-(--spacing-md)", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: retry, children: retryLabel }) }) : null
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
export { ErrorState };
|
|
168
|
+
//# sourceMappingURL=ErrorState.js.map
|
|
169
|
+
//# sourceMappingURL=ErrorState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/Spinner.tsx","../../../src/components/ui/Button.tsx","../../../src/components/ui/Card.tsx","../../../src/components/ui/ErrorState.tsx"],"names":["Spinner","cva","forwardRef","Button","jsx","Card","jsxs","ErrorState"],"mappings":"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","file":"ErrorState.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 * Spinner — pure CSS rotation indicator.\n *\n * Three sizes; works standalone or inside a Button's loading state.\n *\n * @example\n * <Spinner size=\"md\" />\n * <Spinner size=\"sm\" aria-label=\"Loading\" />\n */\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-t-transparent',\n {\n variants: {\n size: {\n sm: 'h-4 w-4 border-[1.5px]',\n md: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nexport interface SpinnerProps\n extends Omit<HTMLAttributes<HTMLSpanElement>, 'role'>, VariantProps<typeof spinnerVariants> {}\n\nexport const Spinner = forwardRef<HTMLSpanElement, SpinnerProps>(function Spinner(\n { className, size, ...rest },\n ref,\n) {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-label={rest['aria-label'] ?? 'Loading'}\n className={cn(spinnerVariants({ size }), className)}\n {...rest}\n />\n );\n});\n","import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Spinner } from './Spinner';\nimport { cn } from '../../utils/cn';\n\n/**\n * Button — primary interactive primitive.\n *\n * Variants × sizes × states. The `loading` state shows an inline Spinner\n * and disables the button. Use semantic Tailwind token classes; never\n * hardcode colors. Shape responds to `data-style` via\n * `--radius-btn` (rectangles in default, pills in `soft`).\n *\n * @example\n * <Button variant=\"primary\" size=\"md\">Save</Button>\n * <Button variant=\"ghost\" leftIcon={<Plus />} loading>Saving…</Button>\n */\n// Exported so a Next `<Link>` can render AS a styled button (an anchor styled\n// with the button variants — keeps real-link semantics for navigation) without\n// nesting a <button> inside an <a>. Used by the /ready empty state's \"View all\n// issues\" link (Subtask 7.0.6).\nexport const buttonVariants = cva(\n cn(\n // Base layout\n 'inline-flex items-center justify-center gap-2',\n // Typography\n 'font-sans text-sm font-medium leading-none',\n // Shape — semantic tokens that flip with the active style\n 'rounded-(--radius-btn)',\n // Interaction\n 'transition-[transform,background-color,border-color,color] duration-(--transition-duration)',\n 'active:scale-(--active-scale)',\n '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 'disabled:pointer-events-none disabled:opacity-50',\n ),\n {\n variants: {\n variant: {\n primary: 'bg-(--el-accent) text-(--el-accent-text) hover:opacity-90',\n secondary:\n 'bg-transparent text-(--el-text) border border-(--el-button-border) hover:bg-(--el-surface)',\n ghost: 'bg-transparent text-(--el-text) hover:bg-(--el-surface)',\n danger: 'bg-(--el-danger) text-(--el-danger-text) hover:opacity-90',\n },\n size: {\n sm: 'h-(--height-btn-sm) px-3 text-xs',\n md: 'h-(--height-btn-md) px-(--spacing-btn-x)',\n lg: 'h-(--height-btn-lg) px-6 text-base',\n },\n },\n defaultVariants: {\n variant: 'primary',\n size: 'md',\n },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {\n /** Shows inline Spinner + disables interaction. */\n loading?: boolean;\n /** Icon rendered before the label. */\n leftIcon?: ReactNode;\n /** Icon rendered after the label. */\n rightIcon?: ReactNode;\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n { variant, size, loading, leftIcon, rightIcon, disabled, className, children, ...rest },\n ref,\n) {\n return (\n <button\n ref={ref}\n type=\"button\"\n // `data-variant` is the hook the 3D / Immersive style uses to give FILLED\n // buttons physical thickness (a solid palette-derived base edge) + a\n // press-DOWN (7.3.39). Inert under every other style.\n data-variant={variant ?? 'primary'}\n className={cn(buttonVariants({ variant, size }), className)}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? (\n <Spinner size={size === 'lg' ? 'md' : 'sm'} aria-hidden />\n ) : leftIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {leftIcon}\n </span>\n ) : null}\n <span>{children}</span>\n {!loading && rightIcon ? (\n <span aria-hidden className=\"inline-flex\">\n {rightIcon}\n </span>\n ) : null}\n </button>\n );\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","import { forwardRef, type HTMLAttributes } from 'react';\nimport { AlertTriangle } from 'lucide-react';\nimport { Button } from './Button';\nimport { Card } from './Card';\nimport { cn } from '../../utils/cn';\n\n/**\n * ErrorState — composed pattern for \"something failed\" screens.\n *\n * Use this whenever a request, mutation, or background job has failed in a\n * way the user needs to acknowledge or recover from. Prefer this over a\n * silent log line whenever the failure prevents the user from progressing.\n *\n * Has `role=\"alert\"` so screen readers announce the failure when it's\n * inserted into the DOM. The `error.message` debug detail renders only in\n * non-production builds (Next.js statically replaces `process.env.NODE_ENV`\n * at build time, so the block is dead-code-eliminated in production).\n *\n * @example\n * <ErrorState\n * title=\"Couldn't load workspace\"\n * description=\"We couldn't reach the server. Check your connection and try again.\"\n * error={err}\n * retry={() => refetch()}\n * />\n */\nexport interface ErrorStateProps extends HTMLAttributes<HTMLDivElement> {\n /** Short, plain-language summary of what failed. Required. */\n title: string;\n /** Recovery hint or context. Avoid restating the title. */\n description?: string;\n /** Real Error object — rendered as a mono code block in dev only. */\n error?: Error;\n /** Callback for the \"Try again\" button; omit to hide the button. */\n retry?: () => void;\n /**\n * Label for the retry button. Defaults to `'Try again'`. i18n is the consuming\n * app's concern — the design-system package is framework-agnostic (no\n * `next-intl` dependency), so a localized app passes the translated string\n * (motir-core wires it from its `common.retry`).\n */\n retryLabel?: string;\n}\n\nexport const ErrorState = forwardRef<HTMLDivElement, ErrorStateProps>(function ErrorState(\n { title, description, error, retry, className, retryLabel = 'Try again', ...rest },\n ref,\n) {\n const showErrorDetail = error && process.env.NODE_ENV !== 'production';\n return (\n <Card\n ref={ref}\n role=\"alert\"\n className={cn('flex flex-col items-center text-center', className)}\n {...rest}\n >\n <div\n className=\"mb-(--spacing-md) inline-flex h-12 w-12 items-center justify-center\"\n style={{ color: 'var(--el-danger)' }}\n >\n <AlertTriangle className=\"h-12 w-12\" aria-hidden />\n </div>\n <h2 className=\"font-serif text-xl text-(--el-text)\">{title}</h2>\n {description ? (\n <p className=\"text-(--el-text-muted) mt-(--spacing-sm) max-w-prose font-sans text-sm\">\n {description}\n </p>\n ) : null}\n {showErrorDetail ? (\n <pre className=\"bg-(--el-surface) text-(--el-text-muted) mt-(--spacing-md) max-w-full overflow-x-auto rounded-(--radius-control) px-(--spacing-tooltip-x) py-(--spacing-tooltip-y) text-left font-mono text-xs\">\n {error.message}\n </pre>\n ) : null}\n {retry ? (\n <div className=\"mt-(--spacing-md)\">\n <Button variant=\"secondary\" onClick={retry}>\n {retryLabel}\n </Button>\n </div>\n ) : null}\n </Card>\n );\n});\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Internal layout wrapper shared by Input and Textarea — handles the
|
|
6
|
+
* label / control / helper-or-error stack pattern. Not exported from
|
|
7
|
+
* the design system; consumers compose <Input> and <Textarea> directly.
|
|
8
|
+
*/
|
|
9
|
+
interface FormFieldProps {
|
|
10
|
+
/** Visible label rendered above the control. */
|
|
11
|
+
label?: string;
|
|
12
|
+
/** Error message — overrides helperText when present. Sets aria-invalid. */
|
|
13
|
+
error?: string;
|
|
14
|
+
/**
|
|
15
|
+
* How the error message renders: the default `text` is the quiet danger
|
|
16
|
+
* line; `box` is the rose-tint inline-error box (hue in the background,
|
|
17
|
+
* strong text — finding #35), the 2.4.9 grammar inline editors use.
|
|
18
|
+
*/
|
|
19
|
+
errorVariant?: 'text' | 'box';
|
|
20
|
+
/** Helper text rendered below the control. */
|
|
21
|
+
helperText?: string;
|
|
22
|
+
/** id of the control element — used to associate label + describedby. */
|
|
23
|
+
htmlFor: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
children: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
declare function FormField({ label, error, errorVariant, helperText, htmlFor, className, children, }: FormFieldProps): react_jsx_runtime.JSX.Element;
|
|
28
|
+
/** Returns the aria-describedby id that consumers should pass to the control. */
|
|
29
|
+
declare function describedById(htmlFor: string, error: string | undefined, helperText: string | undefined): string | undefined;
|
|
30
|
+
|
|
31
|
+
export { FormField, type FormFieldProps, describedById };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// src/utils/cn.ts
|
|
6
|
+
function cn(...inputs) {
|
|
7
|
+
return twMerge(clsx(inputs));
|
|
8
|
+
}
|
|
9
|
+
function FormField({
|
|
10
|
+
label,
|
|
11
|
+
error,
|
|
12
|
+
errorVariant = "text",
|
|
13
|
+
helperText,
|
|
14
|
+
htmlFor,
|
|
15
|
+
className,
|
|
16
|
+
children
|
|
17
|
+
}) {
|
|
18
|
+
const describedById2 = error ? `${htmlFor}-error` : helperText ? `${htmlFor}-helper` : void 0;
|
|
19
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
20
|
+
label ? /* @__PURE__ */ jsx("label", { htmlFor, className: "font-sans text-sm font-medium text-(--el-text)", children: label }) : null,
|
|
21
|
+
children,
|
|
22
|
+
error ? /* @__PURE__ */ jsx(
|
|
23
|
+
"p",
|
|
24
|
+
{
|
|
25
|
+
id: describedById2,
|
|
26
|
+
role: "alert",
|
|
27
|
+
className: cn(
|
|
28
|
+
"font-sans text-xs",
|
|
29
|
+
errorVariant === "box" && "bg-(--el-danger-surface) text-(--el-danger-surface-text) rounded-(--radius-control) px-(--spacing-tooltip-x) py-(--spacing-tooltip-y)"
|
|
30
|
+
),
|
|
31
|
+
style: errorVariant === "box" ? void 0 : { color: "var(--el-danger)" },
|
|
32
|
+
children: error
|
|
33
|
+
}
|
|
34
|
+
) : helperText ? /* @__PURE__ */ jsx("p", { id: describedById2, className: "text-(--el-text-helper) font-sans text-xs", children: helperText }) : null
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
function describedById(htmlFor, error, helperText) {
|
|
38
|
+
return error ? `${htmlFor}-error` : helperText ? `${htmlFor}-helper` : void 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { FormField, describedById };
|
|
42
|
+
//# sourceMappingURL=FormField.js.map
|
|
43
|
+
//# sourceMappingURL=FormField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/FormField.tsx"],"names":["describedById"],"mappings":";;;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACWO,SAAS,SAAA,CAAU;AAAA,EACxB,KAAA;AAAA,EACA,KAAA;AAAA,EACA,YAAA,GAAe,MAAA;AAAA,EACf,UAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAmB;AACjB,EAAA,MAAMA,cAAAA,GAAgB,QAAQ,CAAA,EAAG,OAAO,WAAW,UAAA,GAAa,CAAA,EAAG,OAAO,CAAA,OAAA,CAAA,GAAY,MAAA;AACtF,EAAA,4BACG,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,uBAAA,EAAyB,SAAS,CAAA,EAClD,QAAA,EAAA;AAAA,IAAA,KAAA,uBACE,OAAA,EAAA,EAAM,OAAA,EAAkB,SAAA,EAAU,gDAAA,EAChC,iBACH,CAAA,GACE,IAAA;AAAA,IACH,QAAA;AAAA,IACA,KAAA,mBACC,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAIA,cAAAA;AAAA,QACJ,IAAA,EAAK,OAAA;AAAA,QACL,SAAA,EAAW,EAAA;AAAA,UACT,mBAAA;AAAA,UACA,iBAAiB,KAAA,IACf;AAAA,SACJ;AAAA,QACA,OAAO,YAAA,KAAiB,KAAA,GAAQ,MAAA,GAAY,EAAE,OAAO,kBAAA,EAAmB;AAAA,QAEvE,QAAA,EAAA;AAAA;AAAA,KACH,GACE,6BACF,GAAA,CAAC,GAAA,EAAA,EAAE,IAAIA,cAAAA,EAAe,SAAA,EAAU,2CAAA,EAC7B,QAAA,EAAA,UAAA,EACH,CAAA,GACE;AAAA,GAAA,EACN,CAAA;AAEJ;AAGO,SAAS,aAAA,CACd,OAAA,EACA,KAAA,EACA,UAAA,EACoB;AACpB,EAAA,OAAO,QAAQ,CAAA,EAAG,OAAO,WAAW,UAAA,GAAa,CAAA,EAAG,OAAO,CAAA,OAAA,CAAA,GAAY,MAAA;AACzE","file":"FormField.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 { type ReactNode } from 'react';\nimport { cn } from '../../utils/cn';\n\n/**\n * Internal layout wrapper shared by Input and Textarea — handles the\n * label / control / helper-or-error stack pattern. Not exported from\n * the design system; consumers compose <Input> and <Textarea> directly.\n */\nexport interface FormFieldProps {\n /** Visible label rendered above the control. */\n label?: string;\n /** Error message — overrides helperText when present. Sets aria-invalid. */\n error?: string;\n /**\n * How the error message renders: the default `text` is the quiet danger\n * line; `box` is the rose-tint inline-error box (hue in the background,\n * strong text — finding #35), the 2.4.9 grammar inline editors use.\n */\n errorVariant?: 'text' | 'box';\n /** Helper text rendered below the control. */\n helperText?: string;\n /** id of the control element — used to associate label + describedby. */\n htmlFor: string;\n className?: string;\n children: ReactNode;\n}\n\nexport function FormField({\n label,\n error,\n errorVariant = 'text',\n helperText,\n htmlFor,\n className,\n children,\n}: FormFieldProps) {\n const describedById = error ? `${htmlFor}-error` : helperText ? `${htmlFor}-helper` : undefined;\n return (\n <div className={cn('flex flex-col gap-1.5', className)}>\n {label ? (\n <label htmlFor={htmlFor} className=\"font-sans text-sm font-medium text-(--el-text)\">\n {label}\n </label>\n ) : null}\n {children}\n {error ? (\n <p\n id={describedById}\n role=\"alert\"\n className={cn(\n 'font-sans text-xs',\n errorVariant === 'box' &&\n 'bg-(--el-danger-surface) text-(--el-danger-surface-text) rounded-(--radius-control) px-(--spacing-tooltip-x) py-(--spacing-tooltip-y)',\n )}\n style={errorVariant === 'box' ? undefined : { color: 'var(--el-danger)' }}\n >\n {error}\n </p>\n ) : helperText ? (\n <p id={describedById} className=\"text-(--el-text-helper) font-sans text-xs\">\n {helperText}\n </p>\n ) : null}\n </div>\n );\n}\n\n/** Returns the aria-describedby id that consumers should pass to the control. */\nexport function describedById(\n htmlFor: string,\n error: string | undefined,\n helperText: string | undefined,\n): string | undefined {\n return error ? `${htmlFor}-error` : helperText ? `${htmlFor}-helper` : undefined;\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { InputHTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Input — single-line text field with optional label, helper text, error
|
|
6
|
+
* state, and addon slots (e.g. icons or labels).
|
|
7
|
+
*
|
|
8
|
+
* Note: `addonStart`/`addonEnd` are used instead of `prefix`/`suffix` because
|
|
9
|
+
* `prefix` is a reserved HTML attribute on <input> (string-typed) and would
|
|
10
|
+
* collide with our ReactNode prop.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <Input label="Email" type="email" helperText="We'll never share it" />
|
|
14
|
+
* <Input label="Domain" addonStart={<Globe />} addonEnd=".motir.dev" />
|
|
15
|
+
*/
|
|
16
|
+
interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
|
|
17
|
+
label?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
/** Error presentation — `text` (default) or the rose-tint `box` (see FormField). */
|
|
20
|
+
errorVariant?: 'text' | 'box';
|
|
21
|
+
helperText?: string;
|
|
22
|
+
/** Rendered inside the input box on the left. */
|
|
23
|
+
addonStart?: ReactNode;
|
|
24
|
+
/** Rendered inside the input box on the right. */
|
|
25
|
+
addonEnd?: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
|
|
28
|
+
|
|
29
|
+
export { Input, type InputProps };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { forwardRef, useId } from 'react';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/components/ui/Input.tsx
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
function FormField({
|
|
11
|
+
label,
|
|
12
|
+
error,
|
|
13
|
+
errorVariant = "text",
|
|
14
|
+
helperText,
|
|
15
|
+
htmlFor,
|
|
16
|
+
className,
|
|
17
|
+
children
|
|
18
|
+
}) {
|
|
19
|
+
const describedById2 = error ? `${htmlFor}-error` : helperText ? `${htmlFor}-helper` : void 0;
|
|
20
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
21
|
+
label ? /* @__PURE__ */ jsx("label", { htmlFor, className: "font-sans text-sm font-medium text-(--el-text)", children: label }) : null,
|
|
22
|
+
children,
|
|
23
|
+
error ? /* @__PURE__ */ jsx(
|
|
24
|
+
"p",
|
|
25
|
+
{
|
|
26
|
+
id: describedById2,
|
|
27
|
+
role: "alert",
|
|
28
|
+
className: cn(
|
|
29
|
+
"font-sans text-xs",
|
|
30
|
+
errorVariant === "box" && "bg-(--el-danger-surface) text-(--el-danger-surface-text) rounded-(--radius-control) px-(--spacing-tooltip-x) py-(--spacing-tooltip-y)"
|
|
31
|
+
),
|
|
32
|
+
style: errorVariant === "box" ? void 0 : { color: "var(--el-danger)" },
|
|
33
|
+
children: error
|
|
34
|
+
}
|
|
35
|
+
) : helperText ? /* @__PURE__ */ jsx("p", { id: describedById2, className: "text-(--el-text-helper) font-sans text-xs", children: helperText }) : null
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
function describedById(htmlFor, error, helperText) {
|
|
39
|
+
return error ? `${htmlFor}-error` : helperText ? `${htmlFor}-helper` : void 0;
|
|
40
|
+
}
|
|
41
|
+
var Input = forwardRef(function Input2({
|
|
42
|
+
label,
|
|
43
|
+
error,
|
|
44
|
+
errorVariant,
|
|
45
|
+
helperText,
|
|
46
|
+
addonStart,
|
|
47
|
+
addonEnd,
|
|
48
|
+
id,
|
|
49
|
+
className,
|
|
50
|
+
disabled,
|
|
51
|
+
...rest
|
|
52
|
+
}, ref) {
|
|
53
|
+
const autoId = useId();
|
|
54
|
+
const inputId = id ?? autoId;
|
|
55
|
+
const describedBy = describedById(inputId, error, helperText);
|
|
56
|
+
const hasError = Boolean(error);
|
|
57
|
+
return /* @__PURE__ */ jsx(
|
|
58
|
+
FormField,
|
|
59
|
+
{
|
|
60
|
+
label,
|
|
61
|
+
error,
|
|
62
|
+
errorVariant,
|
|
63
|
+
helperText,
|
|
64
|
+
htmlFor: inputId,
|
|
65
|
+
children: /* @__PURE__ */ jsxs(
|
|
66
|
+
"div",
|
|
67
|
+
{
|
|
68
|
+
"data-surface": "input",
|
|
69
|
+
className: cn(
|
|
70
|
+
"flex h-(--height-input) w-full items-center gap-2 rounded-(--radius-input) border bg-(--el-page-bg)",
|
|
71
|
+
"px-(--spacing-input-x)",
|
|
72
|
+
"transition-colors",
|
|
73
|
+
"focus-within:ring-2 focus-within:ring-(--focus-ring-color) focus-within:ring-offset-2 focus-within:ring-offset-background",
|
|
74
|
+
hasError ? "border-(--el-danger)" : "border-(--el-input-border)",
|
|
75
|
+
disabled && "cursor-not-allowed opacity-50"
|
|
76
|
+
),
|
|
77
|
+
children: [
|
|
78
|
+
addonStart ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-(--el-icon-field) inline-flex", children: addonStart }) : null,
|
|
79
|
+
/* @__PURE__ */ jsx(
|
|
80
|
+
"input",
|
|
81
|
+
{
|
|
82
|
+
ref,
|
|
83
|
+
id: inputId,
|
|
84
|
+
"aria-invalid": hasError || void 0,
|
|
85
|
+
"aria-describedby": describedBy,
|
|
86
|
+
disabled,
|
|
87
|
+
className: cn(
|
|
88
|
+
"flex-1 bg-transparent font-sans text-sm text-(--el-text) outline-none placeholder:text-(--el-text-muted)",
|
|
89
|
+
"disabled:cursor-not-allowed",
|
|
90
|
+
className
|
|
91
|
+
),
|
|
92
|
+
...rest
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
addonEnd ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-(--el-icon-field) inline-flex", children: addonEnd }) : null
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
export { Input };
|
|
104
|
+
//# sourceMappingURL=Input.js.map
|
|
105
|
+
//# sourceMappingURL=Input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/components/ui/FormField.tsx","../../../src/components/ui/Input.tsx"],"names":["describedById","Input","jsx","jsxs"],"mappings":"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","file":"Input.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 { type ReactNode } from 'react';\nimport { cn } from '../../utils/cn';\n\n/**\n * Internal layout wrapper shared by Input and Textarea — handles the\n * label / control / helper-or-error stack pattern. Not exported from\n * the design system; consumers compose <Input> and <Textarea> directly.\n */\nexport interface FormFieldProps {\n /** Visible label rendered above the control. */\n label?: string;\n /** Error message — overrides helperText when present. Sets aria-invalid. */\n error?: string;\n /**\n * How the error message renders: the default `text` is the quiet danger\n * line; `box` is the rose-tint inline-error box (hue in the background,\n * strong text — finding #35), the 2.4.9 grammar inline editors use.\n */\n errorVariant?: 'text' | 'box';\n /** Helper text rendered below the control. */\n helperText?: string;\n /** id of the control element — used to associate label + describedby. */\n htmlFor: string;\n className?: string;\n children: ReactNode;\n}\n\nexport function FormField({\n label,\n error,\n errorVariant = 'text',\n helperText,\n htmlFor,\n className,\n children,\n}: FormFieldProps) {\n const describedById = error ? `${htmlFor}-error` : helperText ? `${htmlFor}-helper` : undefined;\n return (\n <div className={cn('flex flex-col gap-1.5', className)}>\n {label ? (\n <label htmlFor={htmlFor} className=\"font-sans text-sm font-medium text-(--el-text)\">\n {label}\n </label>\n ) : null}\n {children}\n {error ? (\n <p\n id={describedById}\n role=\"alert\"\n className={cn(\n 'font-sans text-xs',\n errorVariant === 'box' &&\n 'bg-(--el-danger-surface) text-(--el-danger-surface-text) rounded-(--radius-control) px-(--spacing-tooltip-x) py-(--spacing-tooltip-y)',\n )}\n style={errorVariant === 'box' ? undefined : { color: 'var(--el-danger)' }}\n >\n {error}\n </p>\n ) : helperText ? (\n <p id={describedById} className=\"text-(--el-text-helper) font-sans text-xs\">\n {helperText}\n </p>\n ) : null}\n </div>\n );\n}\n\n/** Returns the aria-describedby id that consumers should pass to the control. */\nexport function describedById(\n htmlFor: string,\n error: string | undefined,\n helperText: string | undefined,\n): string | undefined {\n return error ? `${htmlFor}-error` : helperText ? `${htmlFor}-helper` : undefined;\n}\n","import { forwardRef, useId, type InputHTMLAttributes, type ReactNode } from 'react';\nimport { FormField, describedById } from './FormField';\nimport { cn } from '../../utils/cn';\n\n/**\n * Input — single-line text field with optional label, helper text, error\n * state, and addon slots (e.g. icons or labels).\n *\n * Note: `addonStart`/`addonEnd` are used instead of `prefix`/`suffix` because\n * `prefix` is a reserved HTML attribute on <input> (string-typed) and would\n * collide with our ReactNode prop.\n *\n * @example\n * <Input label=\"Email\" type=\"email\" helperText=\"We'll never share it\" />\n * <Input label=\"Domain\" addonStart={<Globe />} addonEnd=\".motir.dev\" />\n */\nexport interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'prefix'> {\n label?: string;\n error?: string;\n /** Error presentation — `text` (default) or the rose-tint `box` (see FormField). */\n errorVariant?: 'text' | 'box';\n helperText?: string;\n /** Rendered inside the input box on the left. */\n addonStart?: ReactNode;\n /** Rendered inside the input box on the right. */\n addonEnd?: ReactNode;\n}\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(function Input(\n {\n label,\n error,\n errorVariant,\n helperText,\n addonStart,\n addonEnd,\n id,\n className,\n disabled,\n ...rest\n },\n ref,\n) {\n const autoId = useId();\n const inputId = id ?? autoId;\n const describedBy = describedById(inputId, error, helperText);\n const hasError = Boolean(error);\n return (\n <FormField\n label={label}\n error={error}\n errorVariant={errorVariant}\n helperText={helperText}\n htmlFor={inputId}\n >\n <div\n // `data-surface` lets a surface-material style (glassmorphism) give the\n // control a translucent fill — see globals.css's material layer.\n data-surface=\"input\"\n className={cn(\n 'flex h-(--height-input) w-full items-center gap-2 rounded-(--radius-input) border bg-(--el-page-bg)',\n 'px-(--spacing-input-x)',\n 'transition-colors',\n 'focus-within:ring-2 focus-within:ring-(--focus-ring-color) focus-within:ring-offset-2 focus-within:ring-offset-background',\n hasError ? 'border-(--el-danger)' : 'border-(--el-input-border)',\n disabled && 'cursor-not-allowed opacity-50',\n )}\n >\n {addonStart ? (\n <span aria-hidden className=\"text-(--el-icon-field) inline-flex\">\n {addonStart}\n </span>\n ) : null}\n <input\n ref={ref}\n id={inputId}\n aria-invalid={hasError || undefined}\n aria-describedby={describedBy}\n disabled={disabled}\n className={cn(\n 'flex-1 bg-transparent font-sans text-sm text-(--el-text) outline-none placeholder:text-(--el-text-muted)',\n 'disabled:cursor-not-allowed',\n className,\n )}\n {...rest}\n />\n {addonEnd ? (\n <span aria-hidden className=\"text-(--el-icon-field) inline-flex\">\n {addonEnd}\n </span>\n ) : null}\n </div>\n </FormField>\n );\n});\n"]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
5
|
+
import * as Dialog from '@radix-ui/react-dialog';
|
|
6
|
+
import { VariantProps } from 'class-variance-authority';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Modal — accessible dialog wrapping @radix-ui/react-dialog.
|
|
10
|
+
*
|
|
11
|
+
* Radix handles focus trap, ESC-to-close, click-outside-to-close, and
|
|
12
|
+
* focus-return-on-close out of the box. We style on top.
|
|
13
|
+
*
|
|
14
|
+
* Open/close is controlled by the consumer via `open` + `onOpenChange`.
|
|
15
|
+
*
|
|
16
|
+
* For a scrollable body with a pinned footer, wrap the fields in `Modal.Body`
|
|
17
|
+
* (it owns the `flex-1 overflow-y-auto` scroll recipe and keeps focus rings
|
|
18
|
+
* from clipping against the scroll edge — see `ModalBody`).
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const [open, setOpen] = useState(false);
|
|
22
|
+
* <Modal open={open} onOpenChange={setOpen} title="Confirm" size="md">
|
|
23
|
+
* <Modal.Body className="gap-4">
|
|
24
|
+
* <Input label="Name" autoFocus />
|
|
25
|
+
* </Modal.Body>
|
|
26
|
+
* <Modal.Footer>
|
|
27
|
+
* <Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
|
|
28
|
+
* <Button variant="primary" onClick={confirm}>Confirm</Button>
|
|
29
|
+
* </Modal.Footer>
|
|
30
|
+
* </Modal>
|
|
31
|
+
*/
|
|
32
|
+
declare const contentVariants: (props?: ({
|
|
33
|
+
size?: "sm" | "md" | "lg" | "xl" | "full" | null | undefined;
|
|
34
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
35
|
+
interface ModalProps extends VariantProps<typeof contentVariants> {
|
|
36
|
+
open: boolean;
|
|
37
|
+
onOpenChange: (open: boolean) => void;
|
|
38
|
+
title?: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
children?: ReactNode;
|
|
41
|
+
/** Hide the default close (×) button in the corner. */
|
|
42
|
+
hideClose?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Accessible name for a dialog that renders its OWN visible heading (so no
|
|
45
|
+
* `title` is passed). Radix requires a Dialog.Title; without `title`/`srTitle`
|
|
46
|
+
* we fall back to the generic "Dialog". Supply this to label the dialog with
|
|
47
|
+
* something meaningful (e.g. the previewed issue's key) while keeping the
|
|
48
|
+
* heading inside `children` (Subtask 2.5.19's quick-view peek).
|
|
49
|
+
*/
|
|
50
|
+
srTitle?: string;
|
|
51
|
+
className?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Extra classes merged onto the backdrop — e.g. the attachment preview
|
|
54
|
+
* lightbox (Subtask 5.2.6) deepens the default `--el-overlay-scrim` to
|
|
55
|
+
* `bg-black/80` per `design/work-items/attachments.mock.html` panel 6.
|
|
56
|
+
*/
|
|
57
|
+
overlayClassName?: string;
|
|
58
|
+
/**
|
|
59
|
+
* The dialog's ARIA role. Defaults to Radix's `dialog`; a DESTRUCTIVE confirm
|
|
60
|
+
* passes `alertdialog` (Subtask 2.8.4's delete confirm) so assistive tech
|
|
61
|
+
* treats it as an alert that interrupts and announces its description.
|
|
62
|
+
*/
|
|
63
|
+
role?: 'dialog' | 'alertdialog';
|
|
64
|
+
/**
|
|
65
|
+
* Accessible label for the corner close (×) button. Defaults to `'Close'`.
|
|
66
|
+
* i18n is the consuming app's concern — the design-system package is
|
|
67
|
+
* framework-agnostic (no `next-intl` dependency), so a localized app passes
|
|
68
|
+
* the translated string here (motir-core wires it from its `common.close`).
|
|
69
|
+
*/
|
|
70
|
+
closeLabel?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Fallback accessible name when neither `title` nor `srTitle` is given.
|
|
73
|
+
* Defaults to `'Dialog'`. See `closeLabel` for the i18n rationale.
|
|
74
|
+
*/
|
|
75
|
+
dialogLabel?: string;
|
|
76
|
+
}
|
|
77
|
+
declare function ModalRoot({ open, onOpenChange, title, description, size, hideClose, srTitle, className, overlayClassName, role, children, closeLabel, dialogLabel, }: ModalProps): react_jsx_runtime.JSX.Element;
|
|
78
|
+
declare const Modal: typeof ModalRoot & {
|
|
79
|
+
Body: react.ForwardRefExoticComponent<react.HTMLAttributes<HTMLDivElement> & react.RefAttributes<HTMLDivElement>>;
|
|
80
|
+
Footer: react.ForwardRefExoticComponent<react.HTMLAttributes<HTMLDivElement> & react.RefAttributes<HTMLDivElement>>;
|
|
81
|
+
Trigger: react.ForwardRefExoticComponent<Dialog.DialogTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export { Modal, type ModalProps };
|