@agentero/design-system 0.28.0 → 0.29.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.
@@ -0,0 +1,60 @@
1
+ import { ComponentPropsWithRef } from 'react';
2
+ import { FieldValues, SubmitHandler, UseFormReturn } from 'react-hook-form';
3
+ export type FormRootProps<TFieldValues extends FieldValues = FieldValues, TTransformedValues = TFieldValues> = Omit<ComponentPropsWithRef<'form'>, 'onSubmit' | 'noValidate'> & {
4
+ /**
5
+ * The object `useForm()` returns. `Form.Root` hands it to every form field
6
+ * inside through react-hook-form's `FormProvider`, so a `FormText` needs only
7
+ * its `name`. Create it in the component that owns the form and pass
8
+ * `defaultValues`: a field whose value is `undefined` on the first render
9
+ * starts uncontrolled. A form whose resolver transforms the values
10
+ * (`useForm<Input, Context, Output>`) is accepted as is.
11
+ */
12
+ methods: UseFormReturn<TFieldValues, any, TTransformedValues>;
13
+ /**
14
+ * Called with the validated values once validation passes: the resolver's
15
+ * output when it transforms them, the form's values otherwise. Wrapped in
16
+ * `methods.handleSubmit`, which prevents the native submit and focuses the
17
+ * first invalid field; when it is omitted the form validates and does nothing
18
+ * else.
19
+ */
20
+ onSubmit?: SubmitHandler<TTransformedValues>;
21
+ /**
22
+ * Accessible name of the form. A `<form>` only becomes a landmark when it is
23
+ * named, and screen readers list landmarks by name, so it is required.
24
+ */
25
+ 'aria-label': string;
26
+ };
27
+ /**
28
+ * The `<form>` element of a react-hook-form form: it provides the `useForm()`
29
+ * instance to the form fields inside it (`FormText` and the other `Form<X>`
30
+ * components) and routes the submit through `handleSubmit`. It always renders
31
+ * `noValidate`, so the `required` attribute a field sets reaches assistive
32
+ * technology without the browser's own validation bubble getting in the way:
33
+ * validation is react-hook-form's, from `rules` or a resolver.
34
+ *
35
+ * Use it around any form built with the `Form<X>` fields. Without
36
+ * react-hook-form, render a plain `<form>` with the presentational primitives
37
+ * ([FieldText](?path=/docs/components-fieldtext--docs)) instead — this
38
+ * component is only the binding.
39
+ *
40
+ * `react-hook-form` is an optional peer dependency: the design system never
41
+ * bundles its own copy, because `FormProvider` and the fields must share one
42
+ * React context. Two copies in an app (a nested version under a workspace,
43
+ * say) make every field throw from `useController`; `yarn why react-hook-form`
44
+ * should list a single instance.
45
+ *
46
+ * @summary Form element bound to a react-hook-form instance, shared with its fields
47
+ *
48
+ * @example
49
+ * const methods = useForm<Values>({ defaultValues: { agencyName: '', email: '' } });
50
+ *
51
+ * <Form.Root methods={methods} onSubmit={saveAgency} aria-label="Agency profile">
52
+ * <FormText name="agencyName" label="Agency name" required rules={{ required: 'Enter the agency name.' }} />
53
+ * <FormText name="email" label="Email" inputProps={{ type: 'email' }} />
54
+ * <Button type="submit">Save</Button>
55
+ * </Form.Root>
56
+ */
57
+ export declare const Root: {
58
+ <TFieldValues extends FieldValues = FieldValues, TTransformedValues = TFieldValues>({ methods, onSubmit, ...props }: FormRootProps<TFieldValues, TTransformedValues>): import("react/jsx-runtime").JSX.Element;
59
+ displayName: string;
60
+ };
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as e } from "react/jsx-runtime";
3
+ import { FormProvider as t } from "react-hook-form";
4
+ //#region src/form/form.tsx
5
+ var n = () => {}, r = ({ methods: r, onSubmit: i, ...a }) => /* @__PURE__ */ e(t, {
6
+ ...r,
7
+ children: /* @__PURE__ */ e("form", {
8
+ "data-slot": "form",
9
+ onSubmit: r.handleSubmit(i ?? n),
10
+ ...a,
11
+ noValidate: !0
12
+ })
13
+ });
14
+ r.displayName = "Form.Root";
15
+ //#endregion
16
+ export { r as Root };
@@ -0,0 +1,7 @@
1
+ export type { FormRootProps } from './form';
2
+ export declare const Form: {
3
+ Root: {
4
+ <TFieldValues extends import('react-hook-form').FieldValues = import('react-hook-form').FieldValues, TTransformedValues = TFieldValues>({ methods, onSubmit, ...props }: import('./form').FormRootProps<TFieldValues, TTransformedValues>): import("react/jsx-runtime").JSX.Element;
5
+ displayName: string;
6
+ };
7
+ };
@@ -0,0 +1,5 @@
1
+ import { Root as e } from "./form.js";
2
+ //#region src/form/index.ts
3
+ var t = { Root: e };
4
+ //#endregion
5
+ export { t as Form };
@@ -0,0 +1,136 @@
1
+ import { ReactNode } from 'react';
2
+ import { Control, FieldPathByValue, FieldValues, UseControllerProps } from 'react-hook-form';
3
+ import { FieldLabelProps } from '../field';
4
+ import { FieldTextProps } from '../field-text';
5
+ import { InputProps } from '../input';
6
+ /**
7
+ * Props `FormText` forwards to its `Input`. The field owns `id` (pass
8
+ * `controlId` on `FormText` to choose it) and react-hook-form owns `name`,
9
+ * `value` and `defaultValue` (set it in `useForm`'s `defaultValues`).
10
+ *
11
+ * The state is the field's as well: `disabled`, `required` and `readOnly` are
12
+ * props of `FormText` itself and `aria-invalid` follows its validation state.
13
+ * An `Input` takes its own props over the ones its context hands it, so
14
+ * setting them here would move the control without the label and the root
15
+ * following. `aria-describedby` stays open: its ids join the ones the field
16
+ * wires rather than replacing them.
17
+ */
18
+ export type FormTextInputProps = Omit<InputProps, 'id' | 'name' | 'value' | 'defaultValue' | 'ref' | 'disabled' | 'required' | 'readOnly' | 'aria-invalid'>;
19
+ /**
20
+ * The paths of a form's values a `FormText` can bind to: those holding a
21
+ * string, `null` and `undefined` included, since an input stores a string and
22
+ * an empty form often starts from either. A path to a number, a boolean or an
23
+ * object is rejected at compile time; it belongs to the `Form<X>` of that
24
+ * value type. Untyped forms (`FieldValues`) accept any path.
25
+ */
26
+ export type FormTextPath<TFieldValues extends FieldValues> = FieldPathByValue<TFieldValues, string | null | undefined>;
27
+ export type FormTextProps<TFieldValues extends FieldValues = FieldValues, TName extends FormTextPath<TFieldValues> = FormTextPath<TFieldValues>, TTransformedValues = TFieldValues> = Omit<FieldTextProps, 'invalid' | 'children'> & {
28
+ /**
29
+ * Path of the value in the form, nested paths included (`'agency.npn'`).
30
+ * Type it against the form's values with the generic
31
+ * (`<FormText<Values> name="agency.npn" />`) or by passing `control`: the
32
+ * path must then exist and hold a string (`FormTextPath`).
33
+ */
34
+ name: TName;
35
+ /**
36
+ * The form's `control`, from `useForm()`. Defaults to the one the
37
+ * surrounding `Form.Root` provides; pass it explicitly to get `name`
38
+ * inference without writing the generic, or when the field renders outside
39
+ * a `Form.Root`. A `control` whose resolver transforms the values is
40
+ * accepted as is.
41
+ */
42
+ control?: Control<TFieldValues, any, TTransformedValues>;
43
+ /**
44
+ * react-hook-form validation rules for this field (`required`, `pattern`,
45
+ * `validate`…), same as `register`'s. They only validate: `rules.required`
46
+ * does not mark the field `required` — that is a separate, visible decision.
47
+ */
48
+ rules?: UseControllerProps<TFieldValues, TName>['rules'];
49
+ /**
50
+ * Drops the value from the form when the field unmounts, react-hook-form's
51
+ * `shouldUnregister`. Defaults to `false`: a field revealed by another one
52
+ * keeps its value while hidden.
53
+ */
54
+ shouldUnregister?: boolean;
55
+ /** The caption, rendered as the field's label. Text in almost every case. */
56
+ label: ReactNode;
57
+ /**
58
+ * Helper text shown under the control and announced through its
59
+ * `aria-describedby`. Use it for guidance the user needs before typing; put
60
+ * details worth a click in `tooltip` instead.
61
+ */
62
+ description?: ReactNode;
63
+ /**
64
+ * Content of an info tooltip rendered beside the label, for details that
65
+ * would clutter the description. The trigger is a sibling of the label, so
66
+ * the field's accessible name stays the label text.
67
+ */
68
+ tooltip?: FieldLabelProps['tooltip'];
69
+ /** Preferred side of the tooltip. Defaults to `'top'`. */
70
+ tooltipSide?: FieldLabelProps['tooltipSide'];
71
+ /**
72
+ * Appends a muted " (optional)" to the label. The forms mark optional fields
73
+ * rather than required ones; ignored when `required` is set.
74
+ */
75
+ optional?: boolean;
76
+ /**
77
+ * Marks the field required: an asterisk on the label and the native
78
+ * `required` attribute on the input, which assistive technology announces.
79
+ * It does not validate — pair it with `rules.required` or a resolver — and it
80
+ * never triggers the browser bubble, since `Form.Root` renders `noValidate`.
81
+ */
82
+ required?: boolean;
83
+ /**
84
+ * Disables the input and marks the field. Unlike react-hook-form's own
85
+ * `disabled` option, the value stays in the submitted data: disabling a
86
+ * field is a presentation decision, not a change to the form's values.
87
+ */
88
+ disabled?: boolean;
89
+ /**
90
+ * Everything else the `Input` should receive: `type`, `placeholder`,
91
+ * `maxLength`, `autoComplete`, `size`, `className`… Handlers such as
92
+ * `onChange` and `onBlur` run after react-hook-form's, which are already
93
+ * wired.
94
+ */
95
+ inputProps?: FormTextInputProps;
96
+ };
97
+ /**
98
+ * A complete text field bound to one react-hook-form value: label, `Input`,
99
+ * helper text and validation error, laid out in the standard order and wired
100
+ * through `useController`. It renders inside a `Form.Root`, which provides the
101
+ * form, and needs only a `name`, which on a typed form must point at a string
102
+ * (`FormTextPath`). The `required`, `invalid` and error states
103
+ * reach the label, the input's `aria-*` attributes and the message with no
104
+ * ids written by hand.
105
+ *
106
+ * Use it for the common case: a labelled single-line input in a form. When
107
+ * the layout is different — an input with addons, another control, a
108
+ * message rendered elsewhere — compose the primitives yourself:
109
+ * `useController` with [FieldText](?path=/docs/components-fieldtext--docs),
110
+ * `Label`, `Input`, `Field.Description` and `Field.Error`. `FormText` adds no
111
+ * behaviour of its own over that composition.
112
+ *
113
+ * Errors come from `useController`'s per-field state, never from
114
+ * `formState.errors`, so the message stays fresh under the React Compiler.
115
+ * With `criteriaMode: 'all'` on the form the error carries every rule that
116
+ * failed and `Field.Error` lists them all. An error on an array or object path
117
+ * (`error.root`) is not this field's: render a `Field.Error` for it where the
118
+ * group lives.
119
+ *
120
+ * @summary Label, Input, description and error bound to one react-hook-form field
121
+ *
122
+ * @example
123
+ * <FormText
124
+ * name="taxId"
125
+ * label="Tax ID"
126
+ * tooltip="The EIN the IRS issued to the agency."
127
+ * description="Nine digits, with or without the dash."
128
+ * required
129
+ * rules={{ required: 'Enter the tax ID.', pattern: { value: /^\d{2}-?\d{7}$/, message: 'Nine digits.' } }}
130
+ * inputProps={{ inputMode: 'numeric', autoComplete: 'off' }}
131
+ * />
132
+ */
133
+ export declare const FormText: {
134
+ <TFieldValues extends FieldValues = FieldValues, TName extends FormTextPath<TFieldValues> = FormTextPath<TFieldValues>, TTransformedValues = TFieldValues>({ name, control, rules, shouldUnregister, disabled, label, description, tooltip, tooltipSide, optional, inputProps, ...root }: FormTextProps<TFieldValues, TName, TTransformedValues>): import("react/jsx-runtime").JSX.Element;
135
+ displayName: string;
136
+ };
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { mergeProps as e } from "../../lib/merge-props.js";
3
+ import { Field as t } from "../field/index.js";
4
+ import { Input as n } from "../input/input.js";
5
+ import { FieldText as r } from "../field-text/field-text.js";
6
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
7
+ import { useController as o } from "react-hook-form";
8
+ //#region src/form-text/form-text.tsx
9
+ var s = ({ name: s, control: c, rules: l, shouldUnregister: u, disabled: d, label: f, description: p, tooltip: m, tooltipSide: h, optional: g, inputProps: _, ...v }) => {
10
+ let { field: y, fieldState: b } = o({
11
+ name: s,
12
+ control: c,
13
+ rules: l,
14
+ shouldUnregister: u
15
+ }), x = {
16
+ name: y.name,
17
+ value: y.value ?? "",
18
+ onChange: y.onChange,
19
+ onBlur: y.onBlur,
20
+ ref: y.ref
21
+ };
22
+ return /* @__PURE__ */ a(r, {
23
+ invalid: b.invalid,
24
+ disabled: d || y.disabled,
25
+ ...v,
26
+ children: [/* @__PURE__ */ i(t.Label, {
27
+ tooltip: m,
28
+ tooltipSide: h,
29
+ optional: g,
30
+ children: f
31
+ }), /* @__PURE__ */ a(t.Content, { children: [
32
+ /* @__PURE__ */ i(n, { ...e(x, _ ?? {}) }),
33
+ p && /* @__PURE__ */ i(t.Description, { children: p }),
34
+ /* @__PURE__ */ i(t.Error, { errors: b.error ? [b.error] : void 0 })
35
+ ] })]
36
+ });
37
+ };
38
+ s.displayName = "FormText";
39
+ //#endregion
40
+ export { s as FormText };
@@ -0,0 +1,2 @@
1
+ export { FormText } from './form-text';
2
+ export type { FormTextInputProps, FormTextPath, FormTextProps } from './form-text';
@@ -0,0 +1,2 @@
1
+ import { FormText as e } from "./form-text.js";
2
+ export { e as FormText };