@igne-agency/react-web-ui 0.2.1 → 0.3.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.
Files changed (59) hide show
  1. package/README.md +39 -0
  2. package/dist/components/checkbox-field/tsf-checkbox-field.js +1 -1
  3. package/dist/components/checkbox-group-field/checkbox-group.d.ts +1 -1
  4. package/dist/components/checkbox-group-field/checkbox-group.js +1 -1
  5. package/dist/components/checkbox-group-field/tsf-checkbox-group-field.js +1 -1
  6. package/dist/components/combobox-field/tsf-combobox-field.js +2 -2
  7. package/dist/components/date-field/tsf-date-field.js +1 -1
  8. package/dist/components/date-range-field/tsf-date-range-field.js +1 -1
  9. package/dist/components/field/field-group-context.d.ts +21 -0
  10. package/dist/components/field/field-group-context.js +38 -0
  11. package/dist/components/field/field.css +27 -8
  12. package/dist/components/field/field.d.ts +6 -3
  13. package/dist/components/field/field.js +88 -45
  14. package/dist/components/field/field.module.js +12 -10
  15. package/dist/components/fieldset/field-group.d.ts +31 -0
  16. package/dist/components/fieldset/field-group.js +102 -0
  17. package/dist/components/fieldset/fieldset-root.d.ts +60 -0
  18. package/dist/components/fieldset/fieldset-root.js +285 -0
  19. package/dist/components/fieldset/fieldset.css +22 -0
  20. package/dist/components/fieldset/fieldset.d.ts +37 -0
  21. package/dist/components/fieldset/fieldset.js +127 -0
  22. package/dist/components/fieldset/fieldset.module.js +8 -0
  23. package/dist/components/fieldset/index.d.ts +4 -0
  24. package/dist/components/fieldset/index.js +4 -0
  25. package/dist/components/fieldset/tsf-field-group.d.ts +21 -0
  26. package/dist/components/fieldset/tsf-field-group.js +58 -0
  27. package/dist/components/file-field/tsf-file-field.js +1 -1
  28. package/dist/components/number-field/tsf-number-field.js +1 -1
  29. package/dist/components/otp-field/tsf-otp-field.js +1 -1
  30. package/dist/components/password-field/tsf-password-field.js +1 -1
  31. package/dist/components/photo-field/tsf-photo-field.js +1 -1
  32. package/dist/components/radio-group-field/radio-group.d.ts +1 -1
  33. package/dist/components/radio-group-field/radio-group.js +1 -1
  34. package/dist/components/radio-group-field/tsf-radio-group-field.js +1 -1
  35. package/dist/components/segmented-control-field/segmented-control.d.ts +1 -1
  36. package/dist/components/segmented-control-field/segmented-control.js +1 -1
  37. package/dist/components/segmented-control-field/tsf-segmented-control-field.js +1 -1
  38. package/dist/components/select-field/tsf-select-field.js +1 -1
  39. package/dist/components/switch-field/tsf-switch-field.js +1 -1
  40. package/dist/components/text-field/connect-text-preset.js +1 -1
  41. package/dist/components/text-field/text-field.css +28 -27
  42. package/dist/components/text-field/text-field.module.js +9 -9
  43. package/dist/components/text-field/tsf-text-field.js +1 -1
  44. package/dist/components/textarea-field/tsf-textarea-field.js +1 -1
  45. package/dist/components/time-field/tsf-time-field.js +1 -1
  46. package/dist/components/toggle-group-field/toggle-group.d.ts +1 -1
  47. package/dist/components/toggle-group-field/toggle-group.js +1 -1
  48. package/dist/components/toggle-group-field/tsf-toggle-group-field.js +1 -1
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.js +6 -2
  51. package/dist/lib/forms/create-app-form.d.ts +18 -9
  52. package/dist/lib/forms/create-app-form.js +17 -8
  53. package/dist/lib/forms/index.d.ts +1 -0
  54. package/dist/lib/forms/index.js +2 -1
  55. package/dist/lib/forms/use-connected-field.d.ts +10 -7
  56. package/dist/lib/forms/use-connected-field.js +54 -28
  57. package/package.json +5 -1
  58. package/dist/components/field/fieldset.d.ts +0 -22
  59. package/dist/components/field/fieldset.js +0 -130
