atlas-react-kit 0.13.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/COMPONENTS.md +144 -120
  2. package/DESIGN_RULES.md +53 -32
  3. package/README.md +12 -53
  4. package/dist/accordion.d.ts +1 -0
  5. package/dist/avatar.d.ts +1 -0
  6. package/dist/badge.d.ts +2 -3
  7. package/dist/breadcrumb.d.ts +2 -3
  8. package/dist/button.d.ts +2 -3
  9. package/dist/calendar.d.ts +2 -0
  10. package/dist/card.d.ts +3 -0
  11. package/dist/chart.d.ts +152 -0
  12. package/dist/chart.js +177 -0
  13. package/dist/color-legend.d.ts +8 -0
  14. package/dist/combobox.d.ts +13 -4
  15. package/dist/combobox.js +1 -8
  16. package/dist/date-picker.d.ts +26 -3
  17. package/dist/date-picker.js +37 -31
  18. package/dist/date-time-picker.d.ts +5 -2
  19. package/dist/date-time-picker.js +25 -38
  20. package/dist/dialog.d.ts +1 -0
  21. package/dist/dialog.js +4 -2
  22. package/dist/dropdown-menu.d.ts +9 -8
  23. package/dist/empty-panel.d.ts +8 -2
  24. package/dist/empty-panel.js +2 -2
  25. package/dist/error-boundary.d.ts +6 -1
  26. package/dist/field.d.ts +3 -0
  27. package/dist/field.js +1 -1
  28. package/dist/form/checkbox-field.d.ts +45 -0
  29. package/dist/form/checkbox-field.js +39 -0
  30. package/dist/form/combobox-field.d.ts +13 -0
  31. package/dist/form/combobox-field.js +14 -0
  32. package/dist/form/control-field.d.ts +6 -7
  33. package/dist/form/date-picker-field.d.ts +12 -21
  34. package/dist/form/date-picker-field.js +5 -4
  35. package/dist/form/date-time-picker-field.d.ts +7 -22
  36. package/dist/form/date-time-picker-field.js +4 -3
  37. package/dist/form/feedback-field.d.ts +2 -0
  38. package/dist/form/field-error.d.ts +35 -3
  39. package/dist/form/field-shell.d.ts +35 -6
  40. package/dist/form/field-shell.js +1 -1
  41. package/dist/form/focus-first-invalid.d.ts +28 -0
  42. package/dist/form/focus-first-invalid.js +29 -0
  43. package/dist/form/markdown-field.d.ts +4 -16
  44. package/dist/form/select-field.d.ts +13 -7
  45. package/dist/form/select-field.js +3 -2
  46. package/dist/form/text-field.d.ts +29 -10
  47. package/dist/form/text-field.js +4 -0
  48. package/dist/form/toggle-group-field.d.ts +30 -0
  49. package/dist/form/toggle-group-field.js +19 -0
  50. package/dist/info-tip.d.ts +10 -1
  51. package/dist/internal/as-child.d.ts +8 -0
  52. package/dist/internal/as-child.js +1 -0
  53. package/dist/internal/form-control.d.ts +3 -2
  54. package/dist/internal/form-control.js +3 -2
  55. package/dist/internal/kanban-layout.d.ts +55 -0
  56. package/dist/internal/kanban-layout.js +80 -0
  57. package/dist/internal/kanban-plugins.d.ts +17 -0
  58. package/dist/internal/kanban-plugins.js +17 -0
  59. package/dist/internal/listbox-navigation.d.ts +0 -11
  60. package/dist/internal/listbox-navigation.js +2 -2
  61. package/dist/internal/listbox-option.d.ts +0 -15
  62. package/dist/internal/listbox-option.js +1 -1
  63. package/dist/internal/use-drag-scroll.d.ts +17 -0
  64. package/dist/internal/use-drag-scroll.js +98 -0
  65. package/dist/internal/use-media-query.d.ts +4 -2
  66. package/dist/internal/use-media-query.js +11 -15
  67. package/dist/kanban.d.ts +84 -0
  68. package/dist/kanban.js +197 -0
  69. package/dist/markdown.d.ts +2 -0
  70. package/dist/meter.d.ts +7 -0
  71. package/dist/nav-link.d.ts +15 -6
  72. package/dist/nav-link.js +14 -4
  73. package/dist/next/nav-link.d.ts +6 -3
  74. package/dist/next/nav-link.js +8 -6
  75. package/dist/next/period-stepper.d.ts +11 -0
  76. package/dist/next/period-stepper.js +16 -0
  77. package/dist/next/tabs-nav-link.d.ts +6 -0
  78. package/dist/next/tabs-nav-link.js +8 -3
  79. package/dist/page-header.d.ts +19 -0
  80. package/dist/page-header.js +11 -0
  81. package/dist/pagination.d.ts +2 -0
  82. package/dist/period-stepper.d.ts +32 -0
  83. package/dist/period-stepper.js +27 -0
  84. package/dist/picker-shell.d.ts +33 -18
  85. package/dist/picker-shell.js +12 -10
  86. package/dist/progress.d.ts +1 -0
  87. package/dist/search-picker.d.ts +12 -0
  88. package/dist/select.d.ts +1 -0
  89. package/dist/skip-link.d.ts +7 -0
  90. package/dist/skip-link.js +10 -0
  91. package/dist/slider.d.ts +11 -0
  92. package/dist/sortable-table-head.d.ts +2 -0
  93. package/dist/stat-tile.d.ts +5 -0
  94. package/dist/table-body-skeleton.d.ts +2 -0
  95. package/dist/table.d.ts +1 -0
  96. package/dist/tabs.d.ts +27 -12
  97. package/dist/tabs.js +23 -10
  98. package/dist/toast.d.ts +7 -1
  99. package/dist/toggle-group.d.ts +9 -2
  100. package/dist/toggle-group.js +2 -1
  101. package/package.json +72 -6
  102. package/skills/atlas-react-kit/SKILL.md +46 -40
  103. package/theme.css +13 -0
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from "react";
4
+ import { Checkbox } from "../checkbox.js";
5
+ import { Field, FieldContent, FieldDescription, FieldLabel, FieldLegend, FieldSet, } from "../field.js";
6
+ import { cn } from "../utils.js";
7
+ import { FieldError, } from "./field-error.js";
8
+ import { describedBy, descriptionIdFor, errorIdFor, invalidWhen, metaErrorText, } from "./field-shell.js";
9
+ /**
10
+ * One boolean as a labelled checkbox, with an optional hint and the same
11
+ * error text and timing as the other `form/*` fields.
12
+ */
13
+ export function FormCheckboxField({ checked, onCheckedChange, onBlur, label, description, id: idProp, meta, submitted, showError, required, disabled, controlPosition = "start", className, fieldClassName, }) {
14
+ const generatedId = useId();
15
+ const id = idProp ?? generatedId;
16
+ const errorText = metaErrorText(meta, { submitted, showError });
17
+ const descriptionId = description ? descriptionIdFor(id) : undefined;
18
+ const checkbox = (_jsx(Checkbox, { id: id, checked: checked, onCheckedChange: (next) => onCheckedChange(next === true), onBlur: onBlur, disabled: disabled, required: required, "aria-required": required || undefined, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText, descriptionId), className: className }));
19
+ const labelNode = (_jsx(FieldLabel, { htmlFor: id, required: required, children: label }));
20
+ return (_jsxs("div", { "data-slot": "form-field", className: cn("flex flex-col gap-1.5", fieldClassName), children: [_jsxs(Field, { orientation: "horizontal", children: [controlPosition === "start" ? checkbox : null, description ? (_jsxs(FieldContent, { children: [labelNode, _jsx(FieldDescription, { id: descriptionId, children: description })] })) : (labelNode), controlPosition === "end" ? checkbox : null] }), _jsx(FieldError, { id: errorIdFor(id), text: errorText })] }));
21
+ }
22
+ /** Several values from a short list, as a labelled set of checkboxes. */
23
+ export function FormCheckboxGroupField({ label, description, options, value, onValueChange, meta, submitted, showError, disabled, className, fieldClassName, }) {
24
+ const id = useId();
25
+ const errorText = metaErrorText(meta, { submitted, showError });
26
+ const descriptionId = description ? descriptionIdFor(id) : undefined;
27
+ function toggle(option, on) {
28
+ const next = new Set(value);
29
+ if (on)
30
+ next.add(option);
31
+ else
32
+ next.delete(option);
33
+ onValueChange(options.map((o) => o.value).filter((v) => next.has(v)));
34
+ }
35
+ return (_jsxs(FieldSet, { "data-slot": "form-field", "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText, descriptionId), className: cn("gap-1.5", fieldClassName), children: [_jsx(FieldLegend, { variant: "label", className: "mb-0", children: label }), description ? (_jsx("p", { id: descriptionId, "data-slot": "form-field-description", className: "text-xs text-muted-foreground", children: description })) : null, _jsx("div", { "data-slot": "form-checkbox-group-options", className: cn("flex flex-wrap gap-x-6 gap-y-3 pt-1", className), children: options.map((option) => {
36
+ const optionId = `${id}-${option.value}`;
37
+ return (_jsxs(Field, { orientation: "horizontal", children: [_jsx(Checkbox, { id: optionId, checked: value.includes(option.value), onCheckedChange: (next) => toggle(option.value, next === true), disabled: disabled }), _jsx(FieldLabel, { htmlFor: optionId, children: option.label })] }, option.value));
38
+ }) }), _jsx(FieldError, { id: errorIdFor(id), text: errorText })] }));
39
+ }
@@ -0,0 +1,13 @@
1
+ import { type ComponentProps } from "react";
2
+ import { Combobox } from "../combobox.js";
3
+ import type { FormFieldBaseProps, FormFieldDescriptionProps, FormFieldMetaProps } from "./field-error.js";
4
+ type Props = FormFieldBaseProps & FormFieldMetaProps & FormFieldDescriptionProps & Omit<ComponentProps<typeof Combobox>, "id" | "aria-invalid" | "aria-describedby" | "aria-required"> & {
5
+ /** Marks the label and passes `aria-required` to the trigger. */
6
+ required?: boolean;
7
+ };
8
+ /**
9
+ * `Combobox` with a label, optional hint, and the `form/*` error text and
10
+ * timing. Pass the field's blur handler as `onBlur` so touch state updates.
11
+ */
12
+ export declare function FormComboboxField({ label, description, meta, submitted, showError, required, fieldClassName, ...props }: Props): import("react").JSX.Element;
13
+ export {};
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useId } from "react";
4
+ import { Combobox } from "../combobox.js";
5
+ import { describedBy, descriptionIdFor, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
6
+ /**
7
+ * `Combobox` with a label, optional hint, and the `form/*` error text and
8
+ * timing. Pass the field's blur handler as `onBlur` so touch state updates.
9
+ */
10
+ export function FormComboboxField({ label, description, meta, submitted, showError, required, fieldClassName, ...props }) {
11
+ const id = useId();
12
+ const errorText = metaErrorText(meta, { submitted, showError });
13
+ return (_jsx(FormFieldShell, { id: id, label: label, description: description, errorText: errorText, required: required, className: fieldClassName, children: _jsx(Combobox, { ...props, id: id, "aria-required": required || undefined, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText, description ? descriptionIdFor(id) : undefined) }) }));
14
+ }
@@ -1,22 +1,21 @@
1
1
  import { type ComponentProps, type ReactNode } from "react";
