@agentero/design-system 0.32.2 → 0.33.1

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.
@@ -2850,7 +2850,7 @@
2850
2850
  {
2851
2851
  "id": "components-combobox--inside-field-text",
2852
2852
  "name": "Inside Field Text",
2853
- "snippet": "const InsideFieldText = () => (\n <FieldText invalid required>\n <Label>Agency</Label>\n <Combobox.Root items={STATES}>\n <Combobox.Input placeholder=\"Search agencies\" />\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n <Field.Description>The agency this agent will be added to.</Field.Description>\n <Field.Error errors={[{ message: 'Pick an agency from the list' }]} />\n </FieldText>\n);",
2853
+ "snippet": "const InsideFieldText = () => (\n <FieldText invalid required>\n <Label>Agency</Label>\n <Combobox.Root items={STATES}>\n <Combobox.Input placeholder=\"Search agencies\" />\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n <Field.Error errors={[{ message: 'Pick an agency from the list' }]} />\n <Field.Description>The agency this agent will be added to.</Field.Description>\n </FieldText>\n);",
2854
2854
  "description": "Dropped into a `FieldText`, the input takes the label, the description and\nthe error from the field with nothing wired by hand: `Combobox.Input` reads the\nsame `InputContext` the plain `Input` does. It also points `aria-labelledby`\nat the label, so the name survives the list opening: Base UI then hides the\nrest of the page from assistive technology, label included, and a\n`<label for>` alone would stop naming the input."
2855
2855
  },
2856
2856
  {
@@ -5295,7 +5295,7 @@
5295
5295
  "Field for a single-line text control, wiring the Input inside it"
5296
5296
  ],
5297
5297
  "example": [
5298
- "<FieldText invalid={!!error} required>\n <Label>Email</Label>\n <Input type=\"email\" />\n <Field.Description>We only use this for policy documents.</Field.Description>\n <Field.Error errors={[error]} />\n</FieldText>"
5298
+ "<FieldText invalid={!!error} required>\n <Label>Email</Label>\n <Input type=\"email\" />\n <Field.Error errors={[error]} />\n <Field.Description>We only use this for policy documents.</Field.Description>\n</FieldText>"
5299
5299
  ]
5300
5300
  },
5301
5301
  "description": "FieldText is a `Field.Root` for a single-line text control. It provides\n`InputContext`, so the `Input` inside associates itself with the label and\nthe messages and takes `invalid`, `required`, `disabled` and `readOnly` from\nthe field — no `id`, `htmlFor`, `aria-describedby` or `aria-invalid` written\nby hand.\n\nIt knows nothing about form libraries, and its contexts compose: a provider\nnested inside it can extend `InputContext` with `value`, `onChange`, `onBlur`\nand `name`, and `FieldContext` with the errors, as the last story shows.",
@@ -5310,7 +5310,7 @@
5310
5310
  "Field for a single-line text control, wiring the Input inside it"
5311
5311
  ],
5312
5312
  "example": [
5313
- "<FieldText invalid={!!error} required>\n <Label>Email</Label>\n <Input type=\"email\" />\n <Field.Description>We only use this for policy documents.</Field.Description>\n <Field.Error errors={[error]} />\n</FieldText>"
5313
+ "<FieldText invalid={!!error} required>\n <Label>Email</Label>\n <Input type=\"email\" />\n <Field.Error errors={[error]} />\n <Field.Description>We only use this for policy documents.</Field.Description>\n</FieldText>"
5314
5314
  ]
5315
5315
  },
5316
5316
  "props": {
@@ -5504,7 +5504,7 @@
5504
5504
  "Wraps a label, control, description and error into one field"
5505
5505
  ],
5506
5506
  "example": [
5507
- "<Field.Root invalid={!!error} required>\n <Field.Label>Email</Field.Label>\n <Input type=\"email\" />\n <Field.Description>We only use this for policy documents.</Field.Description>\n <Field.Error errors={[error]} />\n</Field.Root>"
5507
+ "<Field.Root invalid={!!error} required>\n <Field.Label>Email</Field.Label>\n <Input type=\"email\" />\n <Field.Error errors={[error]} />\n <Field.Description>We only use this for policy documents.</Field.Description>\n</Field.Root>"
5508
5508
  ]
5509
5509
  },
5510
5510
  "description": "Field lays out a single form field — label, control, helper text and error —\nand wires the accessibility relationships between them. `Field.Root`\ngenerates the ids and shares them through context: `Label` reads\n`LabelContext`, `Field.Description` and `Field.Error` read `FieldContext`,\nand a `Field<X>` such as `FieldText` provides the control's context. Nobody\npasses an `id` by hand.\n\nPresentational and form-library agnostic: pass `invalid` and the error\nmessages from whatever validates the form. Spacing between fields belongs to\n`Field.Group`, never to a margin on the field itself.\n\n`Field.Description` and `Field.Error` register themselves with the root, so\nthe control's `aria-describedby` lists exactly the messages on screen and is\nabsent when there is none.",
@@ -5519,7 +5519,7 @@
5519
5519
  "Wraps a label, control, description and error into one field"
5520
5520
  ],
5521
5521
  "example": [
5522
- "<Field.Root invalid={!!error} required>\n <Field.Label>Email</Field.Label>\n <Input type=\"email\" />\n <Field.Description>We only use this for policy documents.</Field.Description>\n <Field.Error errors={[error]} />\n</Field.Root>"
5522
+ "<Field.Root invalid={!!error} required>\n <Field.Label>Email</Field.Label>\n <Input type=\"email\" />\n <Field.Error errors={[error]} />\n <Field.Description>We only use this for policy documents.</Field.Description>\n</Field.Root>"
5523
5523
  ]
5524
5524
  },
5525
5525
  "props": {
@@ -5718,6 +5718,13 @@
5718
5718
  "description": "`Field.Error` renders nothing without a message, so it can stay mounted and\nreceive `undefined` entries while the field is valid. With no description\neither, the control carries no `aria-describedby` at all.",
5719
5719
  "summary": "Error stays mounted and renders nothing while valid"
5720
5720
  },