package/README.md CHANGED
@@ -184,6 +184,45 @@ const SignUp = () => {
184
184
  };
185
185
  ```
186
186
 
187
+ Related fields, such as the parts of an address, share one legend and one error in a field group, which lines them up side by side. Register `TSFFieldGroup` as a form component and name the fields whose errors it shows:
188
+
189
+ ```ts
190
+ import { TSFFieldGroup } from "@igne-agency/react-web-ui/fieldset";
191
+ import { TSFTextField } from "@igne-agency/react-web-ui/text-field";
192
+
193
+ export const { useAppForm, withForm, withFieldGroup } =
194
+ createAppForm(
195
+ { TextField: TSFTextField },
196
+ { FieldGroup: TSFFieldGroup },
197
+ );
198
+ ```
199
+
200
+ ```tsx
201
+ <form.FieldGroup
202
+ legend="Address"
203
+ optionalLabel="optional"
204
+ names={["postalCode", "houseNumber"]}>
205
+ <form.AppField name="postalCode">
206
+ {field => (
207
+ <field.TextField
208
+ label="Postal code"
209
+ requiredness="required"
210
+ optionalLabel="optional"
211
+ />
212
+ )}
213
+ </form.AppField>
214
+ <form.AppField name="houseNumber">
215
+ {field => (
216
+ <field.TextField
217
+ label="House number"
218
+ requiredness="required"
219
+ optionalLabel="optional"
220
+ />
221
+ )}
222
+ </form.AppField>
223
+ </form.FieldGroup>
224
+ ```
225
+
187
226
  Every field takes `requiredness` (`"required"`, `"optional"`, or left out to show neither mark) and `errors`, which accepts strings and anything with a string `message`, such as Standard Schema issues. Validation is the app's own, through Standard Schema; the kit ships no validators.
188
227
 
189
228
  `Form.Root` reports unsaved changes to the page's leave guards (`useLeaveGuard`, or `useRouterLeaveGuard` from `./unsaved-changes/tanstack-router`) while its `leaveGuard` policy returns true for the form's live state. The default, `whenDirty`, guards while the values differ from their defaults and no submit is in flight. Pass `never` for a form whose edits need not survive leaving, or any `(state) => boolean`:
@@ -14,7 +14,7 @@ import { jsx } from "react/jsx-runtime";
14
14
  */
15
15
  var TSFCheckboxField = (props) => {
16
16
  const $ = c(7);
17
- const { field, props: connected } = useConnectedField();
17
+ const { field, props: connected } = useConnectedField(props.label);
18
18
  let t0;
19
19
  if ($[0] !== field) {
20
20
  t0 = (checked) => field.handleChange(checked);
@@ -3,7 +3,7 @@ import { Field as BaseField } from "@base-ui/react/field";
3
3
  import type { ReactNode } from "react";
4
4
  import { Checkbox } from "../checkbox-field/index.ts";
5
5
  import { Field } from "../field/index.ts";
6
- import { FieldsetLegend, FieldsetRoot, type FieldsetRootProps } from "../field/fieldset.tsx";
6
+ import { FieldsetLegend, FieldsetRoot, type FieldsetRootProps } from "../fieldset/fieldset-root.tsx";
7
7
  export type CheckboxGroupRootProps = FieldsetRootProps;
8
8
  declare const CheckboxGroupGroup: ({ className, ...props }: BaseCheckboxGroup.Props) => ReactNode;
9
9
  declare const CheckboxGroupItem: ({ className, ...props }: BaseField.Item.Props) => ReactNode;
@@ -1,6 +1,6 @@
1
1
  import { Checkbox } from "../checkbox-field/checkbox.js";
2
2
  import { Field as Field$1 } from "../field/field.js";
3
- import { FieldsetLegend, FieldsetRoot } from "../field/fieldset.js";
3
+ import { FieldsetLegend, FieldsetRoot } from "../fieldset/fieldset-root.js";
4
4
  import checkbox_group_module_default from "./checkbox-group.module.js";
5
5
  import { c } from "react/compiler-runtime";
6
6
  import { jsx } from "react/jsx-runtime";
@@ -15,7 +15,7 @@ import { jsx } from "react/jsx-runtime";
15
15
  */
16
16
  var TSFCheckboxGroupField = (props) => {
17
17
  const $ = c(7);
18
- const { field, props: connected } = useConnectedField();
18
+ const { field, props: connected } = useConnectedField(props.label);
19
19
  let t0;
20
20
  if ($[0] !== field) {
21
21
  t0 = (value) => field.handleChange(value);
@@ -6,7 +6,7 @@ import { jsx } from "react/jsx-runtime";
6
6
  var SingleField = (t0) => {
7
7
  const $ = c(7);
8
8
  const { props } = t0;
9
- const { field, props: connected } = useConnectedField();
9
+ const { field, props: connected } = useConnectedField(props.label);
10
10
  let t1;
11
11
  if ($[0] !== field) {
12
12
  t1 = (next) => field.handleChange(next);
@@ -32,7 +32,7 @@ var SingleField = (t0) => {
32
32
  var MultipleField = (t0) => {
33
33
  const $ = c(7);
34
34
  const { props } = t0;
35
- const { field, props: connected } = useConnectedField();
35
+ const { field, props: connected } = useConnectedField(props.label);
36
36
  let t1;
37
37
  if ($[0] !== field) {
38
38
  t1 = (next) => field.handleChange(next);
@@ -14,7 +14,7 @@ import { jsx } from "react/jsx-runtime";
14
14
  */
15
15
  var TSFDateField = (props) => {
16
16
  const $ = c(7);
17
- const { field, props: connected } = useConnectedField();
17
+ const { field, props: connected } = useConnectedField(props.label);
18
18
  let t0;
19
19
  if ($[0] !== field) {
20
20
  t0 = (next) => field.handleChange(next);
@@ -15,7 +15,7 @@ import { jsx } from "react/jsx-runtime";
15
15
  */
16
16
  var TSFDateRangeField = (props) => {
17
17
  const $ = c(12);
18
- const { field, props: t0 } = useConnectedField();
18
+ const { field, props: t0 } = useConnectedField(props.label);
19
19
  let connected;
20
20
  let name;
21
21
  if ($[0] !== t0) {
@@ -0,0 +1,21 @@
1
+ import { type Context } from "react";
2
+ /** What a field group tells the fields it collects the errors of. */
3
+ export type FieldGroupContextValue = Readonly<{
4
+ /** The ids of the lines a field's errors show as in the group's error, or `undefined` for a field the group does not collect. */
5
+ lineIds: (name: string) => readonly string[] | undefined;
6
+ /** Tells the group a field's label, until the returned function takes it back. */
7
+ registerLabel: (name: string, label: string) => () => void;
8
+ }>;
9
+ /** The field group the fields inside it report to. */
10
+ export declare const FieldGroupContext: Context<FieldGroupContextValue | null>;
11
+ /** Whether a field root sits directly in a field group's row of fields. */
12
+ export declare const FieldRowContext: Context<boolean>;
13
+ /**
14
+ * The ids of the lines the enclosing field group shows this field's errors as,
15
+ * or `undefined` when no group collects it. A field is matched by the name of
16
+ * the TanStack Form field it renders in.
17
+ *
18
+ * @example
19
+ * const lineIds = useFieldGroupLineIds(); // ["«r3»line0"]
20
+ */
21
+ export declare const useFieldGroupLineIds: () => readonly string[] | undefined;
@@ -0,0 +1,38 @@
1
+ import { fieldContext } from "../../lib/forms/context.js";
2
+ import { c } from "react/compiler-runtime";
3
+ import { createContext, useContext } from "react";
4
+ // src/components/field/field-group-context.ts
5
+ /** The field group the fields inside it report to. */
6
+ var FieldGroupContext = createContext(null);
7
+ /** Whether a field root sits directly in a field group's row of fields. */
8
+ var FieldRowContext = createContext(false);
9
+ /**
10
+ * The ids of the lines `group` shows the errors of `field` as, where there are
11
+ * both.
12
+ *
13
+ * @example
14
+ * groupLineIds(group, field); // ["«r3»line0"]
15
+ */
16
+ var groupLineIds = (group, field) => group === null || field === null ? void 0 : group.lineIds(field.name);
17
+ /**
18
+ * The ids of the lines the enclosing field group shows this field's errors as,
19
+ * or `undefined` when no group collects it. A field is matched by the name of
20
+ * the TanStack Form field it renders in.
21
+ *
22
+ * @example
23
+ * const lineIds = useFieldGroupLineIds(); // ["«r3»line0"]
24
+ */
25
+ var useFieldGroupLineIds = () => {
26
+ const $ = c(3);
27
+ const t0 = useContext(FieldGroupContext);
28
+ const t1 = useContext(fieldContext);
29
+ let t2;
30
+ if ($[0] !== t0 || $[1] !== t1) {
31
+ t2 = groupLineIds(t0, t1);
32
+ $[0] = t0;
33
+ $[1] = t1;
34
+ $[2] = t2;
35
+ } else t2 = $[2];
36
+ return t2;
37
+ };
38
+ export { FieldGroupContext, FieldRowContext, useFieldGroupLineIds };
@@ -1,29 +1,48 @@
1
1
  @layer kit.reset, kit.base, kit.components;
2
2
  @layer kit.components {
3
- ._field_14ydh_3 {
3
+ ._field_dzutr_3 {
4
4
  display: flex;
5
5
  flex-direction: column;
6
6
  gap: var(--field-gap, var(--sys-unit-4));
7
7
  inline-size: 100%;
8
8
  }
9
- ._label_14ydh_9 {
9
+ ._field_dzutr_3[data-field-group-member] > * {
10
+ grid-row: extra;
11
+ margin-block-start: var(--field-gap, var(--sys-unit-4));
12
+ }
13
+ ._field_dzutr_3[data-field-group-member] > ._label_dzutr_13 {
14
+ grid-row: label;
15
+ margin-block: 0 var(--field-gap, var(--sys-unit-4));
16
+ }
17
+ ._field_dzutr_3[data-field-group-member] > :first-child:not(._label_dzutr_13),
18
+ ._field_dzutr_3[data-field-group-member] > ._label_dzutr_13 + * {
19
+ grid-row: control;
20
+ margin-block-start: 0;
21
+ }
22
+ ._field_dzutr_3[data-field-group-member] > ._description_dzutr_22 {
23
+ grid-row: description;
24
+ }
25
+ ._field_dzutr_3[data-field-group-member] > ._error_dzutr_25 {
26
+ grid-row: error;
27
+ }
28
+ ._label_dzutr_13 {
10
29
  color: var(--sys-color-text);
11
30
  font: var(--sys-font-label-medium);
12
31
  cursor: default;
13
32
  }
14
- ._optional_14ydh_14 {
33
+ ._optional_dzutr_33 {
15
34
  vertical-align: middle;
16
35
  color: var(--sys-color-text-muted);
17
36
  font: var(--sys-font-body-small);
18
37
  font-size: 0.75rem;
19
38
  }
20
- ._inlineText_14ydh_20 {
39
+ ._inlineText_dzutr_39 {
21
40
  grid-area: text;
22
41
  }
23
- ._inlineOptional_14ydh_23 {
42
+ ._inlineOptional_dzutr_42 {
24
43
  grid-area: optional;
25
44
  }
26
- ._required_14ydh_26 {
45
+ ._required_dzutr_45 {
27
46
  display: inline-block;
28
47
  vertical-align: middle;
29
48
  translate: 0 -1px;
@@ -32,11 +51,11 @@
32
51
  background-color: var(--field-required-fg, var(--sys-color-primary));
33
52
  mask: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='16%2016%2032%2032'%20fill='currentColor'%3e%3cpath%20d='M45.475%2036.291c.377.162.549.477.523.943-.027.469-.35%201.223-.967%202.264-.617%201.072-1.121%201.725-1.51%201.967-.389.24-.744.24-1.066%200l-8.574-6.215%201.129%2010.508c.078.398-.096.707-.525.92-.428.215-1.262.322-2.495.322-1.236%200-2.066-.107-2.496-.322-.43-.213-.604-.521-.523-.92l1.127-10.508-8.533%206.215c-.322.24-.686.24-1.088%200-.402-.242-.912-.895-1.529-1.967-.617-1.041-.934-1.795-.945-2.264-.016-.467.168-.781.543-.943l9.701-4.29-9.701-4.252c-.375-.188-.559-.516-.543-.984.012-.467.328-1.223.945-2.264.617-1.045%201.127-1.686%201.529-1.926s.766-.24%201.088%200l8.533%206.176-1.127-10.467c-.08-.428.094-.748.523-.963s1.26-.321%202.496-.321c1.232%200%202.066.107%202.494.322.43.215.604.535.525.963l-1.129%2010.467%208.574-6.176c.322-.24.678-.24%201.066%200s.893.896%201.51%201.965c.617%201.018.939%201.758.967%202.225.025.469-.146.797-.523.984l-9.701%204.252%209.702%204.289'/%3e%3c/svg%3e") center/contain no-repeat;
34
53
  }
35
- ._description_14ydh_35 {
54
+ ._description_dzutr_22 {
36
55
  color: var(--field-description-fg, var(--sys-color-text-muted));
37
56
  font: var(--field-description-font, var(--sys-font-body-medium));
38
57
  }
39
- ._pending_14ydh_39 {
58
+ ._pending_dzutr_58 {
40
59
  display: inline-flex;
41
60
  flex: none;
42
61
  align-items: center;
@@ -72,11 +72,12 @@ export type FieldRootProps = BaseField.Root.Props & Readonly<{
72
72
  }>;
73
73
  /**
74
74
  * The field frame: label, control, description and error stacked full width.
75
+ * In a field group's row its parts share the row's tracks with its neighbours.
75
76
  *
76
77
  * @example
77
78
  * <Field.Root name="email" invalid={invalid}>…</Field.Root>
78
79
  */
79
- declare const FieldRoot: ({ className, ...props }: FieldRootProps) => ReactNode;
80
+ declare const FieldRoot: ({ className, children, ...props }: FieldRootProps) => ReactNode;
80
81
  export type FieldLabelProps = Omit<BaseField.Label.Props, "className"> & RequirednessProps & Readonly<{
81
82
  className?: string;
82
83
  }>;
@@ -113,7 +114,9 @@ export type FieldErrorProps = Omit<BaseField.Error.Props, "match" | "render" | "
113
114
  * The one error part every control renders, after the description. Normalises
114
115
  * its errors to lines with `normalizeFieldErrors` and renders nothing when
115
116
  * there are none. It is the field's only live region, so a control must not
116
- * announce the same message a second time.
117
+ * announce the same message a second time. In a field group that collects
118
+ * the field, the group's error shows the lines instead, and the control is
119
+ * described by them.
117
120
  *
118
121
  * @example
119
122
  * <Field.Error>{errors}</Field.Error>
@@ -139,7 +142,7 @@ export type FieldIds = Readonly<{
139
142
  errorId: string;
140
143
  /** A stable id and name for the control. */
141
144
  controlId: string;
142
- /** The control's `aria-describedby`: the description and the error that are shown. */
145
+ /** The control's `aria-describedby`: the description and the error that are shown, or the field's lines in a field group that collects it. */
143
146
  describedBy: (hasDescription: boolean, invalid: boolean) => string | undefined;
144
147
  }>;
145
148
  /**
@@ -1,9 +1,10 @@
1
1
  import { normalizeFieldErrors } from "../../lib/forms/errors.js";
2
2
  import { ErrorText } from "../error-text/error-text.js";
3
3
  import { Spinner } from "../spinner/spinner.js";
4
+ import { FieldRowContext, useFieldGroupLineIds } from "./field-group-context.js";
4
5
  import field_module_default from "./field.module.js";
5
6
  import { c } from "react/compiler-runtime";
6
- import { useId } from "react";
7
+ import { useContext, useId } from "react";
7
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
9
  import { clsx } from "clsx";
9
10
  import { Field } from "@base-ui/react/field";
@@ -104,40 +105,59 @@ var InlineLabelText = (t0) => {
104
105
  };
105
106
  /**
106
107
  * The field frame: label, control, description and error stacked full width.
108
+ * In a field group's row its parts share the row's tracks with its neighbours.
107
109
  *
108
110
  * @example
109
111
  * <Field.Root name="email" invalid={invalid}>…</Field.Root>
110
112
  */
111
113
  var FieldRoot = (t0) => {
112
- const $ = c(8);
114
+ const $ = c(13);
115
+ let children;
113
116
  let className;
114
117
  let props;
115
118
  if ($[0] !== t0) {
116
- ({className, ...props} = t0);
119
+ ({className, children, ...props} = t0);
117
120
  $[0] = t0;
118
- $[1] = className;
119
- $[2] = props;
121
+ $[1] = children;
122
+ $[2] = className;
123
+ $[3] = props;
120
124
  } else {
121
- className = $[1];
122
- props = $[2];
125
+ children = $[1];
126
+ className = $[2];
127
+ props = $[3];
123
128
  }
129
+ const inRow = useContext(FieldRowContext);
124
130
  let t1;
125
- if ($[3] !== className) {
131
+ if ($[4] !== className) {
126
132
  t1 = clsx(field_module_default.field, className);
127
- $[3] = className;
128
- $[4] = t1;
129
- } else t1 = $[4];
130
- let t2;
131
- if ($[5] !== props || $[6] !== t1) {
132
- t2 = /* @__PURE__ */ jsx(Field.Root, {
133
+ $[4] = className;
134
+ $[5] = t1;
135
+ } else t1 = $[5];
136
+ const t2 = inRow ? "" : void 0;
137
+ let t3;
138
+ if ($[6] !== children) {
139
+ t3 = /* @__PURE__ */ jsx(FieldRowContext, {
140
+ value: false,
141
+ children
142
+ });
143
+ $[6] = children;
144
+ $[7] = t3;
145
+ } else t3 = $[7];
146
+ let t4;
147
+ if ($[8] !== props || $[9] !== t1 || $[10] !== t2 || $[11] !== t3) {
148
+ t4 = /* @__PURE__ */ jsx(Field.Root, {
133
149
  className: t1,
134
- ...props
150
+ "data-field-group-member": t2,
151
+ ...props,
152
+ children: t3
135
153
  });
136
- $[5] = props;
137
- $[6] = t1;
138
- $[7] = t2;
139
- } else t2 = $[7];
140
- return t2;
154
+ $[8] = props;
155
+ $[9] = t1;
156
+ $[10] = t2;
157
+ $[11] = t3;
158
+ $[12] = t4;
159
+ } else t4 = $[12];
160
+ return t4;
141
161
  };
142
162
  /**
143
163
  * The label above a control, trailing its requiredness. Renders nothing without
@@ -280,17 +300,20 @@ var FieldDescription = (t0) => {
280
300
  } else t1 = $[7];
281
301
  return t1;
282
302
  };
303
+ var Unrendered = () => null;
283
304
  /**
284
305
  * The one error part every control renders, after the description. Normalises
285
306
  * its errors to lines with `normalizeFieldErrors` and renders nothing when
286
307
  * there are none. It is the field's only live region, so a control must not
287
- * announce the same message a second time.
308
+ * announce the same message a second time. In a field group that collects
309
+ * the field, the group's error shows the lines instead, and the control is
310
+ * described by them.
288
311
  *
289
312
  * @example
290
313
  * <Field.Error>{errors}</Field.Error>
291
314
  */
292
315
  var FieldError = (t0) => {
293
- const $ = c(8);
316
+ const $ = c(10);
294
317
  let errors;
295
318
  let props;
296
319
  if ($[0] !== t0) {
@@ -302,15 +325,25 @@ var FieldError = (t0) => {
302
325
  errors = $[1];
303
326
  props = $[2];
304
327
  }
328
+ const groupLineIds = useFieldGroupLineIds();
329
+ if (groupLineIds !== void 0) {
330
+ let t1;
331
+ if ($[3] !== groupLineIds) {
332
+ t1 = groupLineIds.map(_temp);
333
+ $[3] = groupLineIds;
334
+ $[4] = t1;
335
+ } else t1 = $[4];
336
+ return t1;
337
+ }
305
338
  let t1;
306
- if ($[3] !== errors) {
339
+ if ($[5] !== errors) {
307
340
  t1 = normalizeFieldErrors(errors);
308
- $[3] = errors;
309
- $[4] = t1;
310
- } else t1 = $[4];
341
+ $[5] = errors;
342
+ $[6] = t1;
343
+ } else t1 = $[6];
311
344
  const messages = t1;
312
345
  let t2;
313
- if ($[5] !== messages || $[6] !== props) {
346
+ if ($[7] !== messages || $[8] !== props) {
314
347
  t2 = messages.length === 0 ? null : /* @__PURE__ */ jsx(Field.Error, {
315
348
  match: true,
316
349
  ...props,
@@ -318,13 +351,14 @@ var FieldError = (t0) => {
318
351
  el: "span",
319
352
  role: "alert",
320
353
  ...renderProps,
354
+ className: clsx(field_module_default.error, renderProps.className),
321
355
  children: messages
322
356
  })
323
357
  });
324
- $[5] = messages;
325
- $[6] = props;
326
- $[7] = t2;
327
- } else t2 = $[7];
358
+ $[7] = messages;
359
+ $[8] = props;
360
+ $[9] = t2;
361
+ } else t2 = $[9];
328
362
  return t2;
329
363
  };
330
364
  /**
@@ -369,21 +403,23 @@ var FieldPending = (t0) => {
369
403
  * const { labelId, errorId } = useFieldIds();
370
404
  */
371
405
  var useFieldIds = () => {
372
- const $ = c(9);
406
+ const $ = c(10);
373
407
  const id = useId();
408
+ const groupLineIds = useFieldGroupLineIds();
374
409
  const descriptionId = `${id}description`;
375
410
  const errorId = `${id}error`;
376
411
  const t0 = `${id}label`;
377
412
  const t1 = `${id}control`;
378
413
  let t2;
379
- if ($[0] !== descriptionId || $[1] !== errorId) {
380
- t2 = (hasDescription, invalid) => [hasDescription ? descriptionId : "", invalid ? errorId : ""].filter(_temp).join(" ") || void 0;
414
+ if ($[0] !== descriptionId || $[1] !== errorId || $[2] !== groupLineIds) {
415
+ t2 = (hasDescription, invalid) => [hasDescription ? descriptionId : "", ...groupLineIds ?? [invalid ? errorId : ""]].filter(_temp2).join(" ") || void 0;
381
416
  $[0] = descriptionId;
382
417
  $[1] = errorId;
383
- $[2] = t2;
384
- } else t2 = $[2];
418
+ $[2] = groupLineIds;
419
+ $[3] = t2;
420
+ } else t2 = $[3];
385
421
  let t3;
386
- if ($[3] !== descriptionId || $[4] !== errorId || $[5] !== t0 || $[6] !== t1 || $[7] !== t2) {
422
+ if ($[4] !== descriptionId || $[5] !== errorId || $[6] !== t0 || $[7] !== t1 || $[8] !== t2) {
387
423
  t3 = {
388
424
  labelId: t0,
389
425
  descriptionId,
@@ -391,13 +427,13 @@ var useFieldIds = () => {
391
427
  controlId: t1,
392
428
  describedBy: t2
393
429
  };
394
- $[3] = descriptionId;
395
- $[4] = errorId;
396
- $[5] = t0;
397
- $[6] = t1;
398
- $[7] = t2;
399
- $[8] = t3;
400
- } else t3 = $[8];
430
+ $[4] = descriptionId;
431
+ $[5] = errorId;
432
+ $[6] = t0;
433
+ $[7] = t1;
434
+ $[8] = t2;
435
+ $[9] = t3;
436
+ } else t3 = $[9];
401
437
  return t3;
402
438
  };
403
439
  /**
@@ -428,7 +464,14 @@ var Field$1 = {
428
464
  InlineLabelText,
429
465
  useIds: useFieldIds
430
466
  };
431
- function _temp(part) {
467
+ function _temp(lineId) {
468
+ return /* @__PURE__ */ jsx(Field.Error, {
469
+ match: true,
470
+ id: lineId,
471
+ render: /* @__PURE__ */ jsx(Unrendered, {})
472
+ }, lineId);
473
+ }
474
+ function _temp2(part) {
432
475
  return part !== "";
433
476
  }
434
477
  export { Field$1 as Field, useFieldIds };
@@ -1,20 +1,22 @@
1
1
  import './field.css';// src/components/field/field.module.scss
2
- var field = "_field_14ydh_3";
3
- var label = "_label_14ydh_9";
4
- var optional = "_optional_14ydh_14";
5
- var inlineText = "_inlineText_14ydh_20";
6
- var inlineOptional = "_inlineOptional_14ydh_23";
7
- var required = "_required_14ydh_26";
8
- var description = "_description_14ydh_35";
9
- var pending = "_pending_14ydh_39";
2
+ var field = "_field_dzutr_3";
3
+ var label = "_label_dzutr_13";
4
+ var description = "_description_dzutr_22";
5
+ var error = "_error_dzutr_25";
6
+ var optional = "_optional_dzutr_33";
7
+ var inlineText = "_inlineText_dzutr_39";
8
+ var inlineOptional = "_inlineOptional_dzutr_42";
9
+ var required = "_required_dzutr_45";
10
+ var pending = "_pending_dzutr_58";
10
11
  var field_module_default = {
11
12
  field,
12
13
  label,
14
+ description,
15
+ error,
13
16
  optional,
14
17
  inlineText,
15
18
  inlineOptional,
16
19
  required,
17
- description,
18
20
  pending
19
21
  };
20
- export { field_module_default as default, description, field, inlineOptional, inlineText, label, optional, pending, required };
22
+ export { field_module_default as default, description, error, field, inlineOptional, inlineText, label, optional, pending, required };
@@ -0,0 +1,31 @@
1
+ import { type ReactNode } from "react";
2
+ import type { FieldProps } from "../field/index.ts";
3
+ import type { FieldsetErrorFormat, FieldsetMember } from "./fieldset-root.tsx";
4
+ export type FieldGroupProps = Pick<FieldProps, "requiredness" | "optionalLabel"> & Readonly<{
5
+ /** The visible name of the group, such as "Address". */
6
+ legend: string;
7
+ /** Help about the group as a whole, under its legend. */
8
+ description?: ReactNode;
9
+ /** Errors about the group as a whole, such as a failed address lookup, in any shape `normalizeFieldErrors` accepts. */
10
+ errors?: unknown;
11
+ /** The fields whose errors the group shows in its own error instead of under each field. */
12
+ members?: readonly FieldsetMember[];
13
+ /** Turns a member's message into its line. Left out, the line is the message. */
14
+ formatError?: FieldsetErrorFormat;
15
+ className?: string;
16
+ /** The fields, laid out side by side. */
17
+ children: ReactNode;
18
+ }>;
19
+ /**
20
+ * Related fields side by side under one legend, with one error for all of
21
+ * them: the lines of its `members`, each tied to its field's control, then its
22
+ * own `errors`. Every field's label, control, description and error line up
23
+ * with its neighbours'. The `--field-group-columns` knob sets the columns.
24
+ *
25
+ * @example
26
+ * <FieldGroup legend="Address" optionalLabel="optional" members={members}>
27
+ * <TextField label="Postal code" … />
28
+ * <TextField label="House number" … />
29
+ * </FieldGroup>
30
+ */
31
+ export declare const FieldGroup: ({ legend, description, errors, requiredness, optionalLabel, members, formatError, className, children, }: FieldGroupProps) => ReactNode;