@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,11 @@
|
|
|
1
|
+
import { IconComponent, Tone } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface BadgeProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
tone?: Tone;
|
|
6
|
+
/** Leading icon, decorative (aria-hidden). */
|
|
7
|
+
icon?: IconComponent;
|
|
8
|
+
id?: string;
|
|
9
|
+
'data-testid'?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const Badge: React.FC<BadgeProps>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Badge as Badge$1 } from "../../ui/badge.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
// src/components/atomic/Badge/Badge.tsx
|
|
4
|
+
const PRIMITIVE_VARIANT = {
|
|
5
|
+
neutral: "secondary",
|
|
6
|
+
brand: "secondary",
|
|
7
|
+
success: "secondary",
|
|
8
|
+
warning: "secondary",
|
|
9
|
+
danger: "destructive",
|
|
10
|
+
info: "secondary"
|
|
11
|
+
};
|
|
12
|
+
const TONE_CLASS = {
|
|
13
|
+
brand: "bg-brand-tint text-brand-tint-foreground",
|
|
14
|
+
success: "bg-success/10 text-success dark:bg-success/20",
|
|
15
|
+
warning: "bg-warning/10 text-warning dark:bg-warning/20",
|
|
16
|
+
info: "bg-info/10 text-info dark:bg-info/20"
|
|
17
|
+
};
|
|
18
|
+
const Badge = (props) => {
|
|
19
|
+
const { children, tone = "neutral", icon: Icon, id, "data-testid": testId } = props;
|
|
20
|
+
return /* @__PURE__ */ jsxs(Badge$1, {
|
|
21
|
+
id,
|
|
22
|
+
"data-testid": testId,
|
|
23
|
+
variant: PRIMITIVE_VARIANT[tone],
|
|
24
|
+
className: TONE_CLASS[tone],
|
|
25
|
+
children: [Icon && /* @__PURE__ */ jsx(Icon, {
|
|
26
|
+
"data-icon": "inline-start",
|
|
27
|
+
"aria-hidden": true
|
|
28
|
+
}), children]
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
export { Badge };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IconComponent, Size } from "../../../lib/types.js";
|
|
2
|
+
import { ButtonVariant } from "./buttonVariant.js";
|
|
2
3
|
import React from "react";
|
|
3
|
-
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link';
|
|
4
4
|
export interface ButtonProps {
|
|
5
5
|
variant?: ButtonVariant;
|
|
6
6
|
size?: Size;
|
|
@@ -19,4 +19,5 @@ export interface ButtonProps {
|
|
|
19
19
|
'aria-describedby'?: string;
|
|
20
20
|
'data-testid'?: string;
|
|
21
21
|
}
|
|
22
|
-
export declare const Button: React.FC<ButtonProps>;
|
|
22
|
+
export declare const Button: React.FC<ButtonProps>;
|
|
23
|
+
export type { ButtonVariant };
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { Button as Button$1 } from "../../ui/button.js";
|
|
2
2
|
import { cn } from "../../../lib/utils.js";
|
|
3
|
-
import {
|
|
3
|
+
import { BUTTON_VARIANT } from "./buttonVariant.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { LoaderCircleIcon } from "lucide-react";
|
|
5
6
|
// src/components/atomic/Button/Button.tsx
|
|
6
|
-
const VARIANT = {
|
|
7
|
-
primary: "default",
|
|
8
|
-
secondary: "secondary",
|
|
9
|
-
outline: "outline",
|
|
10
|
-
ghost: "ghost",
|
|
11
|
-
destructive: "destructive",
|
|
12
|
-
link: "link"
|
|
13
|
-
};
|
|
14
7
|
const VARIANT_CLASS = { link: "text-brand-text" };
|
|
15
8
|
const SIZE = {
|
|
16
9
|
sm: "sm",
|
|
@@ -24,7 +17,7 @@ const Button = (props) => {
|
|
|
24
17
|
ref,
|
|
25
18
|
id,
|
|
26
19
|
type,
|
|
27
|
-
variant:
|
|
20
|
+
variant: BUTTON_VARIANT[variant],
|
|
28
21
|
size: SIZE[size],
|
|
29
22
|
className: cn(VARIANT_CLASS[variant], fullWidth && "w-full"),
|
|
30
23
|
disabled: disabled || loading,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FieldBaseProps } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface CheckboxProps extends FieldBaseProps {
|
|
4
|
+
value: boolean;
|
|
5
|
+
onChange: (value: boolean) => void;
|
|
6
|
+
onBlur?: () => void;
|
|
7
|
+
name?: string;
|
|
8
|
+
readOnly?: boolean;
|
|
9
|
+
ref?: React.Ref<HTMLElement>;
|
|
10
|
+
}
|
|
11
|
+
export declare const Checkbox: React.FC<CheckboxProps>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { FormField } from "../../molecule/FormField/FormField.js";
|
|
2
|
+
import { Checkbox as Checkbox$1 } from "../../ui/checkbox.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
// src/components/atomic/Checkbox/Checkbox.tsx
|
|
5
|
+
const Checkbox = (props) => {
|
|
6
|
+
const { label, hideLabel, description, error, required, disabled, id, "data-testid": testId, value, onChange, onBlur, name, readOnly = false, ref } = props;
|
|
7
|
+
return /* @__PURE__ */ jsx(FormField, {
|
|
8
|
+
orientation: "horizontal",
|
|
9
|
+
label,
|
|
10
|
+
hideLabel,
|
|
11
|
+
description,
|
|
12
|
+
error,
|
|
13
|
+
required,
|
|
14
|
+
disabled,
|
|
15
|
+
id,
|
|
16
|
+
"data-testid": testId,
|
|
17
|
+
children: (control) => /* @__PURE__ */ jsx(Checkbox$1, {
|
|
18
|
+
ref,
|
|
19
|
+
id: control.id,
|
|
20
|
+
"aria-describedby": control["aria-describedby"],
|
|
21
|
+
"aria-invalid": control["aria-invalid"],
|
|
22
|
+
"aria-required": control["aria-required"],
|
|
23
|
+
disabled: control.disabled,
|
|
24
|
+
checked: value,
|
|
25
|
+
onCheckedChange: (checked) => onChange(checked),
|
|
26
|
+
onBlur,
|
|
27
|
+
name,
|
|
28
|
+
readOnly
|
|
29
|
+
})
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
export { Checkbox };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FieldBaseProps } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface ColorInputProps extends FieldBaseProps {
|
|
4
|
+
/** 6-digit hex, e.g. '#1a73e8' (orgs.primaryColor), or the text being typed. */
|
|
5
|
+
value: string;
|
|
6
|
+
/**
|
|
7
|
+
* A valid 6-digit hex arrives normalized ('#1a73e8'); anything else arrives as typed, so the
|
|
8
|
+
* form's validation can flag it (e.g. `validate: v => /^#[0-9a-f]{6}$/.test(v) || '…'`).
|
|
9
|
+
*/
|
|
10
|
+
onChange: (value: string) => void;
|
|
11
|
+
onBlur?: () => void;
|
|
12
|
+
name?: string;
|
|
13
|
+
ref?: React.Ref<HTMLInputElement>;
|
|
14
|
+
}
|
|
15
|
+
export declare const ColorInput: React.FC<ColorInputProps>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { FormField } from "../../molecule/FormField/FormField.js";
|
|
2
|
+
import { Input } from "../../ui/input.js";
|
|
3
|
+
import { normalizeHex } from "./colorHex.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
// src/components/atomic/ColorInput/ColorInput.tsx
|
|
6
|
+
const ColorInput = (props) => {
|
|
7
|
+
const { label, hideLabel, description, error, required, disabled, id, "data-testid": testId, value, onChange, onBlur, name, ref } = props;
|
|
8
|
+
const handleText = (next) => onChange(normalizeHex(next) ?? next);
|
|
9
|
+
return /* @__PURE__ */ jsx(FormField, {
|
|
10
|
+
label,
|
|
11
|
+
hideLabel,
|
|
12
|
+
description,
|
|
13
|
+
error,
|
|
14
|
+
required,
|
|
15
|
+
disabled,
|
|
16
|
+
id,
|
|
17
|
+
"data-testid": testId,
|
|
18
|
+
children: (control) => /* @__PURE__ */ jsxs("div", {
|
|
19
|
+
className: "flex items-center gap-2",
|
|
20
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
21
|
+
type: "color",
|
|
22
|
+
"aria-label": `${label} picker`,
|
|
23
|
+
value: normalizeHex(value) ?? "#000000",
|
|
24
|
+
disabled: control.disabled,
|
|
25
|
+
onChange: (event) => handleText(event.target.value),
|
|
26
|
+
className: "border-input h-8 w-10 shrink-0 cursor-pointer rounded-lg border bg-transparent p-0.5 disabled:cursor-not-allowed disabled:opacity-50"
|
|
27
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
28
|
+
ref,
|
|
29
|
+
id: control.id,
|
|
30
|
+
"aria-describedby": control["aria-describedby"],
|
|
31
|
+
"aria-invalid": control["aria-invalid"],
|
|
32
|
+
"aria-required": control["aria-required"],
|
|
33
|
+
disabled: control.disabled,
|
|
34
|
+
name,
|
|
35
|
+
value,
|
|
36
|
+
spellCheck: false,
|
|
37
|
+
autoComplete: "off",
|
|
38
|
+
onChange: (event) => handleText(event.target.value),
|
|
39
|
+
onBlur
|
|
40
|
+
})]
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
export { ColorInput };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// src/components/atomic/ColorInput/colorHex.ts
|
|
2
|
+
const HEX6 = /^#?([0-9a-f]{6})$/i;
|
|
3
|
+
/** '#1A73E8' | '1a73e8' → '#1a73e8'; anything that is not 6 hex digits → null. */
|
|
4
|
+
const normalizeHex = (input) => {
|
|
5
|
+
const match = HEX6.exec(input.trim());
|
|
6
|
+
return match ? `#${match[1].toLowerCase()}` : null;
|
|
7
|
+
};
|
|
8
|
+
export { normalizeHex };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface DividerProps {
|
|
3
|
+
/** A vertical divider stretches to its flex row's height. */
|
|
4
|
+
orientation?: 'horizontal' | 'vertical';
|
|
5
|
+
id?: string;
|
|
6
|
+
'data-testid'?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const Divider: React.FC<DividerProps>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Separator } from "../../ui/separator.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
// src/components/atomic/Divider/Divider.tsx
|
|
4
|
+
const Divider = (props) => {
|
|
5
|
+
const { orientation = "horizontal", id, "data-testid": testId } = props;
|
|
6
|
+
return /* @__PURE__ */ jsx(Separator, {
|
|
7
|
+
orientation,
|
|
8
|
+
id,
|
|
9
|
+
"data-testid": testId
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
export { Divider };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
3
|
+
export interface HeadingProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
/** Renders h1–h6; the visual size follows the level. */
|
|
6
|
+
level: HeadingLevel;
|
|
7
|
+
/** One line with an ellipsis; the parent must constrain the width. */
|
|
8
|
+
truncate?: boolean;
|
|
9
|
+
id?: string;
|
|
10
|
+
'data-testid'?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const Heading: React.FC<HeadingProps>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
// src/components/atomic/Heading/Heading.tsx
|
|
4
|
+
const ELEMENT = {
|
|
5
|
+
1: "h1",
|
|
6
|
+
2: "h2",
|
|
7
|
+
3: "h3",
|
|
8
|
+
4: "h4",
|
|
9
|
+
5: "h5",
|
|
10
|
+
6: "h6"
|
|
11
|
+
};
|
|
12
|
+
const SIZE = {
|
|
13
|
+
1: "text-2xl tracking-tight",
|
|
14
|
+
2: "text-xl tracking-tight",
|
|
15
|
+
3: "text-lg",
|
|
16
|
+
4: "text-base",
|
|
17
|
+
5: "text-sm",
|
|
18
|
+
6: "text-xs"
|
|
19
|
+
};
|
|
20
|
+
const Heading = (props) => {
|
|
21
|
+
const { children, level, truncate = false, id, "data-testid": testId } = props;
|
|
22
|
+
const Element = ELEMENT[level];
|
|
23
|
+
return /* @__PURE__ */ jsx(Element, {
|
|
24
|
+
id,
|
|
25
|
+
"data-testid": testId,
|
|
26
|
+
className: cn("font-heading text-foreground font-semibold", SIZE[level], truncate && "truncate"),
|
|
27
|
+
children
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
export { Heading };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ButtonVariant } from "../Button/buttonVariant.js";
|
|
2
|
+
import { IconComponent, Size } from "../../../lib/types.js";
|
|
3
|
+
import React from "react";
|
|
4
|
+
export type IconButtonVariant = Exclude<ButtonVariant, 'link'>;
|
|
5
|
+
export interface IconButtonProps {
|
|
6
|
+
icon: IconComponent;
|
|
7
|
+
/** Required: an icon-only button has no other accessible name. */
|
|
8
|
+
'aria-label': string;
|
|
9
|
+
variant?: IconButtonVariant;
|
|
10
|
+
size?: Size;
|
|
11
|
+
/** Disables the button, sets aria-busy and swaps the icon for a spinner. */
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
type?: 'button' | 'submit';
|
|
15
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
16
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
17
|
+
id?: string;
|
|
18
|
+
'aria-describedby'?: string;
|
|
19
|
+
'data-testid'?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const IconButton: React.FC<IconButtonProps>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { BUTTON_VARIANT } from "../Button/buttonVariant.js";
|
|
2
|
+
import { Button } from "../../ui/button.js";
|
|
3
|
+
import { cn } from "../../../lib/utils.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { LoaderCircleIcon } from "lucide-react";
|
|
6
|
+
// src/components/atomic/IconButton/IconButton.tsx
|
|
7
|
+
const SIZE = {
|
|
8
|
+
sm: "icon-sm",
|
|
9
|
+
md: "icon",
|
|
10
|
+
lg: "icon-lg"
|
|
11
|
+
};
|
|
12
|
+
const IconButton = (props) => {
|
|
13
|
+
const { icon, "aria-label": ariaLabel, variant = "ghost", size = "md", loading = false, disabled = false, type = "button", onClick, ref, id, "aria-describedby": ariaDescribedBy, "data-testid": testId } = props;
|
|
14
|
+
const Icon = loading ? LoaderCircleIcon : icon;
|
|
15
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
16
|
+
ref,
|
|
17
|
+
id,
|
|
18
|
+
type,
|
|
19
|
+
variant: BUTTON_VARIANT[variant],
|
|
20
|
+
size: SIZE[size],
|
|
21
|
+
disabled: disabled || loading,
|
|
22
|
+
focusableWhenDisabled: loading,
|
|
23
|
+
"aria-busy": loading || void 0,
|
|
24
|
+
"aria-label": ariaLabel,
|
|
25
|
+
"aria-describedby": ariaDescribedBy,
|
|
26
|
+
"data-testid": testId,
|
|
27
|
+
onClick,
|
|
28
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
29
|
+
"aria-hidden": true,
|
|
30
|
+
className: cn(loading && "animate-spin")
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
export { IconButton };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FieldBaseProps } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface SelectOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface SelectProps extends FieldBaseProps {
|
|
9
|
+
options: SelectOption[];
|
|
10
|
+
/** null = nothing picked; the placeholder shows. */
|
|
11
|
+
value: string | null;
|
|
12
|
+
onChange: (value: string) => void;
|
|
13
|
+
onBlur?: () => void;
|
|
14
|
+
name?: string;
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
17
|
+
}
|
|
18
|
+
export declare const Select: React.FC<SelectProps>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { FormField } from "../../molecule/FormField/FormField.js";
|
|
2
|
+
import { Select as Select$1, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../ui/select.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
// src/components/atomic/Select/Select.tsx
|
|
5
|
+
const LEFT_FIELD_REASONS = /* @__PURE__ */ new Set(["outside-press", "focus-out"]);
|
|
6
|
+
const Select = (props) => {
|
|
7
|
+
const { label, hideLabel, description, error, required, disabled, id, "data-testid": testId, options, value, onChange, onBlur, name, placeholder, 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(Select$1, {
|
|
18
|
+
items: options,
|
|
19
|
+
value,
|
|
20
|
+
onValueChange: (next) => {
|
|
21
|
+
if (next !== null) onChange(next);
|
|
22
|
+
},
|
|
23
|
+
onOpenChange: (open, details) => {
|
|
24
|
+
if (!open && LEFT_FIELD_REASONS.has(details.reason)) onBlur?.();
|
|
25
|
+
},
|
|
26
|
+
name,
|
|
27
|
+
disabled: control.disabled,
|
|
28
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
29
|
+
ref,
|
|
30
|
+
id: control.id,
|
|
31
|
+
"aria-describedby": control["aria-describedby"],
|
|
32
|
+
"aria-invalid": control["aria-invalid"],
|
|
33
|
+
"aria-required": control["aria-required"],
|
|
34
|
+
onBlur: (event) => {
|
|
35
|
+
if (!event.relatedTarget?.closest("[data-slot=\"select-content\"]")) onBlur?.();
|
|
36
|
+
},
|
|
37
|
+
className: "w-full",
|
|
38
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder })
|
|
39
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, {
|
|
40
|
+
value: option.value,
|
|
41
|
+
disabled: option.disabled,
|
|
42
|
+
children: option.label
|
|
43
|
+
}, option.value)) })]
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
export { Select };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type SkeletonShape = 'text' | 'circle' | 'block';
|
|
3
|
+
export interface SkeletonProps {
|
|
4
|
+
/** Fills the wrapper's width; the app sizes it with a plain div. */
|
|
5
|
+
shape?: SkeletonShape;
|
|
6
|
+
'data-testid'?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const Skeleton: React.FC<SkeletonProps>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Skeleton as Skeleton$1 } from "../../ui/skeleton.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
// src/components/atomic/Skeleton/Skeleton.tsx
|
|
4
|
+
const SHAPE = {
|
|
5
|
+
text: "h-4 w-full",
|
|
6
|
+
circle: "aspect-square w-full rounded-full",
|
|
7
|
+
block: "size-full"
|
|
8
|
+
};
|
|
9
|
+
const Skeleton = (props) => {
|
|
10
|
+
const { shape = "text", "data-testid": testId } = props;
|
|
11
|
+
return /* @__PURE__ */ jsx(Skeleton$1, {
|
|
12
|
+
"aria-hidden": true,
|
|
13
|
+
"data-testid": testId,
|
|
14
|
+
className: SHAPE[shape]
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
export { Skeleton };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Size } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface SpinnerProps {
|
|
4
|
+
size?: Size;
|
|
5
|
+
/** Accessible name announced by screen readers. */
|
|
6
|
+
label?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
'data-testid'?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const Spinner: React.FC<SpinnerProps>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Spinner as Spinner$1 } from "../../ui/spinner.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
// src/components/atomic/Spinner/Spinner.tsx
|
|
4
|
+
const SIZE = {
|
|
5
|
+
sm: "size-4",
|
|
6
|
+
md: "size-5",
|
|
7
|
+
lg: "size-8"
|
|
8
|
+
};
|
|
9
|
+
const Spinner = (props) => {
|
|
10
|
+
const { size = "md", label = "Loading", id, "data-testid": testId } = props;
|
|
11
|
+
return /* @__PURE__ */ jsx(Spinner$1, {
|
|
12
|
+
id,
|
|
13
|
+
"data-testid": testId,
|
|
14
|
+
"aria-label": label,
|
|
15
|
+
className: SIZE[size]
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
export { Spinner };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FieldBaseProps } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface SwitchProps extends FieldBaseProps {
|
|
4
|
+
value: boolean;
|
|
5
|
+
onChange: (value: boolean) => void;
|
|
6
|
+
onBlur?: () => void;
|
|
7
|
+
name?: string;
|
|
8
|
+
readOnly?: boolean;
|
|
9
|
+
ref?: React.Ref<HTMLElement>;
|
|
10
|
+
}
|
|
11
|
+
export declare const Switch: React.FC<SwitchProps>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { FormField } from "../../molecule/FormField/FormField.js";
|
|
2
|
+
import { Switch as Switch$1 } from "../../ui/switch.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
// src/components/atomic/Switch/Switch.tsx
|
|
5
|
+
const Switch = (props) => {
|
|
6
|
+
const { label, hideLabel, description, error, required, disabled, id, "data-testid": testId, value, onChange, onBlur, name, readOnly = false, ref } = props;
|
|
7
|
+
return /* @__PURE__ */ jsx(FormField, {
|
|
8
|
+
orientation: "horizontal",
|
|
9
|
+
label,
|
|
10
|
+
hideLabel,
|
|
11
|
+
description,
|
|
12
|
+
error,
|
|
13
|
+
required,
|
|
14
|
+
disabled,
|
|
15
|
+
id,
|
|
16
|
+
"data-testid": testId,
|
|
17
|
+
children: (control) => /* @__PURE__ */ jsx(Switch$1, {
|
|
18
|
+
ref,
|
|
19
|
+
id: control.id,
|
|
20
|
+
"aria-describedby": control["aria-describedby"],
|
|
21
|
+
"aria-invalid": control["aria-invalid"],
|
|
22
|
+
"aria-required": control["aria-required"],
|
|
23
|
+
disabled: control.disabled,
|
|
24
|
+
checked: value,
|
|
25
|
+
onCheckedChange: (checked) => onChange(checked),
|
|
26
|
+
onBlur,
|
|
27
|
+
name,
|
|
28
|
+
readOnly
|
|
29
|
+
})
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
export { Switch };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Tone } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export type TextSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
4
|
+
export type TextTone = Tone | 'muted';
|
|
5
|
+
export type TextWeight = 'normal' | 'medium' | 'semibold';
|
|
6
|
+
export interface TextProps {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
as?: 'p' | 'span' | 'div';
|
|
9
|
+
/** Leave unset to inherit the parent's size (the page default is md). */
|
|
10
|
+
size?: TextSize;
|
|
11
|
+
/** Leave unset to inherit the parent's colour. */
|
|
12
|
+
tone?: TextTone;
|
|
13
|
+
/** Leave unset to inherit the parent's weight. */
|
|
14
|
+
weight?: TextWeight;
|
|
15
|
+
/** One line with an ellipsis; the parent must constrain the width. */
|
|
16
|
+
truncate?: boolean;
|
|
17
|
+
id?: string;
|
|
18
|
+
'data-testid'?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare const Text: React.FC<TextProps>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
// src/components/atomic/Text/Text.tsx
|
|
4
|
+
const SIZE = {
|
|
5
|
+
xs: "text-xs",
|
|
6
|
+
sm: "text-sm",
|
|
7
|
+
md: "text-base",
|
|
8
|
+
lg: "text-lg"
|
|
9
|
+
};
|
|
10
|
+
const TONE = {
|
|
11
|
+
neutral: "text-foreground",
|
|
12
|
+
muted: "text-muted-foreground",
|
|
13
|
+
brand: "text-brand-text",
|
|
14
|
+
success: "text-success",
|
|
15
|
+
warning: "text-warning",
|
|
16
|
+
danger: "text-destructive",
|
|
17
|
+
info: "text-info"
|
|
18
|
+
};
|
|
19
|
+
const WEIGHT = {
|
|
20
|
+
normal: "font-normal",
|
|
21
|
+
medium: "font-medium",
|
|
22
|
+
semibold: "font-semibold"
|
|
23
|
+
};
|
|
24
|
+
const Text = (props) => {
|
|
25
|
+
const { children, as: Element = "p", size, tone, weight, truncate = false, id, "data-testid": testId } = props;
|
|
26
|
+
return /* @__PURE__ */ jsx(Element, {
|
|
27
|
+
id,
|
|
28
|
+
"data-testid": testId,
|
|
29
|
+
className: cn(size && SIZE[size], tone && TONE[tone], weight && WEIGHT[weight], truncate && "block truncate"),
|
|
30
|
+
children
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
export { Text };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FieldBaseProps } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface TextAreaProps extends FieldBaseProps {
|
|
4
|
+
value: string;
|
|
5
|
+
onChange: (value: string) => void;
|
|
6
|
+
onBlur?: () => void;
|
|
7
|
+
name?: string;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
rows?: number;
|
|
10
|
+
maxLength?: number;
|
|
11
|
+
readOnly?: boolean;
|
|
12
|
+
ref?: React.Ref<HTMLTextAreaElement>;
|
|
13
|
+
}
|
|
14
|
+
export declare const TextArea: React.FC<TextAreaProps>;
|