@dloizides/ui-forms 1.4.0 → 1.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.0
4
+
5
+ - **Add `Field` — the generic label-over-control wrapper.** `FormField` is hard-wired to a text
6
+ input (`FormFieldProps extends Omit<TextInputProps,'style'>`), so any NON-text control (a
7
+ dropdown, a date picker, a chip selector) had no shared way to get a label — and ended up
8
+ visually misaligned next to its `FormField` siblings: the control's box started ~21px higher
9
+ (no label row) and it missed `FormField`'s `marginBottom: 16`, so wrapped rows lost their
10
+ vertical rhythm. This was hand-rolled in 5 places across zygos-web and aml-v2.
11
+ `Field` takes `{ label, required?, error?, children, containerStyle?, testID? }` and renders the
12
+ same label row / required mark / error line / spacing.
13
+ - **`FormField` now composes `Field` internally**, so the two can never drift apart — the metrics
14
+ live in exactly one place. `FormFieldProps` and every rendered attribute (`form-field-input`
15
+ testID, `aria-invalid` / `aria-describedby` / `aria-required`, the `role="alert"` error line)
16
+ are unchanged: fully backward-compatible.
17
+ - `Field`'s `children` may be a plain node (the common case) or a render function receiving
18
+ `{ describedById, hasError }`, so a custom control can wire the error line into its own
19
+ `aria-describedby` / invalid state exactly the way `FormField` does.
20
+
3
21
  ## 1.4.0
4
22
 
5
23
  - **Accessibility (WCAG 2.1 AA) hardening — additive + backward-compatible.**
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @dloizides/ui-forms
2
2
 
3
3
  Themable, brand-agnostic React Native (RN-web) **form** components for the dloizides.com
4
- portfolio: `FormField`, `ChipSelector`, `FormSwitch`. They read theme colours from the shared
4
+ portfolio: `Field`, `FormField`, `ChipSelector`, `FormSwitch`. They read theme colours from the shared
5
5
  `@dloizides/ui-feedback` UI context (`useUi`) — mount one `UiProvider` / `FeedbackUiProvider`
6
6
  at your app root and these components pick up your theme automatically.
7
7
 
@@ -24,6 +24,44 @@ import { FormField, ChipSelector, FormSwitch } from '@dloizides/ui-forms';
24
24
  <FormSwitch label="Email notifications" value={notify} onValueChange={setNotify} />
