@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.
Files changed (65) hide show
  1. package/dist/components/atomic/Button/buttonLook.js +18 -0
  2. package/dist/components/atomic/Checkbox/Checkbox.d.ts +11 -0
  3. package/dist/components/atomic/Checkbox/Checkbox.js +32 -0
  4. package/dist/components/atomic/Checkbox/index.d.ts +2 -0
  5. package/dist/components/atomic/ColorInput/ColorInput.d.ts +15 -0
  6. package/dist/components/atomic/ColorInput/ColorInput.js +44 -0
  7. package/dist/components/atomic/ColorInput/colorHex.js +8 -0
  8. package/dist/components/atomic/ColorInput/index.d.ts +2 -0
  9. package/dist/components/atomic/Select/Select.d.ts +18 -0
  10. package/dist/components/atomic/Select/Select.js +47 -0
  11. package/dist/components/atomic/Select/index.d.ts +2 -0
  12. package/dist/components/atomic/Switch/Switch.d.ts +11 -0
  13. package/dist/components/atomic/Switch/Switch.js +32 -0
  14. package/dist/components/atomic/Switch/index.d.ts +2 -0
  15. package/dist/components/atomic/TextArea/TextArea.d.ts +14 -0
  16. package/dist/components/atomic/TextArea/TextArea.js +35 -0
  17. package/dist/components/atomic/TextArea/index.d.ts +2 -0
  18. package/dist/components/atomic/TextInput/TextInput.d.ts +18 -0
  19. package/dist/components/atomic/TextInput/TextInput.js +42 -0
  20. package/dist/components/atomic/TextInput/index.d.ts +2 -0
  21. package/dist/components/atomic/Tooltip/Tooltip.d.ts +14 -0
  22. package/dist/components/atomic/Tooltip/Tooltip.js +14 -0
  23. package/dist/components/atomic/Tooltip/index.d.ts +2 -0
  24. package/dist/components/layout/FormLayout/FormLayout.d.ts +9 -0
  25. package/dist/components/layout/FormLayout/FormLayout.js +15 -0
  26. package/dist/components/layout/FormLayout/index.d.ts +2 -0
  27. package/dist/components/molecule/ConfirmDialog/ConfirmDialog.d.ts +17 -0
  28. package/dist/components/molecule/ConfirmDialog/ConfirmDialog.js +28 -0
  29. package/dist/components/molecule/ConfirmDialog/index.d.ts +2 -0
  30. package/dist/components/molecule/DropdownMenu/DropdownMenu.d.ts +28 -0
  31. package/dist/components/molecule/DropdownMenu/DropdownMenu.js +53 -0
  32. package/dist/components/molecule/DropdownMenu/index.d.ts +2 -0
  33. package/dist/components/molecule/FormField/FormField.d.ts +16 -0
  34. package/dist/components/molecule/FormField/FormField.js +47 -0
  35. package/dist/components/molecule/FormField/index.d.ts +2 -0
  36. package/dist/components/molecule/Modal/Modal.d.ts +22 -0
  37. package/dist/components/molecule/Modal/Modal.js +48 -0
  38. package/dist/components/molecule/Modal/index.d.ts +2 -0
  39. package/dist/components/molecule/Popover/Popover.d.ts +24 -0
  40. package/dist/components/molecule/Popover/Popover.js +38 -0
  41. package/dist/components/molecule/Popover/index.d.ts +2 -0
  42. package/dist/components/molecule/Toaster/Toaster.d.ts +3 -0
  43. package/dist/components/molecule/Toaster/Toaster.js +19 -0
  44. package/dist/components/molecule/Toaster/index.d.ts +3 -0
  45. package/dist/components/molecule/Toaster/toast.d.ts +13 -0
  46. package/dist/components/molecule/Toaster/toast.js +18 -0
  47. package/dist/components/ui/alert-dialog.js +61 -0
  48. package/dist/components/ui/checkbox.js +18 -0
  49. package/dist/components/ui/dialog.js +78 -0
  50. package/dist/components/ui/dropdown-menu.js +42 -0
  51. package/dist/components/ui/field.js +65 -0
  52. package/dist/components/ui/input.js +14 -0
  53. package/dist/components/ui/label.js +12 -0
  54. package/dist/components/ui/popover.js +32 -0
  55. package/dist/components/ui/select.js +75 -0
  56. package/dist/components/ui/switch.js +18 -0
  57. package/dist/components/ui/textarea.js +12 -0
  58. package/dist/components/ui/tooltip.js +39 -0
  59. package/dist/index.d.ts +31 -2
  60. package/dist/index.js +16 -1
  61. package/dist/lib/types.d.ts +19 -1
  62. package/dist/theme/brandTheme.d.ts +1 -1
  63. package/dist/theme/brandTheme.js +10 -8
  64. package/package.json +2 -1
  65. 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,2 @@
1
+ import { Checkbox, CheckboxProps } from "./Checkbox.js";
2
+ export { Checkbox, type CheckboxProps };
@@ -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,2 @@
1
+ import { ColorInput, ColorInputProps } from "./ColorInput.js";
2
+ export { ColorInput, type ColorInputProps };
@@ -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,2 @@
1
+ import { Select, SelectOption, SelectProps } from "./Select.js";
2
+ export { Select, type SelectOption, type SelectProps };
@@ -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,2 @@
1
+ import { Switch, SwitchProps } from "./Switch.js";
2
+ export { Switch, type SwitchProps };
@@ -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,2 @@
1
+ import { TextArea, TextAreaProps } from "./TextArea.js";
2
+ export { TextArea, type TextAreaProps };
@@ -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,2 @@
1
+ import { TextInput, TextInputProps, TextInputType } from "./TextInput.js";
2
+ export { TextInput, type TextInputProps, type TextInputType };
@@ -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,2 @@
1
+ import { Tooltip, TooltipProps } from "./Tooltip.js";
2
+ export { Tooltip, type TooltipProps };
@@ -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,2 @@
1
+ import { FormLayout, FormLayoutDirection, FormLayoutProps } from "./FormLayout.js";
2
+ export { FormLayout, type FormLayoutDirection, type FormLayoutProps };
@@ -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,2 @@
1
+ import { ConfirmDialog, ConfirmDialogProps } from "./ConfirmDialog.js";
2
+ export { ConfirmDialog, type ConfirmDialogProps };
@@ -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,2 @@
1
+ import { DropdownMenu, DropdownMenuProps, MenuItem } from "./DropdownMenu.js";
2
+ export { DropdownMenu, type DropdownMenuProps, type MenuItem };
@@ -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>;