@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.
- package/mcp/manifests/components.html +527 -65
- package/mcp/manifests/components.json +534 -1
- package/package.json +13 -1
- package/src/field/context.d.ts +8 -0
- package/src/field/field.d.ts +3 -1
- package/src/field/field.js +7 -4
- package/src/form/form.d.ts +60 -0
- package/src/form/form.js +16 -0
- package/src/form/index.d.ts +7 -0
- package/src/form/index.js +5 -0
- package/src/form-text/form-text.d.ts +136 -0
- package/src/form-text/form-text.js +40 -0
- package/src/form-text/index.d.ts +2 -0
- package/src/form-text/index.js +2 -0
|
@@ -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
|
+
};
|
package/src/form/form.js
ADDED
|
@@ -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,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 };
|