2
- import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
2
+ import type { FormFieldBaseProps, FormFieldDescriptionProps, FormFieldMetaProps } from "./field-error.js";
3
3
  /** Props `FormControlField` hands its control: spread them onto it. */
4
4
  export type FormControlProps = {
5
+ /** Id the label points at. */
5
6
  id: string;
7
+ /** `true` while the field shows error text. */
6
8
  "aria-invalid": true | undefined;
9
+ /** Ids of the hint and the error text, when shown. */
7
10
  "aria-describedby": string | undefined;
11
+ /** `true` when the field is `required`. */
8
12
  "aria-required": true | undefined;
9
13
  };
10
- type Props = ErrorTimingProps & Omit<ComponentProps<"div">, "id" | "children"> & {
14
+ type Props = Omit<FormFieldBaseProps, "fieldClassName"> & FormFieldMetaProps & FormFieldDescriptionProps & Omit<ComponentProps<"div">, "id" | "children"> & {
11
15
  /** Control id the label points at. Defaults to a generated id. */
12
16
  id?: string;
13
- label: string;
14
- /** Hint between the label and the control, read with the control. */
15
- description?: ReactNode;
16
17
  /** Marks the label and passes `aria-required` to the control. */
17
18
  required?: boolean;
18
- /** Error meta, the same shape the other `form/*` fields take. */
19
- meta?: FieldMetaState;
20
19
  /** Renders the control from the id and ARIA props it should carry. */
21
20
  children: (control: FormControlProps) => ReactNode;
22
21
  };
@@ -1,27 +1,18 @@
1
- import { type DateLike, type DatePickerCalendarProps } from "../date-picker.js";
2
- import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
3
- type Props = ErrorTimingProps & {
1
+ import { type DateLike } from "../date-picker.js";
2
+ import { type PickerFieldBaseProps } from "./field-shell.js";
3
+ type Props = PickerFieldBaseProps & {
4
+ /** The picked day, as a `Date` or `yyyy-MM-dd`; empty when unset. */
4
5
  value: DateLike;
6
+ /** Called with the picked day, or `undefined` when cleared. */
5
7
  onValueChange: (date: Date | undefined) => void;
6
- label: string;
7
- meta?: FieldMetaState;
8
+ /** Trigger text while no day is picked. Default "Pick a date". */
8
9
  placeholder?: string;
9
- disabled?: boolean;
10
- /** Marks the label and adds "Required" to the trigger's description. */
11
- required?: boolean;
12
- /** Earliest day that can be picked; earlier days are disabled. */
13
- minDate?: DateLike;
14
- /** Latest day that can be picked; later days are disabled. */
15
- maxDate?: DateLike;
16
- /** Forwarded to the date picker trigger. */
17
- className?: string;
18
- /** Classes for the field wrapper (label, trigger, and error). */
19
- fieldClassName?: string;
20
- /** Forwarded to the date picker popover. */
21
- contentClassName?: string;
22
- /** Day Picker options forwarded to the calendar (disabled days, etc.). */
23
- calendarProps?: DatePickerCalendarProps;
10
+ /**
11
+ * Shows a Clear row in the popover, which calls `onValueChange` with
12
+ * `undefined`. Default `false`.
13
+ */
14
+ clearable?: boolean;
24
15
  };
25
16
  /** Date picker with built-in field error text and invalid styling. */
26
- export declare function FormDatePickerField({ onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
17
+ export declare function FormDatePickerField({ id: idProp, onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
27
18
  export {};
@@ -1,11 +1,12 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useId } from "react";
4
- import { DatePickerButton, } from "../date-picker.js";
4
+ import { DatePickerButton } from "../date-picker.js";
5
5
  import { FormFieldShell, metaErrorText, pickerFieldAria, } from "./field-shell.js";
6
6
  /** Date picker with built-in field error text and invalid styling. */
7
- export function FormDatePickerField({ onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }) {
7
+ export function FormDatePickerField({ id: idProp, onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }) {
8
8
  const errorText = metaErrorText(meta, { submitted, showError });
9
- const id = useId();
10
- return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsx(DatePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText, required) }) }));
9
+ const generatedId = useId();
10
+ const id = idProp ?? generatedId;
11
+ return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsx(DatePickerButton, { ...picker, id: id, onValueChange: onValueChange, ...pickerFieldAria(id, errorText, required) }) }));
11
12
  }