5721
+ {
5722
+ "id": "components-field--error-before-description",
5723
+ "name": "Error Before Description",
5724
+ "snippet": "const ErrorBeforeDescription = () => {\n const [error, setError] = useState<string>();\n\n return (\n <div className=\"flex flex-col items-start gap-4\">\n <Field.Root\n orientation=\"vertical\"\n disabled={false}\n readOnly={false}\n required={false}\n invalid={!!error}>\n <Label>Tax ID</Label>\n <DemoInput inputMode=\"numeric\" />\n <Field.Error id=\"tax-id-error\">{error}</Field.Error>\n <Field.Description>Nine digits, with or without the dash.</Field.Description>\n </Field.Root>\n <button type=\"button\" onClick={() => setError('Enter the tax ID.')}>Validate\n </button>\n </div>\n );\n};",
5725
+ "description": "The error sits right under the control and is announced before the\ndescription, even when it appears after the description is on screen and\ncarries an `id` of its own.",
5726
+ "summary": "Error announced before the description, whenever it appears"
5727
+ },
5721
5728
  {
5722
5729
  "id": "components-field--disabled",
5723
5730
  "name": "Disabled",
@@ -5795,7 +5802,7 @@
5795
5802
  "displayName": "FormText",
5796
5803
  "exportName": "FormText",
5797
5804
  "filePath": "/home/runner/work/design-system/design-system/src/form-text/index.ts",
5798
- "description": "A complete text field bound to one react-hook-form value: label, `Input`,\nhelper text and validation error, laid out in the standard order and wired\nthrough `useController`. It renders inside a `Form.Root`, which provides the\nform, and needs only a `name`, which on a typed form must point at a string\n(`FormTextPath`). The `required`, `invalid` and error states\nreach the label, the input's `aria-*` attributes and the message with no\nids written by hand.\n\nUse it for the common case: a labelled single-line input in a form. When\nthe layout is different — an input with addons, another control, a\nmessage rendered elsewhere — compose the primitives yourself:\n`useController` with [FieldText](?path=/docs/components-fieldtext--docs),\n`Label`, `Input`, `Field.Description` and `Field.Error`. `FormText` adds no\nbehaviour of its own over that composition.\n\nErrors come from `useController`'s per-field state, never from\n`formState.errors`, so the message stays fresh under the React Compiler.\nWith `criteriaMode: 'all'` on the form the error carries every rule that\nfailed and `Field.Error` lists them all. An error on an array or object path\n(`error.root`) is not this field's: render a `Field.Error` for it where the\ngroup lives.",
5805
+ "description": "A complete text field bound to one react-hook-form value: label, `Input`,\nvalidation error and helper text, laid out in the standard order and wired\nthrough `useController`. It renders inside a `Form.Root`, which provides the\nform, and needs only a `name`, which on a typed form must point at a string\n(`FormTextPath`). The `required`, `invalid` and error states\nreach the label, the input's `aria-*` attributes and the message with no\nids written by hand.\n\nUse it for the common case: a labelled single-line input in a form. When\nthe layout is different — an input with addons, another control, a\nmessage rendered elsewhere — compose the primitives yourself:\n`useController` with [FieldText](?path=/docs/components-fieldtext--docs),\n`Label`, `Input`, `Field.Description` and `Field.Error`. `FormText` adds no\nbehaviour of its own over that composition.\n\nErrors come from `useController`'s per-field state, never from\n`formState.errors`, so the message stays fresh under the React Compiler.\nWith `criteriaMode: 'all'` on the form the error carries every rule that\nfailed and `Field.Error` lists them all. An error on an array or object path\n(`error.root`) is not this field's: render a `Field.Error` for it where the\ngroup lives.",
5799
5806
  "jsDocTags": {
5800
5807
  "summary": [
5801
5808
  "Label, Input, description and error bound to one react-hook-form field"
@@ -6021,7 +6028,7 @@
6021
6028
  "type": {
6022
6029
  "name": "ReactNode"
6023
6030
  },
6024
- "description": "Helper text shown under the control and announced through its\n`aria-describedby`. Use it for guidance the user needs before typing; put\ndetails worth a click in `tooltip` instead.",
6031
+ "description": "Helper text shown under the control, below the error when there is one,\nand announced through its `aria-describedby`. Use it for guidance the\nuser needs before typing; put details worth a click in `tooltip` instead.",
6025
6032
  "defaultValue": null,
6026
6033
  "parent": {
6027
6034
  "name": "FormTextProps",
@@ -6156,6 +6163,13 @@
6156
6163
  "description": "`rules` are react-hook-form's, and `name` accepts nested paths typed against\nthe form's values through the generic. `rules.required` validates but does\nnot mark the field required: that stays an explicit `required` prop.",
6157
6164
  "summary": "Nested path with validation rules, typed through the generic"
6158
6165
  },
6166
+ {
6167
+ "id": "components-formtext--error-and-description",
6168
+ "name": "Error And Description",
6169
+ "snippet": "const ErrorAndDescription = () => {\n const methods = useForm<TaxValues>({ defaultValues: { taxId: '' } });\n\n return (\n <Form.Root\n methods={methods}\n onSubmit={onSubmit}\n aria-label=\"Tax details\"\n className=\"flex flex-col items-start gap-7\">\n <FormText\n name=\"taxId\"\n label=\"Tax ID\"\n description=\"Nine digits, with or without the dash.\"\n rules={{ required: 'Enter the tax ID.' }}\n inputProps={{ inputMode: 'numeric', autoComplete: 'off' }}\n />\n <Button type=\"submit\">Save</Button>\n </Form.Root>\n );\n};",
6170
+ "description": "The error sits right under the input and the description stays below it, so\nthe message lands next to the value it is about. Screen readers announce\nthem in the same order.",
6171
+ "summary": "Error under the input, above the helper text"
6172
+ },
6159
6173
  {
6160
6174
  "id": "components-formtext--all-errors",
6161
6175
  "name": "All Errors",
@@ -6624,6 +6638,112 @@
6624
6638
  ],
6625
6639
  "import": "import {\n Button,\n Divider,\n DropdownMenu,\n Field,\n FieldText,\n IconCancel,\n IconKeyboardArrowDown,\n IconSearch,\n Input,\n InputGroup,\n Label,\n Tag,\n} from \"@agentero/design-system\";"
6626
6640
  },
6641
+ "components-inputpassword": {
6642
+ "id": "components-inputpassword",
6643
+ "name": "InputPassword",
6644
+ "path": "./src/input-password/input-password.stories.tsx",
6645
+ "jsDocTags": {
6646
+ "summary": [
6647
+ "Password input with a show/hide toggle inside the frame"
6648
+ ],
6649
+ "dataAttribute": [
6650
+ "{string} data-slot - Always set to \"input-password\" on the frame"
6651
+ ],
6652
+ "example": [
6653
+ "<FieldText required>\n <Label>Password</Label>\n <InputPassword name=\"password\" autoComplete=\"current-password\" />\n</FieldText>"
6654
+ ]
6655
+ },
6656
+ "description": "InputPassword is a password field with a show/hide toggle at the end of its\nframe. It is an `Input` inside an `InputGroup`: `className` styles the\nframe and every other prop reaches the `<input>`, which takes its label,\nmessages and states from a surrounding `FieldText`. The toggle is not\nrendered while the input is disabled.",
6657
+ "summary": "Password input with a show/hide toggle inside the frame",
6658
+ "reactComponentMeta": {
6659
+ "displayName": "InputPassword",
6660
+ "exportName": "InputPassword",
6661
+ "filePath": "/home/runner/work/design-system/design-system/src/input-password/input-password.tsx",
6662
+ "description": "InputPassword is a password field with a show/hide toggle at the end of its\nframe. The value starts hidden; the toggle reveals it as plain text and\nhides it again. Its accessible name reads \"Show password\" or \"Hide\npassword\", depending on what the next press does. Pressing the toggle keeps\nfocus and the caret in the input. The toggle is not rendered while the input\nis disabled, whether `disabled` comes from the prop, from a surrounding\n`FieldText` or from a `<fieldset disabled>`.\n\nIt is an [Input](?path=/docs/components-input--docs) inside an\n[InputGroup](?path=/docs/components-inputgroup--docs) frame. `className`\nstyles the frame. Every other prop goes to the `<input>`: the `ref`, `id`,\n`name`, `value`, `onChange`, `aria-*` and every other standard attribute,\nsuch as `autoComplete`, `required` or `maxLength`. Only `type` is not\naccepted, because the toggle owns it. Inside a `FieldText` the input takes\nits label, messages and states from the field as a plain `Input` does. The\ninvalid state comes from `aria-invalid`, and the frame paints the\ndestructive border. Set `autoComplete` to `current-password` on sign-in and\n`new-password` when the user picks one, so password managers fill the right\nvalue.\n\nDo not use it for text that is not secret; use `Input` instead. A one-time\ncode is an `Input` with `autoComplete=\"one-time-code\"`, not a password.",
6663
+ "jsDocTags": {
6664
+ "summary": [
6665
+ "Password input with a show/hide toggle inside the frame"
6666
+ ],
6667
+ "dataAttribute": [
6668
+ "{string} data-slot - Always set to \"input-password\" on the frame"
6669
+ ],
6670
+ "example": [
6671
+ "<FieldText required>\n <Label>Password</Label>\n <InputPassword name=\"password\" autoComplete=\"current-password\" />\n</FieldText>"
6672
+ ]
6673
+ },
6674
+ "props": {
6675
+ "size": {
6676
+ "name": "size",
6677
+ "required": false,
6678
+ "type": {
6679
+ "name": "enum",
6680
+ "raw": "\"sm\" | \"md\" | \"lg\"",
6681
+ "value": [
6682
+ {
6683
+ "value": "\"sm\""
6684
+ },
6685
+ {
6686
+ "value": "\"md\""
6687
+ },
6688
+ {
6689
+ "value": "\"lg\""
6690
+ }
6691
+ ]
6692
+ },
6693
+ "description": "Control height. Defaults to `'md'`.\n- `sm` (32px) — dense layouts where vertical space is tight.\n- `md` (40px) — standard form density.\n- `lg` (48px) — low-density forms and larger touch targets. Also raises\n the text to `base` and the corner radius to `lg`.",
6694
+ "defaultValue": null,
6695
+ "parent": {
6696
+ "name": "InputProps",
6697
+ "fileName": "src/input/input.tsx"
6698
+ },
6699
+ "declarations": [
6700
+ {
6701
+ "name": "TypeLiteral",
6702
+ "fileName": "src/input/input.tsx"
6703
+ }
6704
+ ]
6705
+ }
6706
+ }
6707
+ },
6708
+ "stories": [
6709
+ {
6710
+ "id": "components-inputpassword--default",
6711
+ "name": "Default",
6712
+ "snippet": "const Default = () => <InputPassword aria-label=\"Password\" autoComplete=\"current-password\" />;",
6713
+ "description": "The value starts hidden. The toggle shows it as plain text and hides it\nagain; pressing it leaves focus and the caret in the input.",
6714
+ "summary": "Hidden value with a toggle that reveals and hides it"
6715
+ },
6716
+ {
6717
+ "id": "components-inputpassword--disabled",
6718
+ "name": "Disabled",
6719
+ "snippet": "const Disabled = () => <div style={{ display: 'grid', gap: '1rem' }}>\n <InputPassword\n aria-label=\"Password\"\n autoComplete=\"current-password\"\n disabled\n defaultValue=\"hunter22\" />\n <fieldset disabled style={{ border: 0, margin: 0, padding: 0 }}>\n <InputPassword aria-label=\"New password\" autoComplete=\"new-password\" />\n </fieldset>\n</div>;",
6720
+ "description": "A disabled input has no toggle, whether `disabled` is set on the input\nitself or comes from a `<fieldset disabled>` around it.",
6721
+ "summary": "Disabled input with the toggle removed"
6722
+ },
6723
+ {
6724
+ "id": "components-inputpassword--invalid",
6725
+ "name": "Invalid",
6726
+ "snippet": "const Invalid = () => <div style={{ display: 'grid', gap: '1.5rem' }}>\n <InputPassword\n autoComplete=\"current-password\"\n aria-label=\"Valid password\"\n defaultValue=\"hunter22\" />\n <FieldText invalid>\n <Label>Password</Label>\n <InputPassword autoComplete=\"new-password\" defaultValue=\"hunter\" />\n <Field.Error errors={[{ message: 'Use at least 8 characters.' }]} />\n </FieldText>\n</div>;",
6727
+ "description": "`invalid` on the surrounding `FieldText` sets `aria-invalid` on the input,\nwhich paints the frame's destructive border, and `Field.Error` is announced\nwith the input.",
6728
+ "summary": "Invalid field driving the frame's destructive border"
6729
+ },
6730
+ {
6731
+ "id": "components-inputpassword--all-sizes",
6732
+ "name": "All Sizes",
6733
+ "snippet": "const AllSizes = () => <div style={{ display: 'grid', gap: '1rem' }}>\n <InputPassword autoComplete=\"current-password\" size=\"sm\" aria-label=\"Small\" />\n <InputPassword autoComplete=\"current-password\" size=\"md\" aria-label=\"Medium\" />\n <InputPassword autoComplete=\"current-password\" size=\"lg\" aria-label=\"Large\" />\n</div>;",
6734
+ "description": "The three sizes match Input's: the frame is 32, 40 or 48px tall. The toggle\ngrows with it (a 24px button with a 16px icon at `sm`, 32px with 20px at\n`md`, 32px with 24px at `lg`) and sits as far from the right edge as from\nthe top and bottom.",
6735
+ "summary": "The three sizes, with the toggle scaled and evenly inset"
6736
+ },
6737
+ {
6738
+ "id": "components-inputpassword--inside-field-text",
6739
+ "name": "Inside Field Text",
6740
+ "snippet": "const InsideFieldText = () => (\n <div style={{ display: 'grid', gap: '1.5rem' }}>\n <FieldText required>\n <Label>Password</Label>\n <InputPassword autoComplete=\"new-password\" />\n <Field.Description>At least 8 characters.</Field.Description>\n </FieldText>\n <FieldText disabled>\n <Label>Current password</Label>\n <InputPassword autoComplete=\"current-password\" defaultValue=\"hunter22\" />\n </FieldText>\n </div>\n);",
6741
+ "description": "Inside a `FieldText` the input takes its label, description and states from\nthe field through the frame. A disabled field removes the toggle too.",
6742
+ "summary": "Label and disabled state reaching the input through the frame"
6743
+ }
6744
+ ],
6745
+ "import": "import { Field, FieldText, InputPassword, Label } from \"@agentero/design-system\";"
6746
+ },
6627
6747
  "components-input": {
6628
6748
  "id": "components-input",
6629
6749
  "name": "Input",
@@ -9590,6 +9710,6 @@
9590
9710
  },
9591
9711
  "meta": {
9592
9712
  "docgen": "react-component-meta",
9593
- "durationMs": 4877
9713
+ "durationMs": 3879
9594
9714
  }
9595
9715
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentero/design-system",
3
- "version": "0.32.2",
3
+ "version": "0.33.1",
4
4
  "description": "A React component library built with Tailwind CSS v4 and Radix UI primitives",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -97,6 +97,10 @@
97
97
  "types": "./src/input-group/index.d.ts",
98
98
  "import": "./src/input-group/index.js"
99
99
  },
100
+ "./input-password": {
101
+ "types": "./src/input-password/index.d.ts",
102
+ "import": "./src/input-password/index.js"
103
+ },
100
104
  "./label": {
101
105
  "types": "./src/label/index.d.ts",
102
106
  "import": "./src/label/index.js"
@@ -11,6 +11,8 @@ export type FieldErrorLike = {
11
11
  */
12
12
  types?: Record<string, string | string[] | boolean | undefined>;
13
13
  } | undefined;
14
+ /** What a registered message is, which sets its place in `aria-describedby`. */
15
+ export type FieldMessageKind = 'error' | 'description';
14
16
  /**
15
17
  * State of the surrounding `Field.Root`, shared with the Field family
16
18
  * (`Field.Description`, `Field.Error`) and with controls that have no context
@@ -39,9 +41,11 @@ export type FieldContextValue = {
39
41
  /**
40
42
  * Adds a message element to `describedBy` and returns the function that
41
43
  * removes it. `Field.Description` and `Field.Error` call it from a layout
42
- * effect; a custom message element can do the same.
44
+ * effect; a custom message element can do the same. Errors are listed first,
45
+ * then descriptions, then messages registered without a `kind`, each in the
46
+ * order they registered.
43
47
  */
44
- registerMessage: (id: string) => () => void;
48
+ registerMessage: (id: string, kind?: FieldMessageKind) => () => void;
45
49
  invalid: boolean;
46
50
  disabled: boolean;
47
51
  readOnly: boolean;
@@ -141,8 +141,8 @@ export type FieldRootProps = ComponentPropsWithRef<'div'> & {
141
141
  * <Field.Root invalid={!!error} required>
142
142
  * <Field.Label>Email</Field.Label>
143
143
  * <Input type="email" />
144
- * <Field.Description>We only use this for policy documents.</Field.Description>
145
144
  * <Field.Error errors={[error]} />
145
+ * <Field.Description>We only use this for policy documents.</Field.Description>
146
146
  * </Field.Root>
147
147
  */
148
148
  export declare const Root: {
@@ -201,7 +201,9 @@ export declare const Description: {
201
201
  };
202
202
  /**
203
203
  * Validation feedback for the field, announced as an alert when it appears.
204
- * Renders nothing without a message, so it can stay mounted unconditionally;
204
+ * Place it right after the control, before `Field.Description`: the control's
205
+ * `aria-describedby` reads the error first as well. Renders nothing without a
206
+ * message, so it can stay mounted unconditionally;
205
207
  * set `invalid` on `Field.Root` alongside it. Takes `children` or an `errors`
206
208
  * array, and a form adapter can supply `errors` through `FieldContext` so a
207
209
  * bare `<Field.Error />` renders them. One per field: it takes the field's
@@ -38,31 +38,34 @@ var m = s({
38
38
  ] }
39
39
  } },
40
40
  defaultVariants: { orientation: "vertical" }
41
- }), h = m(), g = ({ className: t, ...n }) => /* @__PURE__ */ f("div", {
41
+ }), h = m(), g = (e) => e === "error" ? 0 : e === "description" ? 1 : 2, _ = ({ className: t, ...n }) => /* @__PURE__ */ f("div", {
42
42
  "data-slot": "field-group",
43
43
  className: e(h.group(), t),
44
44
  ...n
45
45
  });
46
- g.displayName = "Field.Group";
47
- var _ = ({ className: n, orientation: r = "vertical", invalid: a = !1, disabled: o = !1, readOnly: s = !1, required: u = !1, controlId: p, ...m }) => {
48
- let g = l(), _ = p ?? `${g}-control`, v = `${g}-label`, y = `${g}-description`, b = `${g}-error`, [x, S] = d([]), C = c((e) => (S((t) => t.includes(e) ? t : [...t, e]), () => S((t) => t.filter((t) => t !== e))), []), w = {
49
- controlId: _,
50
- labelId: v,
51
- descriptionId: y,
52
- errorId: b,
53
- describedBy: [...[y, b].filter((e) => x.includes(e)), ...x.filter((e) => e !== y && e !== b)].join(" ") || void 0,
54
- registerMessage: C,
46
+ _.displayName = "Field.Group";
47
+ var v = ({ className: n, orientation: r = "vertical", invalid: a = !1, disabled: o = !1, readOnly: s = !1, required: u = !1, controlId: p, ...m }) => {
48
+ let _ = l(), v = p ?? `${_}-control`, y = `${_}-label`, b = `${_}-description`, x = `${_}-error`, [S, C] = d([]), w = c((e, t) => (C((n) => n.some((t) => t.id === e) ? n : [...n, {
49
+ id: e,
50
+ kind: t
51
+ }]), () => C((t) => t.filter((t) => t.id !== e))), []), T = {
52
+ controlId: v,
53
+ labelId: y,
54
+ descriptionId: b,
55
+ errorId: x,
56
+ describedBy: [...S].sort((e, t) => g(e.kind) - g(t.kind)).map((e) => e.id).join(" ") || void 0,
57
+ registerMessage: w,
55
58
  invalid: a,
56
59
  disabled: o,
57
60
  readOnly: s,
58
61
  required: u
59
62
  };
60
63
  return /* @__PURE__ */ f(i, {
61
- value: w,
64
+ value: T,
62
65
  children: /* @__PURE__ */ f(t, {
63
66
  value: {
64
- id: v,
65
- htmlFor: _,
67
+ id: y,
68
+ htmlFor: v,
66
69
  required: u || void 0
67
70
  },
68
71
  children: /* @__PURE__ */ f("div", {
@@ -77,14 +80,14 @@ var _ = ({ className: n, orientation: r = "vertical", invalid: a = !1, disabled:
77
80
  })
78
81
  });
79
82
  };
80
- _.displayName = "Field.Root";
81
- var v = ({ className: t, ...n }) => /* @__PURE__ */ f("div", {
83
+ v.displayName = "Field.Root";
84
+ var y = ({ className: t, ...n }) => /* @__PURE__ */ f("div", {
82
85
  "data-slot": "field-content",
83
86
  className: e(h.content(), t),
84
87
  ...n
85
88
  });
86
- v.displayName = "Field.Content";
87
- var y = ({ tooltip: e, tooltipSide: t, ...i }) => /* @__PURE__ */ p("div", {
89
+ y.displayName = "Field.Content";
90
+ var b = ({ tooltip: e, tooltipSide: t, ...i }) => /* @__PURE__ */ p("div", {
88
91
  "data-slot": "field-label",
89
92
  className: h.label(),
90
93
  children: [/* @__PURE__ */ f(n, { ...i }), e && /* @__PURE__ */ f(r, {
@@ -100,23 +103,23 @@ var y = ({ tooltip: e, tooltipSide: t, ...i }) => /* @__PURE__ */ p("div", {
100
103
  })
101
104
  })]
102
105
  });
103
- y.displayName = "Field.Label";
104
- var b = ({ className: t, id: n, ...r }) => {
106
+ b.displayName = "Field.Label";
107
+ var x = ({ className: t, id: n, ...r }) => {
105
108
  let i = a(), o = n ?? i?.descriptionId, s = i?.registerMessage;
106
- return u(() => o ? s?.(o) : void 0, [s, o]), /* @__PURE__ */ f("p", {
109
+ return u(() => o ? s?.(o, "description") : void 0, [s, o]), /* @__PURE__ */ f("p", {
107
110
  "data-slot": "field-description",
108
111
  id: o,
109
112
  className: e(h.description(), t),
110
113
  ...r
111
114
  });
112
115
  };
113
- b.displayName = "Field.Description";
114
- var x = (e) => {
116
+ x.displayName = "Field.Description";
117
+ var S = (e) => {
115
118
  let t = Object.values(e?.types ?? {}).flat().filter((e) => typeof e == "string" && e.length > 0);
116
119
  return t.length ? t : e?.message ? [e.message] : [];
117
- }, S = ({ className: t, children: n, errors: r, id: i, ...o }) => {
118
- let s = a(), c = i ?? s?.errorId, l = r ?? s?.errors, d = s?.registerMessage, p = [...new Set(l?.flatMap(x))], m = n ?? (p.length > 1 ? /* @__PURE__ */ f("ul", { children: p.map((e) => /* @__PURE__ */ f("li", { children: e }, e)) }) : p[0]), g = !!m;
119
- return u(() => g && c ? d?.(c) : void 0, [
120
+ }, C = ({ className: t, children: n, errors: r, id: i, ...o }) => {
121
+ let s = a(), c = i ?? s?.errorId, l = r ?? s?.errors, d = s?.registerMessage, p = [...new Set(l?.flatMap(S))], m = n ?? (p.length > 1 ? /* @__PURE__ */ f("ul", { children: p.map((e) => /* @__PURE__ */ f("li", { children: e }, e)) }) : p[0]), g = !!m;
122
+ return u(() => g && c ? d?.(c, "error") : void 0, [
120
123
  d,
121
124
  g,
122
125
  c
@@ -129,6 +132,6 @@ var x = (e) => {
129
132
  children: m
130
133
  }) : null;
131
134
  };
132
- S.displayName = "Field.Error";
135
+ C.displayName = "Field.Error";
133
136
  //#endregion
134
- export { v as Content, b as Description, S as Error, g as Group, y as Label, _ as Root, m as fieldRecipe };
137
+ export { y as Content, x as Description, C as Error, _ as Group, b as Label, v as Root, m as fieldRecipe };
@@ -1,7 +1,7 @@
1
1
  export { fieldRecipe } from './field';
2
2
  export { FieldContext, useFieldContext } from './context';
3
3
  export type { FieldContentProps, FieldGroupProps, FieldLabelProps, FieldRootProps, FieldVariants } from './field';
4
- export type { FieldContextValue, FieldDescriptionProps, FieldErrorLike, FieldErrorProps } from './context';
4
+ export type { FieldContextValue, FieldDescriptionProps, FieldErrorLike, FieldErrorProps, FieldMessageKind } from './context';
5
5
  export declare const Field: {
6
6
  Root: {
7
7
  ({ className, orientation, invalid, disabled, readOnly, required, controlId: controlIdProp, ...props }: import('./field').FieldRootProps): import("react/jsx-runtime").JSX.Element;
@@ -18,8 +18,8 @@ export type FieldTextProps = FieldRootProps;
18
18
  * <FieldText invalid={!!error} required>
19
19
  * <Label>Email</Label>
20
20
  * <Input type="email" />
21
- * <Field.Description>We only use this for policy documents.</Field.Description>
22
21
  * <Field.Error errors={[error]} />
22
+ * <Field.Description>We only use this for policy documents.</Field.Description>
23
23
  * </FieldText>
24
24
  */
25
25
  export declare const FieldText: {
@@ -55,9 +55,9 @@ export type FormTextProps<TFieldValues extends FieldValues = FieldValues, TName
55
55
  /** The caption, rendered as the field's label. Text in almost every case. */
56
56
  label: ReactNode;
57
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.
58
+ * Helper text shown under the control, below the error when there is one,
59
+ * and announced through its `aria-describedby`. Use it for guidance the
60
+ * user needs before typing; put details worth a click in `tooltip` instead.
61
61
  */
62
62
  description?: ReactNode;
63
63
  /**
@@ -96,7 +96,7 @@ export type FormTextProps<TFieldValues extends FieldValues = FieldValues, TName
96
96
  };
97
97
  /**
98
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
99
+ * validation error and helper text, laid out in the standard order and wired
100
100
  * through `useController`. It renders inside a `Form.Root`, which provides the
101
101
  * form, and needs only a `name`, which on a typed form must point at a string
102
102
  * (`FormTextPath`). The `required`, `invalid` and error states
@@ -30,8 +30,8 @@ var s = ({ name: s, control: c, rules: l, shouldUnregister: u, disabled: d, labe
30
30
  children: f
31
31
  }), /* @__PURE__ */ a(t.Content, { children: [
32
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 })
33
+ /* @__PURE__ */ i(t.Error, { errors: b.error ? [b.error] : void 0 }),
34
+ p && /* @__PURE__ */ i(t.Description, { children: p })
35
35
  ] })]
36
36
  });
37
37
  };
@@ -0,0 +1,16 @@
1
+ import { SVGProps } from 'react';
2
+ /**
3
+ * Open eye on the InputPassword toggle while the value is hidden. Fill color
4
+ * comes from the input group addon, so the icon follows the field's state.
5
+ *
6
+ * @summary 24px open-eye icon on the show-password toggle
7
+ */
8
+ export declare const IconVisibility: (props: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
9
+ /**
10
+ * Struck-through eye on the InputPassword toggle while the value is shown.
11
+ * Fill color comes from the input group addon, so the icon follows the
12
+ * field's state.
13
+ *
14
+ * @summary 24px crossed-eye icon on the hide-password toggle
15
+ */
16
+ export declare const IconVisibilityOff: (props: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { jsx as e } from "react/jsx-runtime";
2
+ //#region src/input-password/icons.tsx
3
+ var t = (t) => /* @__PURE__ */ e("svg", {
4
+ width: "24",
5
+ height: "24",
6
+ viewBox: "0 0 24 24",
7
+ fill: "none",
8
+ "aria-hidden": "true",
9
+ xmlns: "http://www.w3.org/2000/svg",
10
+ ...t,
11
+ children: /* @__PURE__ */ e("path", { d: "M12.0023 15.5769C13.1354 15.5769 14.0978 15.1803 14.8895 14.3871C15.6811 13.5939 16.077 12.6308 16.077 11.4977C16.077 10.3646 15.6804 9.40224 14.8872 8.61058C14.094 7.81891 13.1309 7.42308 11.9978 7.42308C10.8647 7.42308 9.90233 7.81966 9.11066 8.61282C8.31899 9.40601 7.92316 10.3692 7.92316 11.5023C7.92316 12.6353 8.31974 13.5977 9.11291 14.3894C9.90609 15.181 10.8692 15.5769 12.0023 15.5769ZM12.0001 14.2C11.2501 14.2 10.6126 13.9375 10.0876 13.4125C9.56256 12.8875 9.30006 12.25 9.30006 11.5C9.30006 10.75 9.56256 10.1125 10.0876 9.58748C10.6126 9.06248 11.2501 8.79998 12.0001 8.79998C12.7501 8.79998 13.3876 9.06248 13.9126 9.58748C14.4376 10.1125 14.7001 10.75 14.7001 11.5C14.7001 12.25 14.4376 12.8875 13.9126 13.4125C13.3876 13.9375 12.7501 14.2 12.0001 14.2ZM12.0001 18.5C9.81161 18.5 7.80553 17.9243 5.98181 16.773C4.15811 15.6217 2.76741 14.0653 1.80971 12.1038C1.76613 12.0269 1.736 11.9353 1.71934 11.8292C1.70267 11.723 1.69434 11.6132 1.69434 11.5C1.69434 11.3867 1.70267 11.277 1.71934 11.1708C1.736 11.0646 1.76613 10.9731 1.80971 10.8961C2.76741 8.93461 4.15811 7.37821 5.98181 6.22693C7.80553 5.07564 9.81161 4.5 12.0001 4.5C14.1885 4.5 16.1946 5.07564 18.0183 6.22693C19.842 7.37821 21.2327 8.93461 22.1904 10.8961C22.234 10.9731 22.2641 11.0646 22.2808 11.1708C22.2975 11.277 22.3058 11.3867 22.3058 11.5C22.3058 11.6132 22.2975 11.723 22.2808 11.8292C22.2641 11.9353 22.234 12.0269 22.1904 12.1038C21.2327 14.0653 19.842 15.6217 18.0183 16.773C16.1946 17.9243 14.1885 18.5 12.0001 18.5ZM12.0001 17C13.8834 17 15.6126 16.5041 17.1876 15.5125C18.7626 14.5208 19.9667 13.1833 20.8001 11.5C19.9667 9.81664 18.7626 8.47914 17.1876 7.48748C15.6126 6.49581 13.8834 5.99998 12.0001 5.99998C10.1167 5.99998 8.38756 6.49581 6.81256 7.48748C5.23756 8.47914 4.03339 9.81664 3.20006 11.5C4.03339 13.1833 5.23756 14.5208 6.81256 15.5125C8.38756 16.5041 10.1167 17 12.0001 17Z" })
12
+ }), n = (t) => /* @__PURE__ */ e("svg", {
13
+ width: "24",
14
+ height: "24",
15
+ viewBox: "0 0 24 24",
16
+ fill: "none",
17
+ "aria-hidden": "true",
18
+ xmlns: "http://www.w3.org/2000/svg",
19
+ ...t,
20
+ children: /* @__PURE__ */ e("path", { d: "M15.7731 12.9731L14.6501 11.85C14.8001 11.0218 14.5638 10.2772 13.9414 9.61636C13.319 8.95546 12.5552 8.70001 11.6501 8.85001L10.527 7.72696C10.7526 7.62568 10.9841 7.54972 11.2212 7.49909C11.4584 7.44844 11.718 7.42311 12.0001 7.42311C13.1347 7.42311 14.0978 7.81895 14.8895 8.61061C15.6811 9.40228 16.077 10.3654 16.077 11.5C16.077 11.7821 16.0516 12.0449 16.001 12.2885C15.9504 12.532 15.8744 12.7602 15.7731 12.9731ZM18.9539 16.0846L17.8501 15.05C18.4834 14.5667 19.0459 14.0375 19.5376 13.4625C20.0292 12.8875 20.4501 12.2333 20.8001 11.5C19.9667 9.81668 18.7709 8.47918 17.2126 7.48751C15.6542 6.49585 13.9167 6.00001 12.0001 6.00001C11.5167 6.00001 11.0417 6.03335 10.5751 6.10001C10.1084 6.16668 9.65006 6.26668 9.20006 6.40001L8.03471 5.23466C8.66676 4.98338 9.31227 4.79812 9.97124 4.67889C10.6302 4.55965 11.3065 4.50004 12.0001 4.50004C14.2231 4.50004 16.2462 5.08914 18.0693 6.26734C19.8923 7.44554 21.266 8.98848 22.1904 10.8962C22.234 10.9731 22.2641 11.0644 22.2808 11.1702C22.2975 11.276 22.3058 11.3859 22.3058 11.5C22.3058 11.6141 22.2949 11.724 22.2731 11.8298C22.2513 11.9356 22.2237 12.0269 22.1904 12.1039C21.8199 12.8833 21.3632 13.6074 20.8202 14.276C20.2773 14.9445 19.6552 15.5474 18.9539 16.0846ZM19.2347 21.3423L15.7155 17.8538C15.2026 18.0436 14.6344 18.1987 14.0107 18.3192C13.3869 18.4397 12.7167 18.5 12.0001 18.5C9.76418 18.5 7.7395 17.9109 5.92604 16.7327C4.11259 15.5545 2.74048 14.0115 1.80971 12.1039C1.76613 12.0269 1.736 11.9356 1.71934 11.8298C1.70267 11.724 1.69434 11.6141 1.69434 11.5C1.69434 11.3859 1.70267 11.2785 1.71934 11.1779C1.736 11.0773 1.76613 10.9885 1.80971 10.9116C2.18536 10.1423 2.64049 9.43143 3.17511 8.77886C3.70973 8.1263 4.28473 7.54617 4.90011 7.03849L2.64241 4.76539C2.50394 4.62692 2.43311 4.45545 2.42991 4.25099C2.42671 4.0465 2.50267 3.86669 2.65779 3.71156C2.80265 3.56669 2.97829 3.49426 3.18471 3.49426C3.39111 3.49426 3.56674 3.56669 3.71161 3.71156L20.2885 20.2885C20.427 20.4269 20.5004 20.5984 20.5087 20.8029C20.517 21.0073 20.4436 21.1872 20.2885 21.3423C20.1436 21.4872 19.968 21.5596 19.7616 21.5596C19.5552 21.5596 19.3796 21.4872 19.2347 21.3423ZM5.95394 8.09229C5.42574 8.5128 4.91195 9.01826 4.41259 9.60866C3.91322 10.199 3.50904 10.8295 3.20006 11.5C4.03339 13.1833 5.22923 14.5208 6.78756 15.5125C8.34589 16.5042 10.0834 17 12.0001 17C12.4552 17 12.9084 16.9615 13.3597 16.8846C13.811 16.8077 14.1937 16.7282 14.5078 16.6462L13.2424 15.35C13.0719 15.4192 12.8744 15.4743 12.6501 15.5154C12.4257 15.5564 12.209 15.5769 12.0001 15.5769C10.8655 15.5769 9.90233 15.1811 9.11066 14.3894C8.31899 13.5977 7.92316 12.6346 7.92316 11.5C7.92316 11.2974 7.94368 11.0856 7.98471 10.8644C8.02574 10.6433 8.08087 10.441 8.15009 10.2577L5.95394 8.09229Z" })
21
+ });
22
+ //#endregion
23
+ export { t as IconVisibility, n as IconVisibilityOff };
@@ -0,0 +1,2 @@
1
+ export { InputPassword, inputPasswordRecipe } from './input-password';
2
+ export type { InputPasswordProps } from './input-password';
@@ -0,0 +1,2 @@
1
+ import { InputPassword as e, inputPasswordRecipe as t } from "./input-password.js";
2
+ export { e as InputPassword, t as inputPasswordRecipe };
@@ -0,0 +1,103 @@
1
+ import { InputProps } from '../input';
2
+ /**
3
+ * Style recipe for InputPassword. Slots: `toggle` (the addon holding the
4
+ * button) and `button`. The `size` variant follows the input's: the button is
5
+ * 24px at `sm` and 32px above, the icon 16, 20 or 24px, and a negative end
6
+ * margin pulls the addon into the frame's padding so the button sits as far
7
+ * from the right edge as from the top and bottom (4px, 8px at `lg`).
8
+ *
9
+ * @summary tailwind-variants recipe backing the InputPassword toggle
10
+ */
11
+ export declare const inputPasswordRecipe: import('tailwind-variants').TVReturnType<{
12
+ size: {
13
+ sm: {
14
+ toggle: string;
15
+ button: string;
16
+ };
17
+ md: {
18
+ toggle: string;
19
+ button: string;
20
+ };
21
+ lg: {
22
+ toggle: string;
23
+ button: string;
24
+ };
25
+ };
26
+ }, {
27
+ toggle: string;
28
+ button: string;
29
+ }, undefined, {
30
+ size: {
31
+ sm: {
32
+ toggle: string;
33
+ button: string;
34
+ };
35
+ md: {
36
+ toggle: string;
37
+ button: string;
38
+ };
39
+ lg: {
40
+ toggle: string;
41
+ button: string;
42
+ };
43
+ };
44
+ }, {
45
+ toggle: string;
46
+ button: string;
47
+ }, import('tailwind-variants').TVReturnType<{
48
+ size: {
49
+ sm: {
50
+ toggle: string;
51
+ button: string;
52
+ };
53
+ md: {
54
+ toggle: string;
55
+ button: string;
56
+ };
57
+ lg: {
58
+ toggle: string;
59
+ button: string;
60
+ };
61
+ };
62
+ }, {
63
+ toggle: string;
64
+ button: string;
65
+ }, undefined, unknown, unknown, undefined>>;
66
+ export type InputPasswordProps = Omit<InputProps, 'type'>;
67
+ /**
68
+ * InputPassword is a password field with a show/hide toggle at the end of its
69
+ * frame. The value starts hidden; the toggle reveals it as plain text and
70
+ * hides it again. Its accessible name reads "Show password" or "Hide
71
+ * password", depending on what the next press does. Pressing the toggle keeps
72
+ * focus and the caret in the input. The toggle is not rendered while the input
73
+ * is disabled, whether `disabled` comes from the prop, from a surrounding
74
+ * `FieldText` or from a `<fieldset disabled>`.
75
+ *
76
+ * It is an [Input](?path=/docs/components-input--docs) inside an
77
+ * [InputGroup](?path=/docs/components-inputgroup--docs) frame. `className`
78
+ * styles the frame. Every other prop goes to the `<input>`: the `ref`, `id`,
79
+ * `name`, `value`, `onChange`, `aria-*` and every other standard attribute,
80
+ * such as `autoComplete`, `required` or `maxLength`. Only `type` is not
81
+ * accepted, because the toggle owns it. Inside a `FieldText` the input takes
82
+ * its label, messages and states from the field as a plain `Input` does. The
83
+ * invalid state comes from `aria-invalid`, and the frame paints the
84
+ * destructive border. Set `autoComplete` to `current-password` on sign-in and
85
+ * `new-password` when the user picks one, so password managers fill the right
86
+ * value.
87
+ *
88
+ * Do not use it for text that is not secret; use `Input` instead. A one-time
89
+ * code is an `Input` with `autoComplete="one-time-code"`, not a password.
90
+ *
91
+ * @summary Password input with a show/hide toggle inside the frame
92
+ * @dataAttribute {string} data-slot - Always set to "input-password" on the frame
93
+ *
94
+ * @example
95
+ * <FieldText required>
96
+ * <Label>Password</Label>
97
+ * <InputPassword name="password" autoComplete="current-password" />
98
+ * </FieldText>
99
+ */
100
+ export declare const InputPassword: {
101
+ ({ className, size, ...props }: InputPasswordProps): import("react/jsx-runtime").JSX.Element;
102
+ displayName: string;
103
+ };