@ashwa-io/ui 0.0.3 → 0.0.5
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/Button/buttonLook.js +18 -0
- 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/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/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/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/atomic/Tooltip/Tooltip.d.ts +14 -0
- package/dist/components/atomic/Tooltip/Tooltip.js +14 -0
- package/dist/components/atomic/Tooltip/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/molecule/ConfirmDialog/ConfirmDialog.d.ts +17 -0
- package/dist/components/molecule/ConfirmDialog/ConfirmDialog.js +28 -0
- package/dist/components/molecule/ConfirmDialog/index.d.ts +2 -0
- package/dist/components/molecule/DropdownMenu/DropdownMenu.d.ts +28 -0
- package/dist/components/molecule/DropdownMenu/DropdownMenu.js +53 -0
- package/dist/components/molecule/DropdownMenu/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/molecule/Modal/Modal.d.ts +22 -0
- package/dist/components/molecule/Modal/Modal.js +48 -0
- package/dist/components/molecule/Modal/index.d.ts +2 -0
- package/dist/components/molecule/Popover/Popover.d.ts +24 -0
- package/dist/components/molecule/Popover/Popover.js +38 -0
- package/dist/components/molecule/Popover/index.d.ts +2 -0
- package/dist/components/molecule/Toaster/Toaster.d.ts +3 -0
- package/dist/components/molecule/Toaster/Toaster.js +19 -0
- package/dist/components/molecule/Toaster/index.d.ts +3 -0
- package/dist/components/molecule/Toaster/toast.d.ts +13 -0
- package/dist/components/molecule/Toaster/toast.js +18 -0
- package/dist/components/ui/alert-dialog.js +61 -0
- package/dist/components/ui/checkbox.js +18 -0
- package/dist/components/ui/dialog.js +78 -0
- package/dist/components/ui/dropdown-menu.js +42 -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/popover.js +32 -0
- package/dist/components/ui/select.js +75 -0
- package/dist/components/ui/switch.js +18 -0
- package/dist/components/ui/textarea.js +12 -0
- package/dist/components/ui/tooltip.js +39 -0
- package/dist/index.d.ts +31 -2
- package/dist/index.js +16 -1
- package/dist/lib/types.d.ts +19 -1
- package/dist/theme/brandTheme.d.ts +1 -1
- package/dist/theme/brandTheme.js +10 -8
- package/package.json +2 -1
- package/src/styles/index.css +45 -43
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BUTTON_VARIANT } from "./buttonVariant.js";
|
|
2
|
+
// src/components/atomic/Button/buttonLook.ts
|
|
3
|
+
const TEXT_SIZE = {
|
|
4
|
+
sm: "sm",
|
|
5
|
+
md: "default",
|
|
6
|
+
lg: "lg"
|
|
7
|
+
};
|
|
8
|
+
const ICON_SIZE = {
|
|
9
|
+
sm: "icon-sm",
|
|
10
|
+
md: "icon",
|
|
11
|
+
lg: "icon-lg"
|
|
12
|
+
};
|
|
13
|
+
/** Primitive props for an overlay trigger drawn in the public Button look. */
|
|
14
|
+
const buttonLook = (variant, size, iconOnly) => ({
|
|
15
|
+
variant: BUTTON_VARIANT[variant],
|
|
16
|
+
size: (iconOnly ? ICON_SIZE : TEXT_SIZE)[size]
|
|
17
|
+
});
|
|
18
|
+
export { buttonLook };
|
|
@@ -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,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,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,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>;
|
|
@@ -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,14 @@
|
|
|
1
|
+
import { OverlaySide } from "../../../lib/types.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface TooltipProps {
|
|
4
|
+
/**
|
|
5
|
+
* Short plain text, a visual hint only: screen readers don't get it, and it needs a focusable
|
|
6
|
+
* child to appear for keyboards. Never put essential information only here.
|
|
7
|
+
*/
|
|
8
|
+
content: string;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
side?: OverlaySide;
|
|
11
|
+
/** Lets a full-width child (e.g. `<Button fullWidth>`) fill its container. */
|
|
12
|
+
fullWidth?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare const Tooltip: React.FC<TooltipProps>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Tooltip as Tooltip$1, TooltipContent, TooltipProvider, TooltipTrigger } from "../../ui/tooltip.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
// src/components/atomic/Tooltip/Tooltip.tsx
|
|
4
|
+
const Tooltip = (props) => {
|
|
5
|
+
const { content, children, side = "top", fullWidth = false } = props;
|
|
6
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
7
|
+
render: /* @__PURE__ */ jsx("span", { className: fullWidth ? "flex w-full" : "inline-flex max-w-full min-w-0" }),
|
|
8
|
+
children
|
|
9
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
10
|
+
side,
|
|
11
|
+
children: content
|
|
12
|
+
})] }) });
|
|
13
|
+
};
|
|
14
|
+
export { Tooltip };
|
|
@@ -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,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface ConfirmDialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
/** Cancel and Escape. */
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
/** Never closes the dialog: the app closes it when its work finishes. */
|
|
7
|
+
onConfirm: () => void;
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
confirmLabel: string;
|
|
11
|
+
cancelLabel?: string;
|
|
12
|
+
destructive?: boolean;
|
|
13
|
+
/** Confirm shows busy, Cancel is disabled and Escape is ignored. */
|
|
14
|
+
loading?: boolean;
|
|
15
|
+
'data-testid'?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const ConfirmDialog: React.FC<ConfirmDialogProps>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Button } from "../../atomic/Button/Button.js";
|
|
2
|
+
import { AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "../../ui/alert-dialog.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
// src/components/molecule/ConfirmDialog/ConfirmDialog.tsx
|
|
5
|
+
const ConfirmDialog = (props) => {
|
|
6
|
+
const { open, onClose, onConfirm, title, description, confirmLabel, cancelLabel = "Cancel", destructive = false, loading = false, "data-testid": testId } = props;
|
|
7
|
+
return /* @__PURE__ */ jsx(AlertDialog, {
|
|
8
|
+
open,
|
|
9
|
+
onOpenChange: (next) => {
|
|
10
|
+
if (!next && !loading) onClose();
|
|
11
|
+
},
|
|
12
|
+
children: /* @__PURE__ */ jsxs(AlertDialogContent, {
|
|
13
|
+
"data-testid": testId,
|
|
14
|
+
children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: title }), description && /* @__PURE__ */ jsx(AlertDialogDescription, { children: description })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
|
|
15
|
+
variant: "outline",
|
|
16
|
+
disabled: loading,
|
|
17
|
+
onClick: onClose,
|
|
18
|
+
children: cancelLabel
|
|
19
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
20
|
+
variant: destructive ? "destructive" : "primary",
|
|
21
|
+
loading,
|
|
22
|
+
onClick: onConfirm,
|
|
23
|
+
children: confirmLabel
|
|
24
|
+
})] })]
|
|
25
|
+
})
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
export { ConfirmDialog };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import "../../atomic/Button/index.js";
|
|
2
|
+
import { ButtonVariant } from "../../atomic/Button/buttonVariant.js";
|
|
3
|
+
import { IconComponent, OverlayAlign, Size } from "../../../lib/types.js";
|
|
4
|
+
import React from "react";
|
|
5
|
+
export interface MenuItem {
|
|
6
|
+
label: string;
|
|
7
|
+
onSelect: () => void;
|
|
8
|
+
icon?: IconComponent;
|
|
9
|
+
/** Second line under the label. */
|
|
10
|
+
description?: string;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
destructive?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface DropdownMenuProps {
|
|
15
|
+
/** Trigger text (the trigger's aria-label when iconOnly). */
|
|
16
|
+
label: string;
|
|
17
|
+
items: MenuItem[];
|
|
18
|
+
icon?: IconComponent;
|
|
19
|
+
iconOnly?: boolean;
|
|
20
|
+
/** Trigger look. */
|
|
21
|
+
variant?: ButtonVariant;
|
|
22
|
+
size?: Size;
|
|
23
|
+
align?: OverlayAlign;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
onOpenChange?: (open: boolean) => void;
|
|
26
|
+
'data-testid'?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const DropdownMenu: React.FC<DropdownMenuProps>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { buttonLook } from "../../atomic/Button/buttonLook.js";
|
|
2
|
+
import { Button } from "../../ui/button.js";
|
|
3
|
+
import { DropdownMenu as DropdownMenu$1, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../ui/dropdown-menu.js";
|
|
4
|
+
import { cn } from "../../../lib/utils.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { ChevronDownIcon } from "lucide-react";
|
|
7
|
+
// src/components/molecule/DropdownMenu/DropdownMenu.tsx
|
|
8
|
+
const DropdownMenu = (props) => {
|
|
9
|
+
const { label, items, icon: Icon, iconOnly = false, variant = "outline", size = "md", align = "end", disabled = false, onOpenChange, "data-testid": testId } = props;
|
|
10
|
+
const look = buttonLook(variant, size, iconOnly);
|
|
11
|
+
return /* @__PURE__ */ jsxs(DropdownMenu$1, {
|
|
12
|
+
onOpenChange: (open) => onOpenChange?.(open),
|
|
13
|
+
children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
|
|
14
|
+
disabled,
|
|
15
|
+
"aria-label": iconOnly ? label : void 0,
|
|
16
|
+
"data-testid": testId,
|
|
17
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
18
|
+
variant: look.variant,
|
|
19
|
+
size: look.size
|
|
20
|
+
}),
|
|
21
|
+
children: [
|
|
22
|
+
Icon && /* @__PURE__ */ jsx(Icon, {
|
|
23
|
+
"data-icon": iconOnly ? void 0 : "inline-start",
|
|
24
|
+
"aria-hidden": true
|
|
25
|
+
}),
|
|
26
|
+
!iconOnly && label,
|
|
27
|
+
!iconOnly && /* @__PURE__ */ jsx(ChevronDownIcon, {
|
|
28
|
+
"data-icon": "inline-end",
|
|
29
|
+
"aria-hidden": true
|
|
30
|
+
})
|
|
31
|
+
]
|
|
32
|
+
}), /* @__PURE__ */ jsx(DropdownMenuContent, {
|
|
33
|
+
align,
|
|
34
|
+
className: "w-auto max-w-80 min-w-48",
|
|
35
|
+
children: items.map((item) => {
|
|
36
|
+
const ItemIcon = item.icon;
|
|
37
|
+
return /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
38
|
+
onClick: item.onSelect,
|
|
39
|
+
disabled: item.disabled,
|
|
40
|
+
variant: item.destructive ? "destructive" : "default",
|
|
41
|
+
children: [ItemIcon && /* @__PURE__ */ jsx(ItemIcon, { "aria-hidden": true }), /* @__PURE__ */ jsxs("span", {
|
|
42
|
+
className: "flex flex-col",
|
|
43
|
+
children: [/* @__PURE__ */ jsx("span", { children: item.label }), item.description && /* @__PURE__ */ jsx("span", {
|
|
44
|
+
className: cn("text-xs", !item.destructive && "text-muted-foreground"),
|
|
45
|
+
children: item.description
|
|
46
|
+
})]
|
|
47
|
+
})]
|
|
48
|
+
}, item.label);
|
|
49
|
+
})
|
|
50
|
+
})]
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
export { DropdownMenu };
|
|
@@ -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>;
|