@@ -1,29 +1,14 @@
1
- import type { DateLike, DatePickerCalendarProps } from "../date-picker.js";
2
- import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
3
- type Props = ErrorTimingProps & {
1
+ import { type PickerFieldBaseProps } from "./field-shell.js";
2
+ type Props = PickerFieldBaseProps & {
3
+ /** The picked instant, or `null` when empty. */
4
4
  value: Date | null;
5
+ /** Called with the picked instant, or `null` on Clear. */
5
6
  onValueChange: (date: Date | null) => void;
6
- label: string;
7
- meta?: FieldMetaState;
7
+ /** Trigger text while nothing is picked. Default "Pick a date and time". */
8
8
  placeholder?: string;
9
- disabled?: boolean;
10
- /** Marks the label and adds "Required" to the trigger's description. */
11
- required?: boolean;
12
- /** Earliest day that can be picked; earlier days are disabled. */
13
- minDate?: DateLike;
14
- /** Latest day that can be picked; later days are disabled. */
15
- maxDate?: DateLike;
16
- /** Minute increment forwarded to the picker. */
9
+ /** Minute increment for the time selects (1, 5, 15, 30, or 60). Default 15. */
17
10
  timeStep?: number;
18
- /** Forwarded to the date-time picker trigger. */
19
- className?: string;
20
- /** Classes for the field wrapper (label, trigger, and error). */
21
- fieldClassName?: string;
22
- /** Forwarded to the date-time picker popover. */
23
- contentClassName?: string;
24
- /** Day Picker options forwarded to the calendar (disabled days, etc.). */
25
- calendarProps?: DatePickerCalendarProps;
26
11
  };
27
12
  /** Date-time picker with built-in field error text and invalid styling. */
28
- export declare function FormDateTimePickerField({ onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
13
+ export declare function FormDateTimePickerField({ id: idProp, onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }: Props): import("react").JSX.Element;
29
14
  export {};
@@ -4,8 +4,9 @@ import { useId } from "react";
4
4
  import { DateTimePickerButton } from "../date-time-picker.js";
5
5
  import { FormFieldShell, metaErrorText, pickerFieldAria, } from "./field-shell.js";
6
6
  /** Date-time picker with built-in field error text and invalid styling. */
7
- export function FormDateTimePickerField({ onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }) {
7
+ export function FormDateTimePickerField({ id: idProp, onValueChange, label, meta, submitted, showError, required, fieldClassName, ...picker }) {
8
8
  const errorText = metaErrorText(meta, { submitted, showError });
9
- const id = useId();
10
- return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsx(DateTimePickerButton, { ...picker, id: id, onChange: onValueChange, ...pickerFieldAria(id, errorText, required) }) }));
9
+ const generatedId = useId();
10
+ const id = idProp ?? generatedId;
11
+ return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsx(DateTimePickerButton, { ...picker, id: id, onValueChange: onValueChange, ...pickerFieldAria(id, errorText, required) }) }));
11
12
  }
