@ashwa-io/ui 0.0.2 → 0.0.4
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/dist/components/atomic/Badge/Badge.d.ts +11 -0
- package/dist/components/atomic/Badge/Badge.js +31 -0
- package/dist/components/atomic/Badge/index.d.ts +2 -0
- package/dist/components/atomic/Button/Button.d.ts +3 -2
- package/dist/components/atomic/Button/Button.js +3 -10
- package/dist/components/atomic/Button/buttonVariant.d.ts +3 -0
- package/dist/components/atomic/Button/buttonVariant.js +10 -0
- package/dist/components/atomic/Button/index.d.ts +2 -1
- package/dist/components/atomic/Checkbox/Checkbox.d.ts +11 -0
- package/dist/components/atomic/Checkbox/Checkbox.js +32 -0
- package/dist/components/atomic/Checkbox/index.d.ts +2 -0
- package/dist/components/atomic/ColorInput/ColorInput.d.ts +15 -0
- package/dist/components/atomic/ColorInput/ColorInput.js +44 -0
- package/dist/components/atomic/ColorInput/colorHex.js +8 -0
- package/dist/components/atomic/ColorInput/index.d.ts +2 -0
- package/dist/components/atomic/Divider/Divider.d.ts +8 -0
- package/dist/components/atomic/Divider/Divider.js +12 -0
- package/dist/components/atomic/Divider/index.d.ts +2 -0
- package/dist/components/atomic/Heading/Heading.d.ts +12 -0
- package/dist/components/atomic/Heading/Heading.js +30 -0
- package/dist/components/atomic/Heading/index.d.ts +2 -0
- package/dist/components/atomic/IconButton/IconButton.d.ts +21 -0
- package/dist/components/atomic/IconButton/IconButton.js +34 -0
- package/dist/components/atomic/IconButton/index.d.ts +2 -0
- package/dist/components/atomic/Select/Select.d.ts +18 -0
- package/dist/components/atomic/Select/Select.js +47 -0
- package/dist/components/atomic/Select/index.d.ts +2 -0
- package/dist/components/atomic/Skeleton/Skeleton.d.ts +8 -0
- package/dist/components/atomic/Skeleton/Skeleton.js +17 -0
- package/dist/components/atomic/Skeleton/index.d.ts +2 -0
- package/dist/components/atomic/Spinner/Spinner.d.ts +10 -0
- package/dist/components/atomic/Spinner/Spinner.js +18 -0
- package/dist/components/atomic/Spinner/index.d.ts +2 -0
- package/dist/components/atomic/Switch/Switch.d.ts +11 -0
- package/dist/components/atomic/Switch/Switch.js +32 -0
- package/dist/components/atomic/Switch/index.d.ts +2 -0
- package/dist/components/atomic/Text/Text.d.ts +20 -0
- package/dist/components/atomic/Text/Text.js +33 -0
- package/dist/components/atomic/Text/index.d.ts +2 -0
- package/dist/components/atomic/TextArea/TextArea.d.ts +14 -0
- package/dist/components/atomic/TextArea/TextArea.js +35 -0
- package/dist/components/atomic/TextArea/index.d.ts +2 -0
- package/dist/components/atomic/TextInput/TextInput.d.ts +18 -0
- package/dist/components/atomic/TextInput/TextInput.js +42 -0
- package/dist/components/atomic/TextInput/index.d.ts +2 -0
- package/dist/components/layout/FormLayout/FormLayout.d.ts +9 -0
- package/dist/components/layout/FormLayout/FormLayout.js +15 -0
- package/dist/components/layout/FormLayout/index.d.ts +2 -0
- package/dist/components/layout/Stack/Stack.d.ts +22 -0
- package/dist/components/layout/Stack/Stack.js +62 -0
- package/dist/components/layout/Stack/index.d.ts +2 -0
- package/dist/components/molecule/FormField/FormField.d.ts +16 -0
- package/dist/components/molecule/FormField/FormField.js +47 -0
- package/dist/components/molecule/FormField/index.d.ts +2 -0
- package/dist/components/ui/badge.js +26 -0
- package/dist/components/ui/button.d.ts +2 -0
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/checkbox.js +18 -0
- package/dist/components/ui/field.js +65 -0
- package/dist/components/ui/input.js +14 -0
- package/dist/components/ui/label.js +12 -0
- package/dist/components/ui/select.js +75 -0
- package/dist/components/ui/separator.js +13 -0
- package/dist/components/ui/skeleton.js +11 -0
- package/dist/components/ui/spinner.js +14 -0
- package/dist/components/ui/switch.js +18 -0
- package/dist/components/ui/textarea.js +12 -0
- package/dist/index.d.ts +36 -3
- package/dist/index.js +18 -1
- package/dist/lib/types.d.ts +16 -1
- package/package.json +1 -1
- package/src/styles/index.css +11 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { FormField } from "../../molecule/FormField/FormField.js";
|
|
2
|
+
import { Textarea } from "../../ui/textarea.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
// src/components/atomic/TextArea/TextArea.tsx
|
|
5
|
+
const TextArea = (props) => {
|
|
6
|
+
const { label, hideLabel, description, error, required, disabled, id, "data-testid": testId, value, onChange, onBlur, name, placeholder, rows = 3, maxLength, readOnly = false, ref } = props;
|
|
7
|
+
return /* @__PURE__ */ jsx(FormField, {
|
|
8
|
+
label,
|
|
9
|
+
hideLabel,
|
|
10
|
+
description,
|
|
11
|
+
error,
|
|
12
|
+
required,
|
|
13
|
+
disabled,
|
|
14
|
+
id,
|
|
15
|
+
"data-testid": testId,
|
|
16
|
+
children: (control) => /* @__PURE__ */ jsx(Textarea, {
|
|
17
|
+
ref,
|
|
18
|
+
id: control.id,
|
|
19
|
+
"aria-describedby": control["aria-describedby"],
|
|
20
|
+
"aria-invalid": control["aria-invalid"],
|
|
21
|
+
"aria-required": control["aria-required"],
|
|
22
|
+
disabled: control.disabled,
|
|
23
|
+
name,
|
|
24
|
+
value,
|
|
25
|
+
placeholder,
|
|
26
|
+
rows,
|
|
27
|
+
maxLength,
|
|
28
|
+
readOnly,
|
|
29
|
+
onChange: (event) => onChange(event.target.value),
|
|
30
|
+
onBlur,
|
|
31
|
+
className: "field-sizing-fixed"
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
export { TextArea };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FieldBaseProps, IconComponent } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export type TextInputType = 'text' | 'email' | 'password' | 'search' | 'tel' | 'url';
|
|
4
|
+
export interface TextInputProps extends FieldBaseProps {
|
|
5
|
+
value: string;
|
|
6
|
+
onChange: (value: string) => void;
|
|
7
|
+
onBlur?: () => void;
|
|
8
|
+
name?: string;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
type?: TextInputType;
|
|
11
|
+
/** Leading icon, decorative (aria-hidden). */
|
|
12
|
+
icon?: IconComponent;
|
|
13
|
+
readOnly?: boolean;
|
|
14
|
+
/** e.g. 'username', 'current-password' on login forms. */
|
|
15
|
+
autoComplete?: string;
|
|
16
|
+
ref?: React.Ref<HTMLInputElement>;
|
|
17
|
+
}
|
|
18
|
+
export declare const TextInput: React.FC<TextInputProps>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { FormField } from "../../molecule/FormField/FormField.js";
|
|
2
|
+
import { Input } from "../../ui/input.js";
|
|
3
|
+
import { cn } from "../../../lib/utils.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
// src/components/atomic/TextInput/TextInput.tsx
|
|
6
|
+
const TextInput = (props) => {
|
|
7
|
+
const { label, hideLabel, description, error, required, disabled, id, "data-testid": testId, value, onChange, onBlur, name, placeholder, type = "text", icon: Icon, readOnly = false, autoComplete, ref } = props;
|
|
8
|
+
return /* @__PURE__ */ jsx(FormField, {
|
|
9
|
+
label,
|
|
10
|
+
hideLabel,
|
|
11
|
+
description,
|
|
12
|
+
error,
|
|
13
|
+
required,
|
|
14
|
+
disabled,
|
|
15
|
+
id,
|
|
16
|
+
"data-testid": testId,
|
|
17
|
+
children: (control) => /* @__PURE__ */ jsxs("div", {
|
|
18
|
+
className: "relative",
|
|
19
|
+
children: [Icon && /* @__PURE__ */ jsx(Icon, {
|
|
20
|
+
"aria-hidden": true,
|
|
21
|
+
className: "text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2"
|
|
22
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
23
|
+
ref,
|
|
24
|
+
id: control.id,
|
|
25
|
+
"aria-describedby": control["aria-describedby"],
|
|
26
|
+
"aria-invalid": control["aria-invalid"],
|
|
27
|
+
"aria-required": control["aria-required"],
|
|
28
|
+
disabled: control.disabled,
|
|
29
|
+
type,
|
|
30
|
+
name,
|
|
31
|
+
value,
|
|
32
|
+
placeholder,
|
|
33
|
+
readOnly,
|
|
34
|
+
autoComplete,
|
|
35
|
+
onChange: (event) => onChange(event.target.value),
|
|
36
|
+
onBlur,
|
|
37
|
+
className: cn(Icon && "pl-8")
|
|
38
|
+
})]
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
export { TextInput };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type FormLayoutDirection = 'vertical' | 'horizontal';
|
|
3
|
+
export interface FormLayoutProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
/** horizontal: fields side by side in equal columns from sm up, stacked on phones. */
|
|
6
|
+
direction?: FormLayoutDirection;
|
|
7
|
+
'data-testid'?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const FormLayout: React.FC<FormLayoutProps>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
// src/components/layout/FormLayout/FormLayout.tsx
|
|
3
|
+
const DIRECTION = {
|
|
4
|
+
vertical: "flex flex-col gap-5",
|
|
5
|
+
horizontal: "grid gap-5 sm:auto-cols-fr sm:grid-flow-col"
|
|
6
|
+
};
|
|
7
|
+
const FormLayout = (props) => {
|
|
8
|
+
const { children, direction = "vertical", "data-testid": testId } = props;
|
|
9
|
+
return /* @__PURE__ */ jsx("div", {
|
|
10
|
+
"data-testid": testId,
|
|
11
|
+
className: DIRECTION[direction],
|
|
12
|
+
children
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
export { FormLayout };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type StackGap = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8;
|
|
3
|
+
export type StackAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';
|
|
4
|
+
export type StackJustify = 'start' | 'center' | 'end' | 'between';
|
|
5
|
+
export interface StackProps {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
direction?: 'row' | 'column';
|
|
8
|
+
/** Tailwind spacing step (1 = 4px). */
|
|
9
|
+
gap?: StackGap;
|
|
10
|
+
/** Cross axis. */
|
|
11
|
+
align?: StackAlign;
|
|
12
|
+
/** Main axis. */
|
|
13
|
+
justify?: StackJustify;
|
|
14
|
+
wrap?: boolean;
|
|
15
|
+
id?: string;
|
|
16
|
+
'data-testid'?: string;
|
|
17
|
+
}
|
|
18
|
+
export type HStackProps = Omit<StackProps, 'direction'>;
|
|
19
|
+
export type VStackProps = Omit<StackProps, 'direction'>;
|
|
20
|
+
export declare const Stack: React.FC<StackProps>;
|
|
21
|
+
export declare const HStack: React.FC<HStackProps>;
|
|
22
|
+
export declare const VStack: React.FC<VStackProps>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
// src/components/layout/Stack/Stack.tsx
|
|
4
|
+
const GAP = {
|
|
5
|
+
0: "gap-0",
|
|
6
|
+
1: "gap-1",
|
|
7
|
+
2: "gap-2",
|
|
8
|
+
3: "gap-3",
|
|
9
|
+
4: "gap-4",
|
|
10
|
+
5: "gap-5",
|
|
11
|
+
6: "gap-6",
|
|
12
|
+
8: "gap-8"
|
|
13
|
+
};
|
|
14
|
+
const ALIGN = {
|
|
15
|
+
start: "items-start",
|
|
16
|
+
center: "items-center",
|
|
17
|
+
end: "items-end",
|
|
18
|
+
stretch: "items-stretch",
|
|
19
|
+
baseline: "items-baseline"
|
|
20
|
+
};
|
|
21
|
+
const JUSTIFY = {
|
|
22
|
+
start: "justify-start",
|
|
23
|
+
center: "justify-center",
|
|
24
|
+
end: "justify-end",
|
|
25
|
+
between: "justify-between"
|
|
26
|
+
};
|
|
27
|
+
const Stack = (props) => {
|
|
28
|
+
const { children, direction = "column", gap = 0, align, justify, wrap = false, id, "data-testid": testId } = props;
|
|
29
|
+
return /* @__PURE__ */ jsx("div", {
|
|
30
|
+
id,
|
|
31
|
+
"data-testid": testId,
|
|
32
|
+
className: cn("flex", direction === "row" ? "flex-row" : "flex-col", GAP[gap], align && ALIGN[align], justify && JUSTIFY[justify], wrap && "flex-wrap"),
|
|
33
|
+
children
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
const HStack = (props) => {
|
|
37
|
+
const { children, gap, align, justify, wrap, id, "data-testid": testId } = props;
|
|
38
|
+
return /* @__PURE__ */ jsx(Stack, {
|
|
39
|
+
direction: "row",
|
|
40
|
+
gap,
|
|
41
|
+
align,
|
|
42
|
+
justify,
|
|
43
|
+
wrap,
|
|
44
|
+
id,
|
|
45
|
+
"data-testid": testId,
|
|
46
|
+
children
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
const VStack = (props) => {
|
|
50
|
+
const { children, gap, align, justify, wrap, id, "data-testid": testId } = props;
|
|
51
|
+
return /* @__PURE__ */ jsx(Stack, {
|
|
52
|
+
direction: "column",
|
|
53
|
+
gap,
|
|
54
|
+
align,
|
|
55
|
+
justify,
|
|
56
|
+
wrap,
|
|
57
|
+
id,
|
|
58
|
+
"data-testid": testId,
|
|
59
|
+
children
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
export { HStack, Stack, VStack };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FieldBaseProps } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
/** Wiring FormField hands to its control; pass each prop by name. */
|
|
4
|
+
export interface FormFieldControlProps {
|
|
5
|
+
id: string;
|
|
6
|
+
'aria-describedby'?: string;
|
|
7
|
+
'aria-invalid'?: true;
|
|
8
|
+
'aria-required'?: true;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface FormFieldProps extends FieldBaseProps {
|
|
12
|
+
children: (control: FormFieldControlProps) => React.ReactNode;
|
|
13
|
+
/** horizontal puts the label beside the control (checkbox, switch). */
|
|
14
|
+
orientation?: 'vertical' | 'horizontal';
|
|
15
|
+
}
|
|
16
|
+
export declare const FormField: React.FC<FormFieldProps>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Field, FieldContent, FieldDescription, FieldError, FieldLabel } from "../../ui/field.js";
|
|
2
|
+
import { cn } from "../../../lib/utils.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useId } from "react";
|
|
5
|
+
// src/components/molecule/FormField/FormField.tsx
|
|
6
|
+
const FormField = (props) => {
|
|
7
|
+
const { children, label, hideLabel = false, description, error, required = false, disabled = false, orientation = "vertical", id: idProp, "data-testid": testId } = props;
|
|
8
|
+
const generatedId = useId();
|
|
9
|
+
const id = idProp ?? generatedId;
|
|
10
|
+
const descriptionId = description ? `${id}-description` : void 0;
|
|
11
|
+
const errorId = error ? `${id}-error` : void 0;
|
|
12
|
+
const control = children({
|
|
13
|
+
id,
|
|
14
|
+
"aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || void 0,
|
|
15
|
+
"aria-invalid": error ? true : void 0,
|
|
16
|
+
"aria-required": required ? true : void 0,
|
|
17
|
+
disabled
|
|
18
|
+
});
|
|
19
|
+
const labelNode = /* @__PURE__ */ jsxs(FieldLabel, {
|
|
20
|
+
htmlFor: id,
|
|
21
|
+
className: cn(hideLabel && "sr-only"),
|
|
22
|
+
children: [label, required && /* @__PURE__ */ jsx("span", {
|
|
23
|
+
"aria-hidden": true,
|
|
24
|
+
className: "text-destructive",
|
|
25
|
+
children: "*"
|
|
26
|
+
})]
|
|
27
|
+
});
|
|
28
|
+
const helpNodes = /* @__PURE__ */ jsxs(Fragment, { children: [description && /* @__PURE__ */ jsx(FieldDescription, {
|
|
29
|
+
id: descriptionId,
|
|
30
|
+
children: description
|
|
31
|
+
}), error && /* @__PURE__ */ jsx(FieldError, {
|
|
32
|
+
id: errorId,
|
|
33
|
+
children: error
|
|
34
|
+
})] });
|
|
35
|
+
return /* @__PURE__ */ jsx(Field, {
|
|
36
|
+
orientation,
|
|
37
|
+
"data-invalid": error ? true : void 0,
|
|
38
|
+
"data-disabled": disabled || void 0,
|
|
39
|
+
"data-testid": testId,
|
|
40
|
+
children: orientation === "horizontal" ? /* @__PURE__ */ jsxs(Fragment, { children: [control, /* @__PURE__ */ jsxs(FieldContent, { children: [labelNode, helpNodes] })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
41
|
+
labelNode,
|
|
42
|
+
control,
|
|
43
|
+
helpNodes
|
|
44
|
+
] })
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
export { FormField };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cn } from "cn";
|
|
5
|
+
// src/components/ui/badge.tsx
|
|
6
|
+
const badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", {
|
|
7
|
+
variants: { variant: {
|
|
8
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
9
|
+
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
10
|
+
destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
11
|
+
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
12
|
+
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
13
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
14
|
+
} },
|
|
15
|
+
defaultVariants: { variant: "default" }
|
|
16
|
+
});
|
|
17
|
+
const Badge = ({ className, variant = "default", render, ...props }) => useRender({
|
|
18
|
+
defaultTagName: "span",
|
|
19
|
+
props: mergeProps({ className: cn(badgeVariants({ variant }), className) }, props),
|
|
20
|
+
render,
|
|
21
|
+
state: {
|
|
22
|
+
slot: "badge",
|
|
23
|
+
variant
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
export { Badge, badgeVariants };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Button } from "@base-ui/react/button";
|
|
2
1
|
import { cva } from "class-variance-authority";
|
|
3
2
|
import { cn } from "cn";
|
|
4
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { Button } from "@base-ui/react/button";
|
|
5
5
|
// src/components/ui/button.tsx
|
|
6
6
|
const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
7
7
|
variants: {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { CheckIcon } from "lucide-react";
|
|
4
|
+
import { Checkbox } from "@base-ui/react/checkbox";
|
|
5
|
+
// src/components/ui/checkbox.tsx
|
|
6
|
+
const Checkbox$1 = ({ className, ...props }) => {
|
|
7
|
+
return /* @__PURE__ */ jsx(Checkbox.Root, {
|
|
8
|
+
"data-slot": "checkbox",
|
|
9
|
+
className: cn("peer border-input group-has-[:focus-visible]/field-label:not-data-checked:border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground group-has-[:focus-visible]/field-label:data-checked:border-primary dark:data-checked:bg-primary relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border transition-colors outline-none group-has-disabled/field:opacity-50 group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3", className),
|
|
10
|
+
...props,
|
|
11
|
+
children: /* @__PURE__ */ jsx(Checkbox.Indicator, {
|
|
12
|
+
"data-slot": "checkbox-indicator",
|
|
13
|
+
className: "grid place-content-center text-current transition-none [&>svg]:size-3.5",
|
|
14
|
+
children: /* @__PURE__ */ jsx(CheckIcon, {})
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export { Checkbox$1 as Checkbox };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Label } from "./label.js";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { cn } from "cn";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
// src/components/ui/field.tsx
|
|
7
|
+
const fieldVariants = cva("group/field flex w-full gap-2 data-[invalid=true]:text-destructive", {
|
|
8
|
+
variants: { orientation: {
|
|
9
|
+
vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
|
|
10
|
+
horizontal: "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
11
|
+
responsive: "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
|
|
12
|
+
} },
|
|
13
|
+
defaultVariants: { orientation: "vertical" }
|
|
14
|
+
});
|
|
15
|
+
const Field = ({ className, orientation = "vertical", ...props }) => {
|
|
16
|
+
return /* @__PURE__ */ jsx("div", {
|
|
17
|
+
role: "group",
|
|
18
|
+
"data-slot": "field",
|
|
19
|
+
"data-orientation": orientation,
|
|
20
|
+
className: cn(fieldVariants({ orientation }), className),
|
|
21
|
+
...props
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const FieldContent = ({ className, ...props }) => {
|
|
25
|
+
return /* @__PURE__ */ jsx("div", {
|
|
26
|
+
"data-slot": "field-content",
|
|
27
|
+
className: cn("group/field-content flex flex-1 flex-col gap-0.5 leading-snug", className),
|
|
28
|
+
...props
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const FieldLabel = ({ className, ...props }) => {
|
|
32
|
+
return /* @__PURE__ */ jsx(Label, {
|
|
33
|
+
"data-slot": "field-label",
|
|
34
|
+
className: cn("group/field-label peer/field-label has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 *:data-[slot=field]:p-2.5", "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col", className),
|
|
35
|
+
...props
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
const FieldDescription = ({ className, ...props }) => {
|
|
39
|
+
return /* @__PURE__ */ jsx("p", {
|
|
40
|
+
"data-slot": "field-description",
|
|
41
|
+
className: cn("text-muted-foreground text-left text-sm leading-normal font-normal group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5", "last:mt-0 nth-last-2:-mt-1", "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", className),
|
|
42
|
+
...props
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
const FieldError = ({ className, children, errors, ...props }) => {
|
|
46
|
+
const content = useMemo(() => {
|
|
47
|
+
if (children) return children;
|
|
48
|
+
if (!errors?.length) return null;
|
|
49
|
+
const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
|
|
50
|
+
if (uniqueErrors?.length == 1) return uniqueErrors[0]?.message;
|
|
51
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
52
|
+
className: "ml-4 flex list-disc flex-col gap-1",
|
|
53
|
+
children: uniqueErrors.map((error, index) => error?.message && /* @__PURE__ */ jsx("li", { children: error.message }, index))
|
|
54
|
+
});
|
|
55
|
+
}, [children, errors]);
|
|
56
|
+
if (!content) return null;
|
|
57
|
+
return /* @__PURE__ */ jsx("div", {
|
|
58
|
+
role: "alert",
|
|
59
|
+
"data-slot": "field-error",
|
|
60
|
+
className: cn("text-destructive text-sm font-normal", className),
|
|
61
|
+
...props,
|
|
62
|
+
children: content
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
export { Field, FieldContent, FieldDescription, FieldError, FieldLabel };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import "react";
|
|
4
|
+
import { Input } from "@base-ui/react/input";
|
|
5
|
+
// src/components/ui/input.tsx
|
|
6
|
+
const Input$1 = ({ className, type, ...props }) => {
|
|
7
|
+
return /* @__PURE__ */ jsx(Input, {
|
|
8
|
+
type,
|
|
9
|
+
"data-slot": "input",
|
|
10
|
+
className: cn("border-input file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 disabled:bg-input/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-8 w-full min-w-0 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm", className),
|
|
11
|
+
...props
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
export { Input$1 as Input };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import "react";
|
|
4
|
+
// src/components/ui/label.tsx
|
|
5
|
+
const Label = ({ className, ...props }) => {
|
|
6
|
+
return /* @__PURE__ */ jsx("label", {
|
|
7
|
+
"data-slot": "label",
|
|
8
|
+
className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className),
|
|
9
|
+
...props
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
export { Label };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
4
|
+
import "react";
|
|
5
|
+
import { Select } from "@base-ui/react/select";
|
|
6
|
+
// src/components/ui/select.tsx
|
|
7
|
+
const Select$1 = Select.Root;
|
|
8
|
+
const SelectValue = ({ className, ...props }) => {
|
|
9
|
+
return /* @__PURE__ */ jsx(Select.Value, {
|
|
10
|
+
"data-slot": "select-value",
|
|
11
|
+
className: cn("flex flex-1 text-left", className),
|
|
12
|
+
...props
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const SelectTrigger = ({ className, size = "default", children, ...props }) => {
|
|
16
|
+
return /* @__PURE__ */ jsxs(Select.Trigger, {
|
|
17
|
+
"data-slot": "select-trigger",
|
|
18
|
+
"data-size": size,
|
|
19
|
+
className: cn("border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 flex w-fit items-center justify-between gap-1.5 rounded-lg border bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
|
|
20
|
+
...props,
|
|
21
|
+
children: [children, /* @__PURE__ */ jsx(Select.Icon, { render: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "text-muted-foreground pointer-events-none size-4" }) })]
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const SelectContent = ({ className, children, side = "bottom", sideOffset = 4, align = "center", alignOffset = 0, alignItemWithTrigger = true, ...props }) => {
|
|
25
|
+
return /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, {
|
|
26
|
+
side,
|
|
27
|
+
sideOffset,
|
|
28
|
+
align,
|
|
29
|
+
alignOffset,
|
|
30
|
+
alignItemWithTrigger,
|
|
31
|
+
className: "isolate z-50",
|
|
32
|
+
children: /* @__PURE__ */ jsxs(Select.Popup, {
|
|
33
|
+
"data-slot": "select-content",
|
|
34
|
+
"data-align-trigger": alignItemWithTrigger,
|
|
35
|
+
className: cn("bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg shadow-md ring-1 duration-100 data-[align-trigger=true]:animate-none", className),
|
|
36
|
+
...props,
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsx(SelectScrollUpButton, {}),
|
|
39
|
+
/* @__PURE__ */ jsx(Select.List, { children }),
|
|
40
|
+
/* @__PURE__ */ jsx(SelectScrollDownButton, {})
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
}) });
|
|
44
|
+
};
|
|
45
|
+
const SelectItem = ({ className, children, ...props }) => {
|
|
46
|
+
return /* @__PURE__ */ jsxs(Select.Item, {
|
|
47
|
+
"data-slot": "select-item",
|
|
48
|
+
className: cn("focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className),
|
|
49
|
+
...props,
|
|
50
|
+
children: [/* @__PURE__ */ jsx(Select.ItemText, {
|
|
51
|
+
className: "flex flex-1 shrink-0 gap-2 whitespace-nowrap",
|
|
52
|
+
children
|
|
53
|
+
}), /* @__PURE__ */ jsx(Select.ItemIndicator, {
|
|
54
|
+
render: /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center" }),
|
|
55
|
+
children: /* @__PURE__ */ jsx(CheckIcon, { className: "pointer-events-none" })
|
|
56
|
+
})]
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const SelectScrollUpButton = ({ className, ...props }) => {
|
|
60
|
+
return /* @__PURE__ */ jsx(Select.ScrollUpArrow, {
|
|
61
|
+
"data-slot": "select-scroll-up-button",
|
|
62
|
+
className: cn("bg-popover top-0 z-10 flex w-full cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className),
|
|
63
|
+
...props,
|
|
64
|
+
children: /* @__PURE__ */ jsx(ChevronUpIcon, {})
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
const SelectScrollDownButton = ({ className, ...props }) => {
|
|
68
|
+
return /* @__PURE__ */ jsx(Select.ScrollDownArrow, {
|
|
69
|
+
"data-slot": "select-scroll-down-button",
|
|
70
|
+
className: cn("bg-popover bottom-0 z-10 flex w-full cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4", className),
|
|
71
|
+
...props,
|
|
72
|
+
children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
export { Select$1 as Select, SelectContent, SelectItem, SelectScrollDownButton, SelectScrollUpButton, SelectTrigger, SelectValue };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Separator } from "@base-ui/react/separator";
|
|
4
|
+
// src/components/ui/separator.tsx
|
|
5
|
+
const Separator$1 = ({ className, orientation = "horizontal", ...props }) => {
|
|
6
|
+
return /* @__PURE__ */ jsx(Separator, {
|
|
7
|
+
"data-slot": "separator",
|
|
8
|
+
orientation,
|
|
9
|
+
className: cn("bg-border shrink-0 data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch", className),
|
|
10
|
+
...props
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
export { Separator$1 as Separator };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
// src/components/ui/skeleton.tsx
|
|
4
|
+
const Skeleton = ({ className, ...props }) => {
|
|
5
|
+
return /* @__PURE__ */ jsx("div", {
|
|
6
|
+
"data-slot": "skeleton",
|
|
7
|
+
className: cn("bg-muted animate-pulse rounded-md", className),
|
|
8
|
+
...props
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
export { Skeleton };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Loader2Icon } from "lucide-react";
|
|
4
|
+
// src/components/ui/spinner.tsx
|
|
5
|
+
const Spinner = ({ className, ...props }) => {
|
|
6
|
+
return /* @__PURE__ */ jsx(Loader2Icon, {
|
|
7
|
+
"data-slot": "spinner",
|
|
8
|
+
role: "status",
|
|
9
|
+
"aria-label": "Loading",
|
|
10
|
+
className: cn("size-4 animate-spin", className),
|
|
11
|
+
...props
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
export { Spinner };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "cn";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { Switch } from "@base-ui/react/switch";
|
|
5
|
+
// src/components/ui/switch.tsx
|
|
6
|
+
const Switch$1 = ({ className, size = "default", ...props }) => {
|
|
7
|
+
return /* @__PURE__ */ jsx(Switch.Root, {
|
|
8
|
+
"data-slot": "switch",
|
|
9
|
+
"data-size": size,
|
|
10
|
+
className: cn("peer group/switch focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none group-has-[:focus-visible]/field-label:border-transparent group-has-[:focus-visible]/field-label:ring-0 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:ring-3 aria-invalid:ring-3 data-disabled:cursor-not-allowed data-disabled:opacity-50 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px]", className),
|
|
11
|
+
...props,
|
|
12
|
+
children: /* @__PURE__ */ jsx(Switch.Thumb, {
|
|
13
|
+
"data-slot": "switch-thumb",
|
|
14
|
+
className: "bg-background dark:data-checked:bg-primary-foreground dark:data-unchecked:bg-foreground pointer-events-none block rounded-full ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0"
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export { Switch$1 as Switch };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import "react";
|
|
4
|
+
// src/components/ui/textarea.tsx
|
|
5
|
+
const Textarea = ({ className, ...props }) => {
|
|
6
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
7
|
+
"data-slot": "textarea",
|
|
8
|
+
className: cn("border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 disabled:bg-input/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 flex field-sizing-content min-h-16 w-full rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm", className),
|
|
9
|
+
...props
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
export { Textarea };
|