@astherix/ui 0.40.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 +933 -0
- package/dist/chunk-IZEGCTZW.js +588 -0
- package/dist/chunk-IZEGCTZW.js.map +1 -0
- package/dist/cli.js +345 -0
- package/dist/controlStyles-Bco2sOHE.d.ts +4 -0
- package/dist/editor.d.ts +43 -0
- package/dist/editor.js +373 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.d.ts +2470 -0
- package/dist/index.js +10582 -0
- package/dist/index.js.map +1 -0
- package/dist/theme.d.ts +78 -0
- package/dist/theme.js +287 -0
- package/dist/theme.js.map +1 -0
- package/package.json +69 -0
- package/theme.css +909 -0
- package/ui.theme.schema.json +462 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/lib/cn.ts
|
|
4
|
+
import { clsx } from "clsx";
|
|
5
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
6
|
+
var twMerge = extendTailwindMerge({
|
|
7
|
+
extend: {
|
|
8
|
+
theme: {
|
|
9
|
+
radius: ["control", "control-sm", "control-lg", "modal", "card"]
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
function cn(...inputs) {
|
|
14
|
+
return twMerge(clsx(inputs));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/components/button/Button.tsx
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
import { Slot, Slottable } from "@radix-ui/react-slot";
|
|
20
|
+
import { cva } from "class-variance-authority";
|
|
21
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
22
|
+
var buttonVariants = cva(
|
|
23
|
+
[
|
|
24
|
+
"relative inline-flex items-center justify-center gap-2 whitespace-nowrap select-none",
|
|
25
|
+
"font-medium tracking-[-0.005em] leading-none",
|
|
26
|
+
"[--btn-ring:0_0_#0000] [--btn-edge:0_0_#0000] [--btn-drop:0_0_#0000]",
|
|
27
|
+
"shadow-[var(--btn-ring),var(--btn-edge),var(--btn-drop)]",
|
|
28
|
+
"transition-[background-color,box-shadow,color,translate] duration-100 ease-out",
|
|
29
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
|
|
30
|
+
"disabled:pointer-events-none disabled:opacity-45",
|
|
31
|
+
"aria-busy:cursor-progress",
|
|
32
|
+
"motion-reduce:transition-none",
|
|
33
|
+
// SVG icons
|
|
34
|
+
"[&_svg]:pointer-events-none [&_svg]:size-[1.125em] [&_svg]:shrink-0",
|
|
35
|
+
// Icon fonts (Bootstrap Icons, Font Awesome, Glyphicons…) rendered as <i>
|
|
36
|
+
"[&_i]:pointer-events-none [&_i]:shrink-0 [&_i]:text-[1.125em] [&_i]:leading-none [&_i]:not-italic"
|
|
37
|
+
],
|
|
38
|
+
{
|
|
39
|
+
variants: {
|
|
40
|
+
variant: {
|
|
41
|
+
primary: "bg-primary text-primary-fg hover:bg-primary-hover [--btn-edge-color:var(--color-primary-edge)] [--ui-orbit-color:#fff] [--ui-progress-fill:color-mix(in_srgb,var(--color-primary-fg)_22%,transparent)] [--ui-progress-bar:var(--color-primary-fg)]",
|
|
42
|
+
secondary: [
|
|
43
|
+
"bg-secondary text-fg hover:bg-secondary-hover [--btn-edge-color:var(--color-border-strong)] [--ui-orbit-color:var(--color-primary)] [--ui-progress-fill:color-mix(in_srgb,var(--color-primary)_16%,transparent)] [--ui-progress-bar:var(--color-primary)]",
|
|
44
|
+
"[--btn-ring:inset_0_0_0_1px_var(--color-border-strong)]"
|
|
45
|
+
],
|
|
46
|
+
ghost: "bg-transparent text-fg hover:bg-secondary-hover active:bg-border [--ui-orbit-color:var(--color-primary)] [--ui-progress-fill:color-mix(in_srgb,var(--color-primary)_16%,transparent)] [--ui-progress-bar:var(--color-primary)]",
|
|
47
|
+
danger: "bg-danger text-danger-fg hover:bg-danger-hover [--btn-edge-color:var(--color-danger-edge)] [--ui-orbit-color:#fff] [--ui-progress-fill:color-mix(in_srgb,var(--color-danger-fg)_22%,transparent)] [--ui-progress-bar:var(--color-danger-fg)]"
|
|
48
|
+
},
|
|
49
|
+
// Raised: a 3px darker bottom edge makes the button look lifted. On press it drops
|
|
50
|
+
// 2px and the edge shrinks to 1px, so it reads as pushed in. Flat buttons move 1px.
|
|
51
|
+
raised: {
|
|
52
|
+
true: [
|
|
53
|
+
"pb-[3px] [--btn-edge:inset_0_-3px_0_0_var(--btn-edge-color)]",
|
|
54
|
+
"active:translate-y-[2px] active:[--btn-edge:inset_0_-1px_0_0_var(--btn-edge-color)]",
|
|
55
|
+
"motion-reduce:active:translate-y-0"
|
|
56
|
+
],
|
|
57
|
+
false: "active:translate-y-px motion-reduce:active:translate-y-0"
|
|
58
|
+
},
|
|
59
|
+
// Drop shadow weight. Values are theme tokens (--ui-shadow-*), tuned for light and dark.
|
|
60
|
+
// Pressing a button with a shadow lowers it to the lightest level.
|
|
61
|
+
shadow: {
|
|
62
|
+
none: "",
|
|
63
|
+
sm: "[--btn-drop:var(--ui-shadow-sm)]",
|
|
64
|
+
md: "[--btn-drop:var(--ui-shadow-md)] active:[--btn-drop:var(--ui-shadow-sm)]",
|
|
65
|
+
lg: "[--btn-drop:var(--ui-shadow-lg)] active:[--btn-drop:var(--ui-shadow-sm)]",
|
|
66
|
+
xl: "[--btn-drop:var(--ui-shadow-xl)] active:[--btn-drop:var(--ui-shadow-sm)]"
|
|
67
|
+
},
|
|
68
|
+
size: {
|
|
69
|
+
sm: "h-8 px-3 text-sm",
|
|
70
|
+
md: "h-10 px-4 text-[0.9375rem]",
|
|
71
|
+
lg: "h-12 px-5 text-base"
|
|
72
|
+
},
|
|
73
|
+
// Corner radius. "md" follows --radius-control, so changing that token re-shapes
|
|
74
|
+
// every default button in an app. With iconOnly, "full" makes a perfect circle.
|
|
75
|
+
rounded: {
|
|
76
|
+
none: "rounded-none",
|
|
77
|
+
sm: "rounded-control-sm",
|
|
78
|
+
md: "rounded-control",
|
|
79
|
+
lg: "rounded-control-lg",
|
|
80
|
+
full: "rounded-full"
|
|
81
|
+
},
|
|
82
|
+
iconOnly: {
|
|
83
|
+
true: "px-0",
|
|
84
|
+
false: ""
|
|
85
|
+
},
|
|
86
|
+
fullWidth: {
|
|
87
|
+
true: "w-full",
|
|
88
|
+
false: ""
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
compoundVariants: [
|
|
92
|
+
// Ghost buttons are always flat.
|
|
93
|
+
{
|
|
94
|
+
variant: "ghost",
|
|
95
|
+
raised: true,
|
|
96
|
+
class: "pb-0 [--btn-edge:0_0_#0000] active:[--btn-edge:0_0_#0000] active:translate-y-px"
|
|
97
|
+
},
|
|
98
|
+
{ iconOnly: true, size: "sm", class: "w-8" },
|
|
99
|
+
{ iconOnly: true, size: "md", class: "w-10" },
|
|
100
|
+
{ iconOnly: true, size: "lg", class: "w-12" },
|
|
101
|
+
// Pill buttons get a little more side padding so the label doesn't crowd the curve.
|
|
102
|
+
{ rounded: "full", iconOnly: false, size: "sm", class: "px-4" },
|
|
103
|
+
{ rounded: "full", iconOnly: false, size: "md", class: "px-5" },
|
|
104
|
+
{ rounded: "full", iconOnly: false, size: "lg", class: "px-6" }
|
|
105
|
+
],
|
|
106
|
+
defaultVariants: {
|
|
107
|
+
variant: "primary",
|
|
108
|
+
raised: true,
|
|
109
|
+
shadow: "none",
|
|
110
|
+
size: "md",
|
|
111
|
+
rounded: "md",
|
|
112
|
+
iconOnly: false,
|
|
113
|
+
fullWidth: false
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
function renderIcon(icon) {
|
|
118
|
+
if (typeof icon === "string") {
|
|
119
|
+
return icon.trim() ? /* @__PURE__ */ jsx("i", { className: icon, "aria-hidden": "true" }) : null;
|
|
120
|
+
}
|
|
121
|
+
return icon;
|
|
122
|
+
}
|
|
123
|
+
function Spinner() {
|
|
124
|
+
return /* @__PURE__ */ jsxs(
|
|
125
|
+
"svg",
|
|
126
|
+
{
|
|
127
|
+
viewBox: "0 0 16 16",
|
|
128
|
+
fill: "none",
|
|
129
|
+
"aria-hidden": "true",
|
|
130
|
+
className: "animate-spin motion-reduce:animate-none",
|
|
131
|
+
children: [
|
|
132
|
+
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "6.25", stroke: "currentColor", strokeOpacity: "0.25", strokeWidth: "1.5" }),
|
|
133
|
+
/* @__PURE__ */ jsx("path", { d: "M14.25 8A6.25 6.25 0 0 0 8 1.75", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
function isPromiseLike(value) {
|
|
139
|
+
return !!value && typeof value.then === "function";
|
|
140
|
+
}
|
|
141
|
+
function SideSlot({
|
|
142
|
+
side,
|
|
143
|
+
busy,
|
|
144
|
+
icon
|
|
145
|
+
}) {
|
|
146
|
+
if (icon) return /* @__PURE__ */ jsx(Fragment, { children: busy ? /* @__PURE__ */ jsx(Spinner, {}) : icon });
|
|
147
|
+
return /* @__PURE__ */ jsx(
|
|
148
|
+
"span",
|
|
149
|
+
{
|
|
150
|
+
"aria-hidden": "true",
|
|
151
|
+
className: cn(
|
|
152
|
+
"inline-flex items-center justify-center overflow-hidden",
|
|
153
|
+
"transition-[width,margin] duration-150 ease-out motion-reduce:transition-none",
|
|
154
|
+
busy ? "w-[1.125em]" : cn("w-0", side === "start" ? "-mr-2" : "-ml-2")
|
|
155
|
+
),
|
|
156
|
+
children: busy && /* @__PURE__ */ jsx(Spinner, {})
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
var PROGRESS_FINISH_MS = 420;
|
|
161
|
+
function ProgressIndicator({
|
|
162
|
+
kind,
|
|
163
|
+
value,
|
|
164
|
+
finishing
|
|
165
|
+
}) {
|
|
166
|
+
const barRef = React.useRef(null);
|
|
167
|
+
const determinate = typeof value === "number" && Number.isFinite(value);
|
|
168
|
+
React.useLayoutEffect(() => {
|
|
169
|
+
const el = barRef.current;
|
|
170
|
+
if (!el) return;
|
|
171
|
+
if (!finishing) {
|
|
172
|
+
el.style.animation = el.style.transition = el.style.opacity = "";
|
|
173
|
+
if (!determinate) el.style.width = "";
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const current = getComputedStyle(el).width;
|
|
177
|
+
el.style.animation = "none";
|
|
178
|
+
el.style.width = current;
|
|
179
|
+
void el.getBoundingClientRect();
|
|
180
|
+
el.style.transition = "width 180ms ease-out, opacity 220ms ease 180ms";
|
|
181
|
+
el.style.width = "100%";
|
|
182
|
+
el.style.opacity = "0";
|
|
183
|
+
}, [finishing]);
|
|
184
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 overflow-hidden rounded-[inherit]", children: /* @__PURE__ */ jsx(
|
|
185
|
+
"span",
|
|
186
|
+
{
|
|
187
|
+
ref: barRef,
|
|
188
|
+
className: cn(
|
|
189
|
+
"absolute left-0",
|
|
190
|
+
kind === "fill" ? "inset-y-0 bg-[var(--ui-progress-fill)]" : "bottom-0 h-[var(--ui-progress-height,3px)] bg-[var(--ui-progress-bar)]",
|
|
191
|
+
!determinate && "animate-[ui-progress-creep_var(--ui-progress-duration,8s)_cubic-bezier(0.1,0.6,0.3,1)_forwards]"
|
|
192
|
+
),
|
|
193
|
+
style: determinate && !finishing ? { width: `${Math.min(100, Math.max(0, value))}%`, transition: "width 200ms ease-out" } : determinate ? { width: `${Math.min(100, Math.max(0, value))}%` } : void 0
|
|
194
|
+
}
|
|
195
|
+
) });
|
|
196
|
+
}
|
|
197
|
+
var Button = React.forwardRef(
|
|
198
|
+
({
|
|
199
|
+
className,
|
|
200
|
+
variant,
|
|
201
|
+
size,
|
|
202
|
+
rounded,
|
|
203
|
+
raised,
|
|
204
|
+
shadow,
|
|
205
|
+
iconOnly,
|
|
206
|
+
fullWidth,
|
|
207
|
+
asChild = false,
|
|
208
|
+
loading = false,
|
|
209
|
+
spinnerPlacement = "center",
|
|
210
|
+
loadingIndicator = "spinner",
|
|
211
|
+
progressStyle = "fill",
|
|
212
|
+
progress,
|
|
213
|
+
loadingLabel,
|
|
214
|
+
minLoadingTime = 0,
|
|
215
|
+
leadingIcon,
|
|
216
|
+
trailingIcon,
|
|
217
|
+
icon,
|
|
218
|
+
disabled,
|
|
219
|
+
type,
|
|
220
|
+
onClick,
|
|
221
|
+
children,
|
|
222
|
+
...props
|
|
223
|
+
}, ref) => {
|
|
224
|
+
const [pending, setPending] = React.useState(false);
|
|
225
|
+
const mounted = React.useRef(true);
|
|
226
|
+
React.useEffect(() => {
|
|
227
|
+
mounted.current = true;
|
|
228
|
+
return () => {
|
|
229
|
+
mounted.current = false;
|
|
230
|
+
};
|
|
231
|
+
}, []);
|
|
232
|
+
const busy = loading || pending;
|
|
233
|
+
const usesProgress = loadingIndicator === "progress";
|
|
234
|
+
const [finishing, setFinishing] = React.useState(false);
|
|
235
|
+
const wasBusy = React.useRef(busy);
|
|
236
|
+
React.useEffect(() => {
|
|
237
|
+
const ended = wasBusy.current && !busy;
|
|
238
|
+
wasBusy.current = busy;
|
|
239
|
+
if (busy) setFinishing(false);
|
|
240
|
+
if (!ended || !usesProgress) return;
|
|
241
|
+
setFinishing(true);
|
|
242
|
+
const timer = window.setTimeout(() => setFinishing(false), PROGRESS_FINISH_MS);
|
|
243
|
+
return () => window.clearTimeout(timer);
|
|
244
|
+
}, [busy, usesProgress]);
|
|
245
|
+
const classes = cn(buttonVariants({ variant, raised, shadow, size, rounded, iconOnly, fullWidth }), className);
|
|
246
|
+
const lead = renderIcon(leadingIcon);
|
|
247
|
+
const trail = renderIcon(trailingIcon);
|
|
248
|
+
const content = icon !== void 0 ? renderIcon(icon) : children;
|
|
249
|
+
if (asChild) {
|
|
250
|
+
return /* @__PURE__ */ jsxs(Slot, { ref, className: classes, onClick, ...props, children: [
|
|
251
|
+
lead,
|
|
252
|
+
/* @__PURE__ */ jsx(Slottable, { children }),
|
|
253
|
+
trail
|
|
254
|
+
] });
|
|
255
|
+
}
|
|
256
|
+
const handleClick = (event) => {
|
|
257
|
+
if (busy) {
|
|
258
|
+
event.preventDefault();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const result = onClick?.(event);
|
|
262
|
+
if (!isPromiseLike(result)) return;
|
|
263
|
+
setPending(true);
|
|
264
|
+
const startedAt = Date.now();
|
|
265
|
+
const stop = () => {
|
|
266
|
+
const remaining = Math.max(0, minLoadingTime - (Date.now() - startedAt));
|
|
267
|
+
setTimeout(() => {
|
|
268
|
+
if (mounted.current) setPending(false);
|
|
269
|
+
}, remaining);
|
|
270
|
+
};
|
|
271
|
+
Promise.resolve(result).then(stop, stop);
|
|
272
|
+
};
|
|
273
|
+
const orbit = loadingIndicator === "orbit";
|
|
274
|
+
const keepsLabel = orbit || usesProgress;
|
|
275
|
+
const centered = spinnerPlacement === "center" || iconOnly;
|
|
276
|
+
const showSpinner = busy && !keepsLabel;
|
|
277
|
+
const useLoadingLabel = busy && !iconOnly && loadingLabel !== void 0 && (keepsLabel || !centered);
|
|
278
|
+
const label = useLoadingLabel ? loadingLabel : content;
|
|
279
|
+
return /* @__PURE__ */ jsxs(
|
|
280
|
+
"button",
|
|
281
|
+
{
|
|
282
|
+
ref,
|
|
283
|
+
type: type ?? "button",
|
|
284
|
+
className: classes,
|
|
285
|
+
disabled,
|
|
286
|
+
"aria-disabled": busy || void 0,
|
|
287
|
+
"aria-busy": busy || void 0,
|
|
288
|
+
"data-loading": busy ? "" : void 0,
|
|
289
|
+
onClick: handleClick,
|
|
290
|
+
...props,
|
|
291
|
+
children: [
|
|
292
|
+
orbit && busy && // Experimental: a light running around the edge. The outer span adds the glow,
|
|
293
|
+
// the inner span draws the light (see .ui-btn-orbit in theme.css).
|
|
294
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ui-btn-orbit-glow", children: /* @__PURE__ */ jsx("span", { className: "ui-btn-orbit" }) }),
|
|
295
|
+
usesProgress && (busy || finishing) && /* @__PURE__ */ jsx(
|
|
296
|
+
ProgressIndicator,
|
|
297
|
+
{
|
|
298
|
+
kind: progressStyle,
|
|
299
|
+
value: progress,
|
|
300
|
+
finishing: !busy && finishing
|
|
301
|
+
}
|
|
302
|
+
),
|
|
303
|
+
centered && showSpinner && /* @__PURE__ */ jsx("span", { className: "absolute inset-0 flex items-center justify-center pb-[inherit]", children: /* @__PURE__ */ jsx(Spinner, {}) }),
|
|
304
|
+
/* @__PURE__ */ jsx("span", { className: cn("relative inline-flex items-center gap-2", centered && showSpinner && "opacity-0"), children: centered || keepsLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
305
|
+
lead,
|
|
306
|
+
label,
|
|
307
|
+
trail
|
|
308
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
309
|
+
spinnerPlacement === "start" ? /* @__PURE__ */ jsx(SideSlot, { side: "start", busy: showSpinner, icon: lead }) : lead,
|
|
310
|
+
label,
|
|
311
|
+
spinnerPlacement === "end" ? /* @__PURE__ */ jsx(SideSlot, { side: "end", busy: showSpinner, icon: trail }) : trail
|
|
312
|
+
] }) })
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
Button.displayName = "Button";
|
|
319
|
+
|
|
320
|
+
// src/components/input/Field.tsx
|
|
321
|
+
import * as React2 from "react";
|
|
322
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
323
|
+
var FieldContext = React2.createContext(null);
|
|
324
|
+
function useField() {
|
|
325
|
+
return React2.useContext(FieldContext);
|
|
326
|
+
}
|
|
327
|
+
function useFieldControlProps(props) {
|
|
328
|
+
const field = useField();
|
|
329
|
+
const describedBy = [props["aria-describedby"], field?.descriptionId, field?.errorId].filter(Boolean).join(" ") || void 0;
|
|
330
|
+
const invalid = props.invalid ?? field?.invalid ?? false;
|
|
331
|
+
return {
|
|
332
|
+
id: props.id ?? field?.id,
|
|
333
|
+
required: props.required ?? field?.required,
|
|
334
|
+
disabled: props.disabled ?? field?.disabled,
|
|
335
|
+
invalid,
|
|
336
|
+
"aria-describedby": describedBy,
|
|
337
|
+
"aria-invalid": invalid || void 0
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function Field({
|
|
341
|
+
label,
|
|
342
|
+
description,
|
|
343
|
+
error,
|
|
344
|
+
required = false,
|
|
345
|
+
optional = false,
|
|
346
|
+
disabled = false,
|
|
347
|
+
id: idProp,
|
|
348
|
+
className,
|
|
349
|
+
children,
|
|
350
|
+
...props
|
|
351
|
+
}) {
|
|
352
|
+
const autoId = React2.useId();
|
|
353
|
+
const id = idProp ?? `${autoId}-control`;
|
|
354
|
+
const hasError = error !== void 0 && error !== null && error !== false && error !== "";
|
|
355
|
+
const value = {
|
|
356
|
+
id,
|
|
357
|
+
labelId: `${id}-label`,
|
|
358
|
+
descriptionId: description ? `${id}-description` : void 0,
|
|
359
|
+
errorId: hasError ? `${id}-error` : void 0,
|
|
360
|
+
invalid: hasError,
|
|
361
|
+
required,
|
|
362
|
+
disabled
|
|
363
|
+
};
|
|
364
|
+
return /* @__PURE__ */ jsx2(FieldContext.Provider, { value, children: /* @__PURE__ */ jsxs2("div", { className: cn("grid content-start gap-1.5", className), ...props, children: [
|
|
365
|
+
/* @__PURE__ */ jsxs2("label", { id: value.labelId, htmlFor: id, className: "w-fit text-sm font-medium leading-snug text-fg", children: [
|
|
366
|
+
label,
|
|
367
|
+
required && /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "ml-0.5 text-danger", children: "*" }),
|
|
368
|
+
optional && /* @__PURE__ */ jsx2("span", { className: "ml-1 font-normal text-fg-muted", children: "(optional)" })
|
|
369
|
+
] }),
|
|
370
|
+
description && /* @__PURE__ */ jsx2("p", { id: value.descriptionId, className: "-mt-0.5 text-[0.8125rem] leading-relaxed text-fg-muted", children: description }),
|
|
371
|
+
children,
|
|
372
|
+
hasError && /* @__PURE__ */ jsxs2("p", { id: value.errorId, className: "flex items-start gap-1.5 text-[0.8125rem] leading-snug text-danger", children: [
|
|
373
|
+
/* @__PURE__ */ jsx2("svg", { viewBox: "0 0 16 16", "aria-hidden": "true", className: "mt-px size-3.5 shrink-0", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M8 1.5a6.5 6.5 0 1 1 0 13 6.5 6.5 0 0 1 0-13Zm0 3a.75.75 0 0 0-.75.75v3a.75.75 0 0 0 1.5 0v-3A.75.75 0 0 0 8 4.5Zm0 6.25a.85.85 0 1 0 0 1.7.85.85 0 0 0 0-1.7Z" }) }),
|
|
374
|
+
/* @__PURE__ */ jsx2("span", { children: error })
|
|
375
|
+
] })
|
|
376
|
+
] }) });
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/components/input/Input.tsx
|
|
380
|
+
import * as React3 from "react";
|
|
381
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
382
|
+
|
|
383
|
+
// src/components/input/controlStyles.ts
|
|
384
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
385
|
+
var controlFrame = cva2(
|
|
386
|
+
[
|
|
387
|
+
"relative flex w-full border bg-surface text-fg transition-[border-color,box-shadow] duration-100",
|
|
388
|
+
"border-border-strong shadow-[inset_0_1px_2px_rgb(var(--ui-shadow-color)/0.07)]",
|
|
389
|
+
"hover:not-focus-within:border-fg-muted/60",
|
|
390
|
+
"focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/25",
|
|
391
|
+
"data-[invalid]:border-danger data-[invalid]:hover:not-focus-within:border-danger data-[invalid]:focus-within:ring-danger/25",
|
|
392
|
+
"data-[disabled]:cursor-not-allowed data-[disabled]:bg-secondary-hover data-[disabled]:opacity-70 data-[disabled]:hover:border-border-strong",
|
|
393
|
+
"data-[readonly]:bg-secondary-hover/60",
|
|
394
|
+
"motion-reduce:transition-none"
|
|
395
|
+
],
|
|
396
|
+
{
|
|
397
|
+
variants: {
|
|
398
|
+
rounded: {
|
|
399
|
+
none: "rounded-none",
|
|
400
|
+
sm: "rounded-control-sm",
|
|
401
|
+
md: "rounded-control",
|
|
402
|
+
lg: "rounded-control-lg",
|
|
403
|
+
full: "rounded-full"
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
defaultVariants: { rounded: "md" }
|
|
407
|
+
}
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
// src/components/input/Input.tsx
|
|
411
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
412
|
+
var inputFrame = cva3("items-center", {
|
|
413
|
+
variants: {
|
|
414
|
+
size: {
|
|
415
|
+
sm: "h-8 gap-1.5 px-2.5 text-sm",
|
|
416
|
+
md: "h-10 gap-2 px-3 text-[0.9375rem]",
|
|
417
|
+
lg: "h-12 gap-2.5 px-3.5 text-base"
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
defaultVariants: { size: "md" }
|
|
421
|
+
});
|
|
422
|
+
var EyeIcon = ({ off }) => /* @__PURE__ */ jsxs3("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
423
|
+
/* @__PURE__ */ jsx3("path", { d: "M2 10s3-5.5 8-5.5S18 10 18 10s-3 5.5-8 5.5S2 10 2 10Z" }),
|
|
424
|
+
/* @__PURE__ */ jsx3("circle", { cx: "10", cy: "10", r: "2.5" }),
|
|
425
|
+
off && /* @__PURE__ */ jsx3("path", { d: "M3.5 3.5l13 13" })
|
|
426
|
+
] });
|
|
427
|
+
var ClearIcon = () => /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("path", { d: "M6 6l8 8M14 6l-8 8" }) });
|
|
428
|
+
function ControlButton({ className, ...props }) {
|
|
429
|
+
return /* @__PURE__ */ jsx3(
|
|
430
|
+
"button",
|
|
431
|
+
{
|
|
432
|
+
type: "button",
|
|
433
|
+
className: cn(
|
|
434
|
+
"-mr-1 grid size-7 shrink-0 cursor-pointer place-items-center rounded-control-sm text-fg-muted",
|
|
435
|
+
"hover:bg-secondary-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-ring",
|
|
436
|
+
"[&_svg]:size-[1.05em]",
|
|
437
|
+
className
|
|
438
|
+
),
|
|
439
|
+
...props
|
|
440
|
+
}
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
function setNativeValue(el, value) {
|
|
444
|
+
const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
|
|
445
|
+
Object.getOwnPropertyDescriptor(proto, "value")?.set?.call(el, value);
|
|
446
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
447
|
+
}
|
|
448
|
+
var Input = React3.forwardRef(
|
|
449
|
+
({
|
|
450
|
+
size = "md",
|
|
451
|
+
rounded = "md",
|
|
452
|
+
leadingIcon,
|
|
453
|
+
trailingIcon,
|
|
454
|
+
prefix,
|
|
455
|
+
suffix,
|
|
456
|
+
invalid: invalidProp,
|
|
457
|
+
clearable = false,
|
|
458
|
+
onClear,
|
|
459
|
+
revealable = true,
|
|
460
|
+
frameClassName,
|
|
461
|
+
overlay,
|
|
462
|
+
className,
|
|
463
|
+
type = "text",
|
|
464
|
+
id: idProp,
|
|
465
|
+
required: requiredProp,
|
|
466
|
+
disabled: disabledProp,
|
|
467
|
+
readOnly,
|
|
468
|
+
value,
|
|
469
|
+
defaultValue,
|
|
470
|
+
onChange,
|
|
471
|
+
"aria-describedby": describedByProp,
|
|
472
|
+
...props
|
|
473
|
+
}, ref) => {
|
|
474
|
+
const { invalid, ...control } = useFieldControlProps({
|
|
475
|
+
id: idProp,
|
|
476
|
+
invalid: invalidProp,
|
|
477
|
+
required: requiredProp,
|
|
478
|
+
disabled: disabledProp,
|
|
479
|
+
"aria-describedby": describedByProp
|
|
480
|
+
});
|
|
481
|
+
const inputRef = React3.useRef(null);
|
|
482
|
+
const setRefs = (node) => {
|
|
483
|
+
inputRef.current = node;
|
|
484
|
+
if (typeof ref === "function") ref(node);
|
|
485
|
+
else if (ref) ref.current = node;
|
|
486
|
+
};
|
|
487
|
+
const [hasValue, setHasValue] = React3.useState(() => String(value ?? defaultValue ?? "") !== "");
|
|
488
|
+
React3.useEffect(() => {
|
|
489
|
+
if (value !== void 0) setHasValue(String(value) !== "");
|
|
490
|
+
}, [value]);
|
|
491
|
+
const [revealed, setRevealed] = React3.useState(false);
|
|
492
|
+
const isPassword = type === "password";
|
|
493
|
+
const effectiveType = isPassword && revealed ? "text" : type;
|
|
494
|
+
const lead = renderIcon(leadingIcon);
|
|
495
|
+
const trail = renderIcon(trailingIcon);
|
|
496
|
+
const showClear = clearable && hasValue && !control.disabled && !readOnly;
|
|
497
|
+
const inputEl = /* @__PURE__ */ jsx3(
|
|
498
|
+
"input",
|
|
499
|
+
{
|
|
500
|
+
ref: setRefs,
|
|
501
|
+
type: effectiveType,
|
|
502
|
+
readOnly,
|
|
503
|
+
value,
|
|
504
|
+
defaultValue,
|
|
505
|
+
onChange: (event) => {
|
|
506
|
+
if (value === void 0) setHasValue(event.target.value !== "");
|
|
507
|
+
onChange?.(event);
|
|
508
|
+
},
|
|
509
|
+
className: cn(
|
|
510
|
+
"h-full w-0 min-w-0 flex-1 bg-transparent text-fg outline-none placeholder:text-fg-muted/70",
|
|
511
|
+
"disabled:cursor-not-allowed [&::-webkit-search-cancel-button]:appearance-none",
|
|
512
|
+
className
|
|
513
|
+
),
|
|
514
|
+
...control,
|
|
515
|
+
...props
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
return /* @__PURE__ */ jsxs3(
|
|
519
|
+
"div",
|
|
520
|
+
{
|
|
521
|
+
"data-control-frame": "",
|
|
522
|
+
"data-invalid": invalid ? "" : void 0,
|
|
523
|
+
"data-disabled": control.disabled ? "" : void 0,
|
|
524
|
+
"data-readonly": readOnly ? "" : void 0,
|
|
525
|
+
className: cn(
|
|
526
|
+
controlFrame({ rounded }),
|
|
527
|
+
inputFrame({ size }),
|
|
528
|
+
rounded === "full" && (size === "sm" ? "px-3.5" : size === "lg" ? "px-5" : "px-4"),
|
|
529
|
+
"[&>svg]:size-[1.125em] [&>svg]:shrink-0 [&>svg]:text-fg-muted [&>i]:shrink-0 [&>i]:text-[1.125em] [&>i]:leading-none [&>i]:text-fg-muted",
|
|
530
|
+
frameClassName
|
|
531
|
+
),
|
|
532
|
+
onMouseDown: (event) => {
|
|
533
|
+
if (event.target !== inputRef.current && !event.target.closest("button")) {
|
|
534
|
+
event.preventDefault();
|
|
535
|
+
inputRef.current?.focus();
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
children: [
|
|
539
|
+
lead,
|
|
540
|
+
prefix !== void 0 && /* @__PURE__ */ jsx3("span", { className: "shrink-0 select-none whitespace-nowrap text-fg-muted", children: prefix }),
|
|
541
|
+
overlay !== void 0 ? /* @__PURE__ */ jsxs3("span", { className: "relative flex h-full min-w-0 flex-1", children: [
|
|
542
|
+
inputEl,
|
|
543
|
+
overlay
|
|
544
|
+
] }) : inputEl,
|
|
545
|
+
suffix !== void 0 && /* @__PURE__ */ jsx3("span", { className: "shrink-0 select-none whitespace-nowrap text-fg-muted", children: suffix }),
|
|
546
|
+
showClear && /* @__PURE__ */ jsx3(
|
|
547
|
+
ControlButton,
|
|
548
|
+
{
|
|
549
|
+
"aria-label": "Clear",
|
|
550
|
+
onClick: () => {
|
|
551
|
+
const el = inputRef.current;
|
|
552
|
+
if (!el) return;
|
|
553
|
+
setNativeValue(el, "");
|
|
554
|
+
el.focus();
|
|
555
|
+
onClear?.();
|
|
556
|
+
},
|
|
557
|
+
children: /* @__PURE__ */ jsx3(ClearIcon, {})
|
|
558
|
+
}
|
|
559
|
+
),
|
|
560
|
+
isPassword && revealable && !control.disabled && /* @__PURE__ */ jsx3(
|
|
561
|
+
ControlButton,
|
|
562
|
+
{
|
|
563
|
+
"aria-label": revealed ? "Hide password" : "Show password",
|
|
564
|
+
"aria-pressed": revealed,
|
|
565
|
+
onClick: () => setRevealed((r) => !r),
|
|
566
|
+
children: /* @__PURE__ */ jsx3(EyeIcon, { off: revealed })
|
|
567
|
+
}
|
|
568
|
+
),
|
|
569
|
+
trail
|
|
570
|
+
]
|
|
571
|
+
}
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
);
|
|
575
|
+
Input.displayName = "Input";
|
|
576
|
+
|
|
577
|
+
export {
|
|
578
|
+
cn,
|
|
579
|
+
buttonVariants,
|
|
580
|
+
renderIcon,
|
|
581
|
+
Button,
|
|
582
|
+
useField,
|
|
583
|
+
useFieldControlProps,
|
|
584
|
+
Field,
|
|
585
|
+
controlFrame,
|
|
586
|
+
Input
|
|
587
|
+
};
|
|
588
|
+
//# sourceMappingURL=chunk-IZEGCTZW.js.map
|