@@ -1,5 +1,7 @@
1
1
  /** Form-level submit or mutation error (not tied to a single field). */
2
2
  export declare function FormFeedbackField({ message, className, }: {
3
+ /** Error text to show; `null` or empty renders nothing. */
3
4
  message: string | null;
5
+ /** Classes for the message paragraph. */
4
6
  className?: string;
5
7
  }): import("react").JSX.Element | null;
@@ -1,7 +1,9 @@
1
+ import type { ReactNode } from "react";
1
2
  export type FieldErrorMessage = string | {
2
3
  message?: string;
3
4
  } | undefined;
4
5
  export type FieldMetaState = {
6
+ /** Validation errors for the field; the `form/*` fields show the first. */
5
7
  errors: FieldErrorMessage[];
6
8
  /**
7
9
  * Whether the person has changed or left the field (TanStack Form's
@@ -14,20 +16,50 @@ export type FieldMetaState = {
14
16
  /** When a field shows its error: the `form/*` fields' shared timing props. */
15
17
  export type ErrorTimingProps = {
16
18
  /**
17
- * Set once the form has been submitted (TanStack Form:
18
- * `form.state.submissionAttempts > 0`), so an untouched field shows its
19
- * error too. Only matters when `meta.isTouched` is passed.
19
+ * Set once the form has been submitted, so an untouched field shows its
20
+ * error too. For form libraries that do not mark fields touched on
21
+ * submit; TanStack Form does, so its users pass `meta` only. Only matters
22
+ * when `meta.isTouched` is passed.
20
23
  */
21
24
  submitted?: boolean;
22
25
  /** When false, never shows error text or invalid styling. Default true. */
23
26
  showError?: boolean;
24
27
  };
28
+ /**
29
+ * Props every labelled `form/*` field shares: the error timing, the label,
30
+ * and the classes for the field wrapper.
31
+ */
32
+ export type FormFieldBaseProps = ErrorTimingProps & {
33
+ /** Visible label text (the legend, for a checkbox group). */
34
+ label: string;
35
+ /**
36
+ * Classes for the field wrapper (`data-slot="form-field"`: label, hint,
37
+ * control, and error), e.g. a width.
38
+ */
39
+ fieldClassName?: string;
40
+ };
41
+ /** Error meta for the `form/*` fields that are not bound to a `field` API. */
42
+ export type FormFieldMetaProps = {
43
+ /**
44
+ * The field's meta (TanStack Form's `field.state.meta`); its first error
45
+ * shows under the control. Leave it out for no error text.
46
+ */
47
+ meta?: FieldMetaState;
48
+ };
49
+ /** The optional hint the `form/*` fields render under the label. */
50
+ export type FormFieldDescriptionProps = {
51
+ /** Hint between the label and the control, read with the control. */
52
+ description?: ReactNode;
53
+ };
25
54
  /** First TanStack Form error message for a field, if any. */
26
55
  export declare function getFieldErrorMessage(message: FieldErrorMessage): string | undefined;
27
56
  export declare function FieldError({ id, message, text, className, }: {
28
57
  /** Point the control's `aria-describedby` here so the error is read with it. */
29
58
  id?: string;
59
+ /** Error to show, as TanStack Form reports it; ignored when `text` is set. */
30
60
  message?: FieldErrorMessage;
61
+ /** Error text to show as is; takes precedence over `message`. */
31
62
  text?: string;
63
+ /** Classes for the error paragraph. */
32
64
  className?: string;
33
65
  }): import("react").JSX.Element | null;
@@ -1,5 +1,6 @@
1
1
  import type { ComponentProps, ReactNode } from "react";
2
- import { type ErrorTimingProps, type FieldMetaState } from "./field-error.js";
2
+ import type { DateLike, DatePickerCalendarProps } from "../date-picker.js";
3
+ import { type ErrorTimingProps, type FieldMetaState, type FormFieldBaseProps, type FormFieldMetaProps } from "./field-error.js";
3
4
  /**
4
5
  * Not a public entry. The layout and error wiring every `form/*` field
5
6
  * shares, so a field only says which control it renders. Custom controls
@@ -19,16 +20,37 @@ export declare function labelIdFor(id: string): string;
19
20
  export declare function descriptionIdFor(id: string): string;
20
21
  /** Id of the error text `FormFieldShell` renders for control `id`. */
21
22
  export declare function errorIdFor(id: string): string;
22
- /**
23
- * Id of the hidden "Required" text `FormFieldShell` renders for a required
24
- * control `id`, for controls that cannot carry `required` themselves.
25
- */
26
- export declare function requiredIdFor(id: string): string;
27
23
  /**
28
24
  * `aria-describedby` for the control: the given hint ids, plus the error
29
25
  * text while there is one, or `undefined` when there is nothing to read.
30
26
  */
31
27
  export declare function describedBy(id: string, errorText: string | undefined, ...hintIds: Array<string | undefined>): string | undefined;
28
+ /**
29
+ * Props the date and date-time fields share; each adds its own `value`,
30
+ * `onValueChange`, and `placeholder`.
31
+ */
32
+ export type PickerFieldBaseProps = FormFieldBaseProps & FormFieldMetaProps & {
33
+ /**
34
+ * Id of the trigger the label points at. Defaults to a generated id;
35
+ * pass the field name (`id={field.name}`) so a submit handler such as
36
+ * `focusFirstInvalid` can find the control.
37
+ */
38
+ id?: string;
39
+ /** Disables the trigger so no date can be picked. */
40
+ disabled?: boolean;
41
+ /** Marks the label and adds "Required" to the trigger's description. */
42
+ required?: boolean;
43
+ /** Earliest day that can be picked; earlier days are disabled. */
44
+ minDate?: DateLike;
45
+ /** Latest day that can be picked; later days are disabled. */
46
+ maxDate?: DateLike;
47
+ /** Forwarded to the picker trigger. */
48
+ className?: string;
49
+ /** Forwarded to the picker popover. */
50
+ contentClassName?: string;
51
+ /** Day Picker options forwarded to the calendar (disabled days, etc.). */
52
+ calendarProps?: DatePickerCalendarProps;
53
+ };
32
54
  /**
33
55
  * Trigger ARIA for the date and date-time fields: invalid while there is
34
56
  * error text, named "label, value" (a `<label for>` alone would hide the
@@ -53,12 +75,19 @@ export declare function pickerFieldAria(id: string, errorText: string | undefine
53
75
  * so put fields in a grid row with `items-start` to keep controls aligned.
54
76
  */
55
77
  export declare function FormFieldShell({ id, label, labelAction, description, errorText, labelFor, required, className, children, ...props }: Omit<ComponentProps<"div">, "id" | "children"> & {
78
+ /** Id of the control; the label, hint, and error ids derive from it. */
56
79
  id: string;
80
+ /** Visible label text. */
57
81
  label: string;
82
+ /** Content on the label's row, after the label (e.g. a mode toggle). */
58
83
  labelAction?: ReactNode;
84
+ /** Hint between the label and the control, with id `descriptionIdFor(id)`. */
59
85
  description?: ReactNode;
86
+ /** Error text under the control; `undefined` shows none. */
60
87
  errorText: string | undefined;
88
+ /** When false, the label does not point at the control. Default true. */
61
89
  labelFor?: boolean;
90
+ /** Marks the label and renders the hidden "Required" text. */
62
91
  required?: boolean;
63
92
  children: ReactNode;
64
93
  }): import("react").JSX.Element;
@@ -40,7 +40,7 @@ export function errorIdFor(id) {
40
40
  * Id of the hidden "Required" text `FormFieldShell` renders for a required
41
41
  * control `id`, for controls that cannot carry `required` themselves.
42
42
  */
43
- export function requiredIdFor(id) {
43
+ function requiredIdFor(id) {
44
44
  return `${id}-required`;
45
45
  }
46
46
  /**
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The part of a form library's invalid-submit event this helper reads: the
3
+ * form state's per-field meta, keyed by field name (TanStack Form's
4
+ * `onSubmitInvalid` passes `{ formApi }` with this shape).
5
+ */
6
+ export type InvalidSubmitEvent = {
7
+ formApi: {
8
+ state: {
9
+ fieldMeta: Partial<Record<string, {
10
+ errors: readonly unknown[];
11
+ }>>;
12
+ };
13
+ };
14
+ };
15
+ /**
16
+ * Invalid-submit handler that focuses the first field, in page order, whose
17
+ * meta has errors. It reads the form state, not the rendered `aria-invalid`,
18
+ * so it works before React has re-rendered. Fields are matched by control
19
+ * id, so give each `form/*` field `id={field.name}` (`FormTextField`
20
+ * defaults to it). The form library needs no focus API of its own.
21
+ *
22
+ * ```tsx
23
+ * const form = useForm({ onSubmitInvalid: focusFirstInvalid("order-form") })
24
+ * <form id="order-form" onSubmit={...}>
25
+ * <FormSelectField id={field.name} ... />
26
+ * ```
27
+ */
28
+ export declare function focusFirstInvalid(formId: string): ({ formApi }: InvalidSubmitEvent) => void;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Invalid-submit handler that focuses the first field, in page order, whose
3
+ * meta has errors. It reads the form state, not the rendered `aria-invalid`,
4
+ * so it works before React has re-rendered. Fields are matched by control
5
+ * id, so give each `form/*` field `id={field.name}` (`FormTextField`
6
+ * defaults to it). The form library needs no focus API of its own.
7
+ *
8
+ * ```tsx
9
+ * const form = useForm({ onSubmitInvalid: focusFirstInvalid("order-form") })
10
+ * <form id="order-form" onSubmit={...}>
11
+ * <FormSelectField id={field.name} ... />
12
+ * ```
13
+ */
14
+ export function focusFirstInvalid(formId) {
15
+ return ({ formApi }) => {
16
+ const form = document.getElementById(formId);
17
+ if (!(form instanceof HTMLFormElement))
18
+ return;
19
+ const invalid = new Set(Object.entries(formApi.state.fieldMeta)
20
+ .filter(([, meta]) => meta?.errors.length)
21
+ .map(([name]) => name));
22
+ for (const element of form.elements) {
23
+ if (element instanceof HTMLElement && invalid.has(element.id)) {
24
+ element.focus();
25
+ return;
26
+ }
27
+ }
28
+ };
29
+ }
@@ -1,25 +1,13 @@
1
- import type { ErrorTimingProps } from "./field-error.js";
2
- import type { TextFieldApi } from "./text-field.js";
1
+ import type { TextFieldBaseProps } from "./text-field.js";
3
2
  /**
4
3
  * GFM body field: Write is a textarea, Preview is the sanitized Markdown
5
4
  * renderer. String-in/string-out — not a WYSIWYG document model.
6
5
  */
7
- export declare function FormMarkdownField({ field, id, label, placeholder, rows, showError, submitted, required, className, fieldClassName, previewClassName, }: ErrorTimingProps & {
8
- field: TextFieldApi;
9
- /**
10
- * Control id the label points at. Defaults to `field.name`; pass a unique
11
- * id when two forms on one page share a field name.
12
- */
13
- id?: string;
14
- label: string;
6
+ export declare function FormMarkdownField({ field, id, label, placeholder, rows, showError, submitted, required, className, fieldClassName, previewClassName, }: TextFieldBaseProps & {
7
+ /** Write textarea placeholder. Default "Markdown supported". */
15
8
  placeholder?: string;
9
+ /** Write textarea rows. Default 5. */
16
10
  rows?: number;
17
- /** Sets `required` on the Write textarea and marks the label. */
18
- required?: boolean;
19
- /** Forwarded to the Write textarea. */
20
- className?: string;
21
- /** Classes for the field wrapper (label row, body, and error). */
22
- fieldClassName?: string;
23
11
  /** Classes for the Preview surface, e.g. a taller `min-h-40`. */
24
12
  previewClassName?: string;
25
13
  }): import("react").JSX.Element;
@@ -1,21 +1,27 @@
1
1
  import { type ReactNode } from "react";
2
- import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
3
- type Props = ErrorTimingProps & {
2
+ import type { FormFieldBaseProps, FormFieldMetaProps } from "./field-error.js";
3
+ type Props = FormFieldBaseProps & FormFieldMetaProps & {
4
+ /** The selected item's value, or `undefined` for none. */
4
5
  value: string | undefined;
5
- meta?: FieldMetaState;
6
- label: string;
6
+ /** Called with the value of the item the person picks. */
7
7
  onValueChange: (value: string) => void;
8
+ /** Trigger text while no item is selected. */
8
9
  placeholder?: string;
10
+ /**
11
+ * Id of the trigger the label points at. Defaults to a generated id;
12
+ * pass the field name (`id={field.name}`) so a submit handler such as
13
+ * `focusFirstInvalid` can find the control.
14
+ */
15
+ id?: string;
9
16
  /** Requires a value (native form validation) and marks the label. */
10
17
  required?: boolean;
11
18
  /** Forwarded to the select trigger. */
12
19
  className?: string;
13
- /** Classes for the field wrapper (label, trigger, and error). */
14
- fieldClassName?: string;
15
20
  /** Forwarded to the select's option list (`SelectContent`). */
16
21
  contentClassName?: string;
22
+ /** The `SelectItem`s (and groups) to choose from. */
17
23
  children: ReactNode;
18
24
  };
19
25
  /** Select control with built-in field error text and invalid styling. */
20
- export declare function FormSelectField({ value, meta, submitted, showError, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }: Props): import("react").JSX.Element;
26
+ export declare function FormSelectField({ id: idProp, value, meta, submitted, showError, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }: Props): import("react").JSX.Element;
21
27
  export {};
@@ -4,8 +4,9 @@ import { useId } from "react";
4
4
  import { Select, SelectContent, SelectTrigger, SelectValue } from "../select.js";
5
5
  import { describedBy, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
6
6
  /** Select control with built-in field error text and invalid styling. */
7
- export function FormSelectField({ value, meta, submitted, showError, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }) {
7
+ export function FormSelectField({ id: idProp, value, meta, submitted, showError, label, onValueChange, placeholder, required, className, fieldClassName, contentClassName, children, }) {
8
8
  const errorText = metaErrorText(meta, { submitted, showError });
9
- const id = useId();
9
+ const generatedId = useId();
10
+ const id = idProp ?? generatedId;
10
11
  return (_jsx(FormFieldShell, { id: id, label: label, errorText: errorText, required: required, className: fieldClassName, children: _jsxs(Select, { value: value, onValueChange: onValueChange, required: required, children: [_jsx(SelectTrigger, { id: id, className: className, "aria-required": required || undefined, "aria-invalid": invalidWhen(errorText), "aria-describedby": describedBy(id, errorText), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { className: contentClassName, children: children })] }) }));
11
12
  }
@@ -1,14 +1,24 @@
1
- import type { ErrorTimingProps, FieldMetaState } from "./field-error.js";
1
+ import type { FieldMetaState, FormFieldBaseProps } from "./field-error.js";
2
2
  export type TextFieldApi = {
3
+ /** Field name; the default control id. */
3
4
  name: string;
4
5
  state: {
6
+ /** Current text. */
5
7
  value: string;
8
+ /** Error meta; its first error shows under the control. */
6
9
  meta: FieldMetaState;
7
10
  };
11
+ /** Called with the new text on every change. */
8
12
  handleChange: (value: string) => void;
13
+ /** Called when the control loses focus, to mark the field touched. */
9
14
  handleBlur: () => void;
10
15
  };
11
- export declare function FormTextField({ field, id, label, placeholder, description, autoFocus, multiline, rows, inputType, showError, submitted, autoComplete, required, minLength, className, fieldClassName, }: ErrorTimingProps & {
16
+ /**
17
+ * Props the text and Markdown fields share: both bind to a TanStack Form
18
+ * `field` and render a text control.
19
+ */
20
+ export type TextFieldBaseProps = FormFieldBaseProps & {
21
+ /** The TanStack Form field (or the same shape) the control reads and writes. */
12
22
  field: TextFieldApi;
13
23
  /**
14
24
  * Control id the label points at. Defaults to `field.name`; pass a unique
@@ -16,21 +26,30 @@ export declare function FormTextField({ field, id, label, placeholder, descripti
16
26
  * `email`), or the second label focuses the first input.
17
27
  */
18
28
  id?: string;
19
- label: string;
29
+ /** Sets `required` on the text control and marks the label. */
30
+ required?: boolean;
31
+ /** Forwarded to the text control (the input or textarea). */
32
+ className?: string;
33
+ };
34
+ /**
35
+ * Text input, or a textarea with `multiline`, bound to a TanStack Form
36
+ * `field`, with a label, optional hint, and the `form/*` error text.
37
+ */
38
+ export declare function FormTextField({ field, id, label, placeholder, description, autoFocus, multiline, rows, inputType, showError, submitted, autoComplete, required, minLength, className, fieldClassName, }: TextFieldBaseProps & {
39
+ /** Text shown in the empty control. */
20
40
  placeholder?: string;
21
41
  /** Optional hint under the label (employer vs referrer, etc.). */
22
42
  description?: string;
43
+ /** Focuses the input on mount (ignored with `multiline`). */
23
44
  autoFocus?: boolean;
45
+ /** Renders a textarea instead of an input. */
24
46
  multiline?: boolean;
25
- /** Textarea rows when `multiline` is set. */
47
+ /** Textarea rows when `multiline` is set. Default 3. */
26
48
  rows?: number;
49
+ /** The input's `type` when not `multiline`. Default "text". */
27
50
  inputType?: "text" | "email" | "password" | "date" | "datetime-local" | "time" | "number";
51
+ /** Forwarded to the control's `autoComplete`. */
28
52
  autoComplete?: string;
29
- /** Sets `required` on the control and marks the label. */
30
- required?: boolean;
53
+ /** Minimum text length (native form validation). */
31
54
  minLength?: number;
32
- /** Forwarded to the input or textarea. */
33
- className?: string;
34
- /** Classes for the field wrapper (label, hint, control, and error). */
35
- fieldClassName?: string;
36
55
  }): import("react").JSX.Element;
@@ -4,6 +4,10 @@ import { Input } from "../input.js";
4
4
  import { Textarea } from "../textarea.js";
5
5
  import { onInputChange } from "../utils.js";
6
6
  import { describedBy, descriptionIdFor, FormFieldShell, invalidWhen, metaErrorText, } from "./field-shell.js";
7
+ /**
8
+ * Text input, or a textarea with `multiline`, bound to a TanStack Form
9
+ * `field`, with a label, optional hint, and the `form/*` error text.
10
+ */
7
11
  export function FormTextField({ field, id = field.name, label, placeholder, description, autoFocus, multiline, rows = 3, inputType = "text", showError, submitted, autoComplete, required, minLength, className, fieldClassName, }) {
8
12
  const onChange = onInputChange(field.handleChange);
9
13
  const errorText = metaErrorText(field.state.meta, { submitted, showError });
@@ -0,0 +1,30 @@
1
+ import { type ReactNode } from "react";
2
+ import type { FormFieldBaseProps, FormFieldDescriptionProps, FormFieldMetaProps } from "./field-error.js";
3
+ export type ToggleGroupFieldOption<T extends string> = {
4
+ /** Value passed to `onValueChange` when this item is pressed. */
5
+ value: T;
6
+ /** Content of the toggle item. */
7
+ label: ReactNode;
8
+ };
9
+ type Props<T extends string> = FormFieldBaseProps & FormFieldMetaProps & FormFieldDescriptionProps & {
10
+ /** The selected option, or `""` for none yet. */
11
+ value: T | "";
12
+ /** Called with an option's value; pressing the active item keeps it. */
13
+ onValueChange: (value: T) => void;
14
+ /** The items to choose from, in order. */
15
+ options: ToggleGroupFieldOption<T>[];
16
+ /** Marks the label. */
17
+ required?: boolean;
18
+ /** Disables every item in the group. */
19
+ disabled?: boolean;
20
+ /** Size of the toggle items. Default "sm". */
21
+ size?: "default" | "sm" | "lg";
22
+ /** Forwarded to the toggle group. */
23
+ className?: string;
24
+ };
25
+ /**
26
+ * One choice from a few as a joined outline toggle group (a segmented
27
+ * control), with a visible label and the `form/*` error wiring.
28
+ */
29
+ export declare function FormToggleGroupField<T extends string>({ label, description, value, onValueChange, options, meta, submitted, showError, required, disabled, size, className, fieldClassName, }: Props<T>): import("react").JSX.Element;
30
+ export {};