25
25
  ```
26
26
 
27
+ ## `Field` — label any control, not just a text input
28
+
29
+ `FormField` is hard-wired to a text input. For **any other control** — a dropdown, a date picker,
30
+ a chip selector — wrap it in `Field` to get the identical label row, required mark, error line and
31
+ bottom spacing. This is what keeps a dropdown aligned with the `FormField` next to it: without it
32
+ the dropdown's box starts a label-row higher and the row loses its vertical rhythm.
33
+
34
+ ```tsx
35
+ import { Field } from '@dloizides/ui-forms';
36
+
37
+ <Field label="Currency" required error={errors.currency}>
38
+ <ModalDropdown options={currencies} value={currency} onChange={setCurrency} />
39
+ </Field>
40
+ ```
41
+
42
+ `FormField` composes `Field` internally, so the two can never drift out of alignment.
43
+
44
+ To wire the error line into a custom control's own `aria-describedby` / invalid state, pass a
45
+ **function** child — it receives `{ describedById, hasError }` (`describedById` is `undefined`
46
+ when there is no error):
47
+
48
+ ```tsx
49
+ <Field label="Country" error={errors.country}>
50
+ {({ describedById, hasError }) => (
51
+ <CountryPicker aria-describedby={describedById} hasError={hasError} value={country} onChange={setCountry} />
52
+ )}
53
+ </Field>
54
+ ```
55
+
56
+ | Prop | Type | Notes |
57
+ |------|------|-------|
58
+ | `label` | `string` | required |
59
+ | `children` | `ReactNode \| (ctx) => ReactNode` | the control |
60
+ | `required` | `boolean` | renders a decorative `*` (hidden from assistive tech) |
61
+ | `error` | `string` | renders a `role="alert"` line, linked via the child's `describedById` |
62
+ | `containerStyle` | `ViewStyle` | merged over the container |
63
+ | `testID` | `string` | on the container |
64
+
27
65
  The injected theme needs `colors.{surface,text,textSecondary,border}`, `palette.primary['500']`
28
66
  and `semantic.error['500']` — supplied via `@dloizides/ui-feedback`'s provider. Without a provider
29
67
  the components fall back to a neutral default theme.
package/dist/index.d.mts CHANGED
@@ -1,12 +1,49 @@
1
1
  import React from 'react';
2
- import { TextInputProps, ViewStyle, TextInput, TextStyle } from 'react-native';
2
+ import { ViewStyle, TextInputProps, TextInput, TextStyle } from 'react-native';
3
+
4
+ /**
5
+ * Field — the generic "label over a control, error under it" wrapper.
6
+ *
7
+ * `FormField` is hard-wired to a text input, so any NON-text control (a dropdown, a date picker,
8
+ * a chip selector) had no shared way to get a label — and ended up visually misaligned next to
9
+ * its `FormField` siblings: its box started a label-row higher, and it missed the container's
10
+ * bottom margin so wrapped rows lost their vertical rhythm.
11
+ *
12
+ * `Field` is that label row + spacing, control-agnostic. Its metrics are the single source of
13
+ * truth: `FormField` composes `Field` internally, so the two can never drift apart.
14
+ *
15
+ * Children may be a plain node (the common case — just wrap your control) or a render function
16
+ * receiving `{ describedById, hasError }`, so a custom control can wire the error line into its
17
+ * own `aria-describedby` / invalid state exactly the way `FormField` does.
18
+ */
19
+
20
+ /** What a render-function child receives, so it can mirror `FormField`'s error wiring. */
21
+ interface FieldChildContext {
22
+ /** id of the rendered error line, or `undefined` when there is no error. */
23
+ describedById?: string;
24
+ hasError: boolean;
25
+ }
26
+ type FieldChildren = React.ReactNode | ((context: FieldChildContext) => React.ReactNode);
27
+ interface FieldProps {
28
+ label: string;
29
+ children: FieldChildren;
30
+ required?: boolean;
31
+ error?: string;
32
+ containerStyle?: ViewStyle;
33
+ testID?: string;
34
+ }
35
+ declare const Field: ({ label, children, required, error, containerStyle, testID, }: FieldProps) => React.ReactElement;
3
36
 
4
37
  /**
5
38
  * Reusable form field — labelled text input with optional required mark + error.
6
39
  *
7
- * The input itself is the shared `ThemedTextInput` primitive, so every FormField gets the
8
- * readable + lively treatment (dark text, subtle off-white rest background, brand focus
9
- * animation with a soft ring) for free, driven by the active theme.
40
+ * The label row, the required mark, the error line and the container spacing all come from the
41
+ * generic `Field` wrapper, so a `FormField` and a `Field`-wrapped dropdown sitting next to each
42
+ * other line up exactly and can never drift apart. FormField only adds the control itself.
43
+ *
44
+ * The input is the shared `ThemedTextInput` primitive, so every FormField gets the readable +
45
+ * lively treatment (dark text, subtle off-white rest background, brand focus animation with a
46
+ * soft ring) for free, driven by the active theme.
10
47
  */
11
48
 
12
49
  interface FormFieldProps extends Omit<TextInputProps, 'style'> {
@@ -142,4 +179,4 @@ interface FormCheckboxProps {
142
179
  }
143
180
  declare const FormCheckbox: ({ label, value, onValueChange, onChange, hint, description, disabled, containerStyle, testID, accessibilityLabel, accessibilityHint, }: FormCheckboxProps) => React.ReactElement;
144
181
 
145
- export { type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormSwitch, type FormSwitchProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, type UseThemedInputOptions, useThemedInput };
182
+ export { type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, Field, type FieldChildContext, type FieldChildren, type FieldProps, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormSwitch, type FormSwitchProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, type UseThemedInputOptions, useThemedInput };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,49 @@
1
1
  import React from 'react';
2
- import { TextInputProps, ViewStyle, TextInput, TextStyle } from 'react-native';
2
+ import { ViewStyle, TextInputProps, TextInput, TextStyle } from 'react-native';
3
+
4
+ /**
5
+ * Field — the generic "label over a control, error under it" wrapper.
6
+ *
7
+ * `FormField` is hard-wired to a text input, so any NON-text control (a dropdown, a date picker,
8
+ * a chip selector) had no shared way to get a label — and ended up visually misaligned next to
9
+ * its `FormField` siblings: its box started a label-row higher, and it missed the container's
10
+ * bottom margin so wrapped rows lost their vertical rhythm.
11
+ *
12
+ * `Field` is that label row + spacing, control-agnostic. Its metrics are the single source of
13
+ * truth: `FormField` composes `Field` internally, so the two can never drift apart.
14
+ *
15
+ * Children may be a plain node (the common case — just wrap your control) or a render function
16
+ * receiving `{ describedById, hasError }`, so a custom control can wire the error line into its
17
+ * own `aria-describedby` / invalid state exactly the way `FormField` does.
18
+ */
19
+
20
+ /** What a render-function child receives, so it can mirror `FormField`'s error wiring. */
21
+ interface FieldChildContext {
22
+ /** id of the rendered error line, or `undefined` when there is no error. */
23
+ describedById?: string;
24
+ hasError: boolean;
25
+ }
26
+ type FieldChildren = React.ReactNode | ((context: FieldChildContext) => React.ReactNode);
27
+ interface FieldProps {
28
+ label: string;
29
+ children: FieldChildren;
30
+ required?: boolean;
31
+ error?: string;
32
+ containerStyle?: ViewStyle;
33
+ testID?: string;
34
+ }
35
+ declare const Field: ({ label, children, required, error, containerStyle, testID, }: FieldProps) => React.ReactElement;
3
36
 
4
37
  /**
5
38
  * Reusable form field — labelled text input with optional required mark + error.
6
39
  *
7
- * The input itself is the shared `ThemedTextInput` primitive, so every FormField gets the
8
- * readable + lively treatment (dark text, subtle off-white rest background, brand focus
9
- * animation with a soft ring) for free, driven by the active theme.
40
+ * The label row, the required mark, the error line and the container spacing all come from the
41
+ * generic `Field` wrapper, so a `FormField` and a `Field`-wrapped dropdown sitting next to each
42
+ * other line up exactly and can never drift apart. FormField only adds the control itself.
43
+ *
44
+ * The input is the shared `ThemedTextInput` primitive, so every FormField gets the readable +
45
+ * lively treatment (dark text, subtle off-white rest background, brand focus animation with a
46
+ * soft ring) for free, driven by the active theme.
10
47
  */
11
48
 
12
49
  interface FormFieldProps extends Omit<TextInputProps, 'style'> {
@@ -142,4 +179,4 @@ interface FormCheckboxProps {
142
179
  }
143
180
  declare const FormCheckbox: ({ label, value, onValueChange, onChange, hint, description, disabled, containerStyle, testID, accessibilityLabel, accessibilityHint, }: FormCheckboxProps) => React.ReactElement;
144
181
 
145
- export { type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormSwitch, type FormSwitchProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, type UseThemedInputOptions, useThemedInput };
182
+ export { type ChipOption, ChipSelector, type ChipSelectorProps, type ChipVariant, Field, type FieldChildContext, type FieldChildren, type FieldProps, FormCheckbox, type FormCheckboxProps, FormField, type FormFieldProps, FormSwitch, type FormSwitchProps, type ThemedInput, ThemedTextInput, type ThemedTextInputProps, type UseThemedInputOptions, useThemedInput };
package/dist/index.js CHANGED
@@ -9,7 +9,74 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
 
10
10
  var React3__default = /*#__PURE__*/_interopDefault(React3);
11
11
 
12
- // src/FormField/FormField.tsx
12
+ // src/Field/Field.tsx
13
+ var CONTAINER_MARGIN_BOTTOM = 16;
14
+ var LABEL_FONT_SIZE = 13;
15
+ var LABEL_FONT_WEIGHT = "600";
16
+ var LABEL_MARGIN_BOTTOM = 4;
17
+ var ERROR_FONT_SIZE = 12;
18
+ var ERROR_MARGIN_TOP = 4;
19
+ var styles = reactNative.StyleSheet.create({
20
+ container: {
21
+ marginBottom: CONTAINER_MARGIN_BOTTOM
22
+ },
23
+ label: {
24
+ marginBottom: LABEL_MARGIN_BOTTOM,
25
+ fontSize: LABEL_FONT_SIZE,
26
+ fontWeight: LABEL_FONT_WEIGHT
27
+ },
28
+ errorText: {
29
+ fontSize: ERROR_FONT_SIZE,
30
+ marginTop: ERROR_MARGIN_TOP
31
+ }
32
+ });
33
+ var fieldSeq = 0;
34
+ function useFieldErrorId() {
35
+ const [id] = React3__default.default.useState(() => {
36
+ fieldSeq += 1;
37
+ return `field-error-${String(fieldSeq)}`;
38
+ });
39
+ return id;
40
+ }
41
+ function hasFieldError(error) {
42
+ return typeof error === "string" && error !== "";
43
+ }
44
+ var Field = ({
45
+ label,
46
+ children,
47
+ required = false,
48
+ error,
49
+ containerStyle,
50
+ testID
51
+ }) => {
52
+ const { theme } = uiFeedback.useUi();
53
+ const { colors, semantic } = theme;
54
+ const errorColor = semantic.error["500"];
55
+ const hasError = hasFieldError(error);
56
+ const errorId = useFieldErrorId();
57
+ const themeStyles = React3__default.default.useMemo(
58
+ () => ({
59
+ label: { color: colors.textSecondary },
60
+ requiredMark: { color: errorColor },
61
+ errorText: { color: errorColor }
62
+ }),
63
+ [colors.textSecondary, errorColor]
64
+ );
65
+ const control = typeof children === "function" ? children({ describedById: hasError ? errorId : void 0, hasError }) : children;
66
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.container, containerStyle], testID, children: [
67
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles.label, themeStyles.label], children: [
68
+ label,
69
+ " ",
70
+ required ? (
71
+ // The asterisk is decorative — the control's `aria-required` conveys "required" to
72
+ // assistive tech, so a screen reader says "required" instead of "star".
73
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: themeStyles.requiredMark, children: "*" })
74
+ ) : null
75
+ ] }),
76
+ control,
77
+ hasError ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { nativeID: errorId, role: "alert", style: [styles.errorText, themeStyles.errorText], children: error }) : null
78
+ ] });
79
+ };
13
80
  var IS_WEB = reactNative.Platform.OS === "web";
14
81
  var TRANSITION_PROPERTY = "border-color, background-color, box-shadow";
15
82
  var TRANSITION_DURATION = "0.14s";
@@ -131,85 +198,37 @@ var ThemedTextInput = React3__default.default.forwardRef(function ThemedTextInpu
131
198
  }
132
199
  );
133
200
  });
134
- var CONTAINER_MARGIN_BOTTOM = 16;
135
201
  var INPUT_PADDING_V = 10;
136
202
  var INPUT_PADDING_H = 12;
137
203
  var INPUT_BORDER_RADIUS = 8;
138
- var ERROR_FONT_SIZE = 12;
139
- var LABEL_FONT_SIZE = 13;
140
- var styles = reactNative.StyleSheet.create({
141
- container: {
142
- marginBottom: CONTAINER_MARGIN_BOTTOM
143
- },
144
- label: {
145
- marginBottom: 4,
146
- fontSize: LABEL_FONT_SIZE,
147
- fontWeight: "600"
148
- },
204
+ var INPUT_BORDER_WIDTH = 1;
205
+ var styles2 = reactNative.StyleSheet.create({
149
206
  input: {
150
207
  paddingVertical: INPUT_PADDING_V,
151
208
  paddingHorizontal: INPUT_PADDING_H,
152
209
  borderRadius: INPUT_BORDER_RADIUS,
153
- borderWidth: 1
154
- },
155
- errorText: {
156
- fontSize: ERROR_FONT_SIZE,
157
- marginTop: 4
210
+ borderWidth: INPUT_BORDER_WIDTH
158
211
  }
159
212
  });
160
- var formFieldSeq = 0;
161
- function useErrorId() {
162
- const [id] = React3__default.default.useState(() => {
163
- formFieldSeq += 1;
164
- return `form-field-error-${String(formFieldSeq)}`;
165
- });
166
- return id;
167
- }
168
213
  var FormField = ({
169
214
  label,
170
215
  required = false,
171
216
  error,
172
217
  containerStyle,
173
218
  ...textInputProps
174
- }) => {
175
- const { theme } = uiFeedback.useUi();
176
- const { colors, semantic } = theme;
177
- const errorColor = semantic.error["500"];
178
- const hasError = typeof error === "string" && error !== "";
179
- const errorId = useErrorId();
180
- const themeStyles = React3__default.default.useMemo(
181
- () => ({
182
- label: { color: colors.textSecondary },
183
- requiredMark: { color: errorColor },
184
- errorText: { color: errorColor }
185
- }),
186
- [colors.textSecondary, errorColor]
187
- );
188
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.container, containerStyle], children: [
189
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [styles.label, themeStyles.label], children: [
190
- label,
191
- " ",
192
- required ? (
193
- // The asterisk is decorative — `requiredField` conveys "required" to assistive tech.
194
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: themeStyles.requiredMark, children: "*" })
195
- ) : null
196
- ] }),
197
- /* @__PURE__ */ jsxRuntime.jsx(
198
- ThemedTextInput,
199
- {
200
- accessibilityHint: `Enter ${label}`,
201
- accessibilityLabel: label,
202
- describedById: hasError ? errorId : void 0,
203
- hasError,
204
- requiredField: required,
205
- style: styles.input,
206
- testID: "form-field-input",
207
- ...textInputProps
208
- }
209
- ),
210
- hasError ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { nativeID: errorId, role: "alert", style: [styles.errorText, themeStyles.errorText], children: error }) : null
211
- ] });
212
- };
219
+ }) => /* @__PURE__ */ jsxRuntime.jsx(Field, { containerStyle, error, label, required, children: ({ describedById, hasError }) => /* @__PURE__ */ jsxRuntime.jsx(
220
+ ThemedTextInput,
221
+ {
222
+ accessibilityHint: `Enter ${label}`,
223
+ accessibilityLabel: label,
224
+ describedById,
225
+ hasError,
226
+ requiredField: required,
227
+ style: styles2.input,
228
+ testID: "form-field-input",
229
+ ...textInputProps
230
+ }
231
+ ) });
213
232
  var TRANSPARENT_COLOR = "transparent";
214
233
  var WHITE_COLOR = "#fff";
215
234
  var SOLID_RADIUS = 20;
@@ -235,7 +254,7 @@ function withAlpha2(hex, alpha) {
235
254
  const isValid = !Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b);
236
255
  return isValid ? `rgba(${r}, ${g}, ${b}, ${alpha})` : hex;
237
256
  }
238
- var styles2 = reactNative.StyleSheet.create({
257
+ var styles3 = reactNative.StyleSheet.create({
239
258
  label: {
240
259
  marginBottom: 8,
241
260
  fontWeight: "600"
@@ -285,12 +304,12 @@ function OutlineChip({
285
304
  accessibilityState: { selected, disabled },
286
305
  disabled,
287
306
  hitSlop: CHIP_HIT_SLOP,
288
- style: styles2.chipWrapper,
307
+ style: styles3.chipWrapper,
289
308
  testID: `chip-selector-chip-${String(option.value)}`,
290
309
  onHoverIn: () => setHovered(true),
291
310
  onHoverOut: () => setHovered(false),
292
311
  onPress,
293
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles2.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.chipText, styles2.chipTextOutline, { color: textColor }], children: option.label }) })
312
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles3.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.chipText, styles3.chipTextOutline, { color: textColor }], children: option.label }) })
294
313
  }
295
314
  );
296
315
  }
@@ -312,10 +331,10 @@ function SolidChip({
312
331
  accessibilityState: { selected, disabled },
313
332
  disabled,
314
333
  hitSlop: CHIP_HIT_SLOP,
315
- style: styles2.chipWrapper,
334
+ style: styles3.chipWrapper,
316
335
  testID: `chip-selector-chip-${String(option.value)}`,
317
336
  onPress,
318
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles2.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.chipText, { color: textColor }], children: option.label }) })
337
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles3.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.chipText, { color: textColor }], children: option.label }) })
319
338
  }
320
339
  );
321
340
  }
@@ -350,8 +369,8 @@ var ChipSelector = ({
350
369
  return value === optionValue;
351
370
  }
352
371
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: containerStyle, children: [
353
- hasLabel ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.label, labelStyle], children: label }) : null,
354
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.chipContainer, children: options.map((option) => {
372
+ hasLabel ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.label, labelStyle], children: label }) : null,
373
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.chipContainer, children: options.map((option) => {
355
374
  const selected = isSelected(option.value);
356
375
  const onPress = () => onChange(option.value);
357
376
  return variant === "outline" ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -379,7 +398,7 @@ var ChipSelector = ({
379
398
  ] });
380
399
  };
381
400
  var DESCRIPTION_FONT_SIZE = 12;
382
- var styles3 = reactNative.StyleSheet.create({
401
+ var styles4 = reactNative.StyleSheet.create({
383
402
  container: {
384
403
  flexDirection: "row",
385
404
  justifyContent: "space-between",
@@ -419,13 +438,13 @@ var FormSwitch = ({
419
438
  reactNative.View,
420
439
  {
421
440
  style: [
422
- styles3.container,
441
+ styles4.container,
423
442
  containerStyle
424
443
  ],
425
444
  children: [
426
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.textColumn, children: [
427
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.label, themeStyles.label], children: label }),
428
- hasDescription ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.description, themeStyles.description], children: description }) : null
445
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.textColumn, children: [
446
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.label, themeStyles.label], children: label }),
447
+ hasDescription ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.description, themeStyles.description], children: description }) : null
429
448
  ] }),
430
449
  /* @__PURE__ */ jsxRuntime.jsx(
431
450
  reactNative.Switch,
@@ -450,7 +469,7 @@ var BOX_SIZE = 20;
450
469
  var BOX_RADIUS = 5;
451
470
  var CHECK_FONT_SIZE = 14;
452
471
  var DISABLED_OPACITY = 0.5;
453
- var styles4 = reactNative.StyleSheet.create({
472
+ var styles5 = reactNative.StyleSheet.create({
454
473
  container: {
455
474
  flexDirection: "row",
456
475
  alignItems: "flex-start",
@@ -532,14 +551,14 @@ var FormCheckbox = ({
532
551
  disabled,
533
552
  onPress: toggle,
534
553
  role: "checkbox",
535
- style: [styles4.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
554
+ style: [styles5.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
536
555
  testID,
537
556
  ...keyboardProps,
538
557
  children: [
539
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles4.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
540
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.textColumn, children: [
541
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.label, themeStyles.label], children: label }),
542
- hasHint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.hint, themeStyles.hint], children: hintText }) : null
558
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles5.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
559
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.textColumn, children: [
560
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.label, themeStyles.label], children: label }),
561
+ hasHint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.hint, themeStyles.hint], children: hintText }) : null
543
562
  ] })
544
563
  ]
545
564
  }
@@ -547,6 +566,7 @@ var FormCheckbox = ({
547
566
  };
548
567
 
549
568
  exports.ChipSelector = ChipSelector;
569
+ exports.Field = Field;
550
570
  exports.FormCheckbox = FormCheckbox;
551
571
  exports.FormField = FormField;
552
572
  exports.FormSwitch = FormSwitch;