@dloizides/ui-forms 1.5.0 → 1.7.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/dist/index.mjs CHANGED
@@ -1,45 +1,116 @@
1
- import React3, { useState, useCallback, useMemo } from 'react';
1
+ import React, { useState, useCallback, useMemo } from 'react';
2
2
  import { StyleSheet, Platform, TextInput, View, Text, Switch, Pressable, TouchableOpacity } from 'react-native';
3
3
  import { useUi } from '@dloizides/ui-feedback';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
+ import { Button } from '@dloizides/ui-buttons';
5
6
 
6
7
  // src/Field/Field.tsx
8
+ function FieldLabel({
9
+ label,
10
+ marker,
11
+ optionalLabel,
12
+ style,
13
+ requiredMarkStyle,
14
+ optionalMarkStyle
15
+ }) {
16
+ return /* @__PURE__ */ jsxs(Text, { style, children: [
17
+ label,
18
+ " ",
19
+ marker === "required" ? (
20
+ // The asterisk is decorative — the control's `aria-required` conveys "required" to
21
+ // assistive tech, so a screen reader says "required" instead of "star".
22
+ /* @__PURE__ */ jsx(Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: requiredMarkStyle, children: "*" })
23
+ ) : null,
24
+ marker === "optional" && optionalLabel !== "" ? (
25
+ // Also decorative: "optional" is the ABSENCE of `aria-required`, which assistive tech
26
+ // already conveys. Rendering it to a screen reader would double-announce the same fact.
27
+ /* @__PURE__ */ jsx(Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: optionalMarkStyle, children: optionalLabel })
28
+ ) : null
29
+ ] });
30
+ }
31
+
32
+ // src/Field/fieldMarkers.ts
33
+ var DEFAULT_OPTIONAL_LABEL = "(optional)";
34
+ function resolveFieldMarker(required, optional) {
35
+ if (required) return "required";
36
+ if (optional) return "optional";
37
+ return "none";
38
+ }
39
+ var FIELD_FONT_SIZE = 13;
40
+ var FIELD_FONT_WEIGHT = "600";
41
+ var CONTROL_FONT_SIZE = 11;
42
+ var CONTROL_FONT_WEIGHT = "700";
43
+ var CONTROL_LETTER_SPACING = 0.4;
44
+ var labelVariantStyles = StyleSheet.create({
45
+ field: {
46
+ fontSize: FIELD_FONT_SIZE,
47
+ fontWeight: FIELD_FONT_WEIGHT
48
+ },
49
+ control: {
50
+ fontSize: CONTROL_FONT_SIZE,
51
+ fontWeight: CONTROL_FONT_WEIGHT,
52
+ letterSpacing: CONTROL_LETTER_SPACING,
53
+ textTransform: "uppercase"
54
+ }
55
+ });
56
+ function resolveLabelVariantStyle(variant) {
57
+ return variant === "control" ? labelVariantStyles.control : labelVariantStyles.field;
58
+ }
7
59
  var CONTAINER_MARGIN_BOTTOM = 16;
8
- var LABEL_FONT_SIZE = 13;
9
- var LABEL_FONT_WEIGHT = "600";
10
60
  var LABEL_MARGIN_BOTTOM = 4;
11
61
  var ERROR_FONT_SIZE = 12;
12
62
  var ERROR_MARGIN_TOP = 4;
63
+ var HINT_FONT_SIZE = 12;
64
+ var HINT_MARGIN_TOP = 4;
65
+ var OPTIONAL_MARK_FONT_WEIGHT = "400";
13
66
  var styles = StyleSheet.create({
14
67
  container: {
15
68
  marginBottom: CONTAINER_MARGIN_BOTTOM
16
69
  },
17
70
  label: {
18
- marginBottom: LABEL_MARGIN_BOTTOM,
19
- fontSize: LABEL_FONT_SIZE,
20
- fontWeight: LABEL_FONT_WEIGHT
71
+ marginBottom: LABEL_MARGIN_BOTTOM
72
+ },
73
+ hintText: {
74
+ fontSize: HINT_FONT_SIZE,
75
+ marginTop: HINT_MARGIN_TOP
21
76
  },
22
77
  errorText: {
23
78
  fontSize: ERROR_FONT_SIZE,
24
79
  marginTop: ERROR_MARGIN_TOP
80
+ },
81
+ optionalMark: {
82
+ fontWeight: OPTIONAL_MARK_FONT_WEIGHT
25
83
  }
26
84
  });
27
85
  var fieldSeq = 0;
28
- function useFieldErrorId() {
29
- const [id] = React3.useState(() => {
86
+ function useFieldIds() {
87
+ const [ids] = React.useState(() => {
30
88
  fieldSeq += 1;
31
- return `field-error-${String(fieldSeq)}`;
89
+ const seq = String(fieldSeq);
90
+ return { hintId: `field-hint-${seq}`, errorId: `field-error-${seq}` };
32
91
  });
33
- return id;
92
+ return ids;
93
+ }
94
+ function isPresent(value) {
95
+ return typeof value === "string" && value !== "";
34
96
  }
35
97
  function hasFieldError(error) {
36
- return typeof error === "string" && error !== "";
98
+ return isPresent(error);
99
+ }
100
+ function joinDescribedBy(hintId, errorId) {
101
+ const ids = [hintId, errorId].filter((id) => isPresent(id));
102
+ return ids.length > 0 ? ids.join(" ") : void 0;
37
103
  }
38
104
  var Field = ({
39
105
  label,
40
106
  children,
41
107
  required = false,
108
+ optional = false,
109
+ optionalLabel = DEFAULT_OPTIONAL_LABEL,
110
+ hint,
42
111
  error,
112
+ labelVariant,
113
+ labelStyle,
43
114
  containerStyle,
44
115
  testID
45
116
  }) => {
@@ -47,30 +118,89 @@ var Field = ({
47
118
  const { colors, semantic } = theme;
48
119
  const errorColor = semantic.error["500"];
49
120
  const hasError = hasFieldError(error);
50
- const errorId = useFieldErrorId();
51
- const themeStyles = React3.useMemo(
121
+ const hasHint = isPresent(hint);
122
+ const labelText = label ?? "";
123
+ const hasLabel = labelText !== "";
124
+ const marker = resolveFieldMarker(required, optional);
125
+ const { hintId, errorId } = useFieldIds();
126
+ const themeStyles = React.useMemo(
52
127
  () => ({
53
128
  label: { color: colors.textSecondary },
54
129
  requiredMark: { color: errorColor },
130
+ // The optional marker is an aside, so it sits a step back from the label's own ink.
131
+ optionalMark: { color: colors.textSecondary },
132
+ hintText: { color: colors.textSecondary },
55
133
  errorText: { color: errorColor }
56
134
  }),
57
135
  [colors.textSecondary, errorColor]
58
136
  );
59
- const control = typeof children === "function" ? children({ describedById: hasError ? errorId : void 0, hasError }) : children;
137
+ const describedById = joinDescribedBy(hasHint ? hintId : void 0, hasError ? errorId : void 0);
138
+ const control = typeof children === "function" ? children({ describedById, hasError }) : children;
60
139
  return /* @__PURE__ */ jsxs(View, { style: [styles.container, containerStyle], testID, children: [
61
- /* @__PURE__ */ jsxs(Text, { style: [styles.label, themeStyles.label], children: [
62
- label,
63
- " ",
64
- required ? (
65
- // The asterisk is decorative — the control's `aria-required` conveys "required" to
66
- // assistive tech, so a screen reader says "required" instead of "star".
67
- /* @__PURE__ */ jsx(Text, { "aria-hidden": true, accessibilityElementsHidden: true, importantForAccessibility: "no", style: themeStyles.requiredMark, children: "*" })
68
- ) : null
69
- ] }),
140
+ hasLabel ? /* @__PURE__ */ jsx(
141
+ FieldLabel,
142
+ {
143
+ label: labelText,
144
+ marker,
145
+ optionalLabel,
146
+ optionalMarkStyle: [styles.optionalMark, themeStyles.optionalMark],
147
+ requiredMarkStyle: themeStyles.requiredMark,
148
+ style: [styles.label, resolveLabelVariantStyle(labelVariant), themeStyles.label, labelStyle]
149
+ }
150
+ ) : null,
70
151
  control,
152
+ hasHint ? /* @__PURE__ */ jsx(Text, { nativeID: hintId, style: [styles.hintText, themeStyles.hintText], children: hint }) : null,
71
153
  hasError ? /* @__PURE__ */ jsx(Text, { nativeID: errorId, role: "alert", style: [styles.errorText, themeStyles.errorText], children: error }) : null
72
154
  ] });
73
155
  };
156
+ var ACTIONS_GAP = 12;
157
+ var styles2 = StyleSheet.create({
158
+ container: {
159
+ flexDirection: "row",
160
+ justifyContent: "flex-end",
161
+ gap: ACTIONS_GAP
162
+ }
163
+ });
164
+ var FormActions = ({
165
+ onSave,
166
+ onCancel,
167
+ saveLabel,
168
+ cancelLabel = "",
169
+ saveHint,
170
+ cancelHint,
171
+ saving = false,
172
+ saveDisabled = false,
173
+ containerStyle,
174
+ saveTestID = "save-button",
175
+ cancelTestID = "cancel-button",
176
+ testID
177
+ }) => /* @__PURE__ */ jsxs(View, { style: [styles2.container, containerStyle], testID, children: [
178
+ typeof onCancel === "function" ? /* @__PURE__ */ jsx(
179
+ Button,
180
+ {
181
+ accessibilityHint: cancelHint ?? cancelLabel,
182
+ accessibilityLabel: cancelLabel,
183
+ disabled: saving,
184
+ label: cancelLabel,
185
+ testID: cancelTestID,
186
+ variant: "outline",
187
+ onPress: onCancel
188
+ }
189
+ ) : null,
190
+ /* @__PURE__ */ jsx(
191
+ Button,
192
+ {
193
+ accessibilityHint: saveHint ?? saveLabel,
194
+ accessibilityLabel: saveLabel,
195
+ disabled: saveDisabled,
196
+ label: saveLabel,
197
+ loading: saving,
198
+ testID: saveTestID,
199
+ variant: "primary",
200
+ onPress: onSave
201
+ }
202
+ )
203
+ ] });
74
204
  var IS_WEB = Platform.OS === "web";
75
205
  var TRANSITION_PROPERTY = "border-color, background-color, box-shadow";
76
206
  var TRANSITION_DURATION = "0.14s";
@@ -130,7 +260,8 @@ function useThemedInput(options = {}) {
130
260
  const backgroundColor = isFocused ? colors.surface : colors.surfaceElevated;
131
261
  const base = { color: colors.text, borderColor, backgroundColor };
132
262
  if (!IS_WEB) return base;
133
- const ring = isFocused && !hasError ? `0 0 0 ${FOCUS_RING_WIDTH}px ${withAlpha(primary, FOCUS_RING_ALPHA)}` : "none";
263
+ const ringColor = hasError ? errorColor : primary;
264
+ const ring = isFocused ? `0 0 0 ${FOCUS_RING_WIDTH}px ${withAlpha(ringColor, FOCUS_RING_ALPHA)}` : "none";
134
265
  const webStyle = {
135
266
  transitionProperty: TRANSITION_PROPERTY,
136
267
  // Collapse the eased transition to instant when the user prefers reduced motion.
@@ -166,7 +297,7 @@ function useThemedInput(options = {}) {
166
297
  };
167
298
  }
168
299
  var IS_WEB2 = Platform.OS === "web";
169
- var ThemedTextInput = React3.forwardRef(function ThemedTextInput2({ hasError = false, describedById, requiredField = false, onFocus, onBlur, style, placeholderTextColor, ...rest }, ref) {
300
+ var ThemedTextInput = React.forwardRef(function ThemedTextInput2({ hasError = false, describedById, requiredField = false, onFocus, onBlur, style, placeholderTextColor, ...rest }, ref) {
170
301
  const {
171
302
  style: themedStyle,
172
303
  placeholderTextColor: themedPlaceholder,
@@ -196,7 +327,7 @@ var INPUT_PADDING_V = 10;
196
327
  var INPUT_PADDING_H = 12;
197
328
  var INPUT_BORDER_RADIUS = 8;
198
329
  var INPUT_BORDER_WIDTH = 1;
199
- var styles2 = StyleSheet.create({
330
+ var styles3 = StyleSheet.create({
200
331
  input: {
201
332
  paddingVertical: INPUT_PADDING_V,
202
333
  paddingHorizontal: INPUT_PADDING_H,
@@ -218,7 +349,7 @@ var FormField = ({
218
349
  describedById,
219
350
  hasError,
220
351
  requiredField: required,
221
- style: styles2.input,
352
+ style: styles3.input,
222
353
  testID: "form-field-input",
223
354
  ...textInputProps
224
355
  }
@@ -238,6 +369,14 @@ var RGB_R_END = 2;
238
369
  var RGB_G_END = 4;
239
370
  var RGB_B_END = 6;
240
371
  var CHIP_HIT_SLOP = { top: 10, bottom: 10, left: 4, right: 4 };
372
+ var CHIP_GUTTER = 8;
373
+ var DEFAULT_CHIP_TESTID_PREFIX = "chip-selector-chip";
374
+ var ON_BRAND_SCALE = "onBrand";
375
+ var MAIN_STEP = "500";
376
+ function onBrandInk(palette) {
377
+ const scales = palette;
378
+ return scales[ON_BRAND_SCALE]?.[MAIN_STEP] ?? WHITE_COLOR;
379
+ }
241
380
  function withAlpha2(hex, alpha) {
242
381
  const normalized = hex.replace("#", "");
243
382
  const isShort = normalized.length === HEX_SHORT_LENGTH2;
@@ -248,18 +387,18 @@ function withAlpha2(hex, alpha) {
248
387
  const isValid = !Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b);
249
388
  return isValid ? `rgba(${r}, ${g}, ${b}, ${alpha})` : hex;
250
389
  }
251
- var styles3 = StyleSheet.create({
252
- label: {
253
- marginBottom: 8,
254
- fontWeight: "600"
255
- },
390
+ var styles4 = StyleSheet.create({
256
391
  chipContainer: {
257
392
  flexDirection: "row",
258
- flexWrap: "wrap"
393
+ flexWrap: "wrap",
394
+ // Each chip's bottom gutter also hangs off the LAST row, stacking on `Field`'s container
395
+ // margin (8 + 16 = 24) and dropping the block below the `FormField` beside it. Cancelling it
396
+ // is standard gutter compensation: inner rows keep their 8px, the block ends at Field's 16.
397
+ marginBottom: -CHIP_GUTTER
259
398
  },
260
399
  chipWrapper: {
261
- marginRight: 8,
262
- marginBottom: 8
400
+ marginRight: CHIP_GUTTER,
401
+ marginBottom: CHIP_GUTTER
263
402
  },
264
403
  chip: {
265
404
  paddingHorizontal: 12,
@@ -277,14 +416,26 @@ var styles3 = StyleSheet.create({
277
416
  chipText: {},
278
417
  chipTextOutline: { fontSize: OUTLINE_FONT_SIZE }
279
418
  });
419
+ function chipIdentity(option, selected, config) {
420
+ return {
421
+ testID: `${config.prefix}-${String(option.value)}`,
422
+ // The English default is retained verbatim: erevna/katalogos assert the exact string
423
+ // `Selects Red`. Consumers that localize pass `optionAccessibilityHint`.
424
+ accessibilityHint: config.hint ?? `Selects ${option.label}`,
425
+ ariaPressed: config.multiple ? selected : void 0
426
+ };
427
+ }
280
428
  function OutlineChip({
281
429
  option,
282
430
  selected,
283
431
  disabled,
284
432
  colors,
433
+ testID,
434
+ accessibilityHint,
435
+ ariaPressed,
285
436
  onPress
286
437
  }) {
287
- const [hovered, setHovered] = React3.useState(false);
438
+ const [hovered, setHovered] = React.useState(false);
288
439
  const active = selected || hovered;
289
440
  const borderColor = active ? colors.primary : colors.border;
290
441
  const backgroundColor = selected ? colors.tint : colors.muted;
@@ -292,18 +443,19 @@ function OutlineChip({
292
443
  return /* @__PURE__ */ jsx(
293
444
  Pressable,
294
445
  {
295
- accessibilityHint: `Selects ${option.label}`,
446
+ accessibilityHint,
296
447
  accessibilityLabel: option.label,
297
448
  accessibilityRole: "button",
298
449
  accessibilityState: { selected, disabled },
450
+ "aria-pressed": ariaPressed,
299
451
  disabled,
300
452
  hitSlop: CHIP_HIT_SLOP,
301
- style: styles3.chipWrapper,
302
- testID: `chip-selector-chip-${String(option.value)}`,
453
+ style: styles4.chipWrapper,
454
+ testID,
303
455
  onHoverIn: () => setHovered(true),
304
456
  onHoverOut: () => setHovered(false),
305
457
  onPress,
306
- children: /* @__PURE__ */ jsx(View, { style: [styles3.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles3.chipText, styles3.chipTextOutline, { color: textColor }], children: option.label }) })
458
+ children: /* @__PURE__ */ jsx(View, { style: [styles4.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles4.chipText, styles4.chipTextOutline, { color: textColor }], children: option.label }) })
307
459
  }
308
460
  );
309
461
  }
@@ -312,26 +464,35 @@ function SolidChip({
312
464
  selected,
313
465
  disabled,
314
466
  colors,
467
+ testID,
468
+ accessibilityHint,
469
+ ariaPressed,
315
470
  onPress
316
471
  }) {
317
472
  const backgroundColor = selected ? colors.primary : TRANSPARENT_COLOR;
318
- const textColor = selected ? WHITE_COLOR : colors.textStrong;
473
+ const textColor = selected ? colors.onPrimary : colors.textStrong;
319
474
  return /* @__PURE__ */ jsx(
320
475
  TouchableOpacity,
321
476
  {
322
- accessibilityHint: `Selects ${option.label}`,
477
+ accessibilityHint,
323
478
  accessibilityLabel: option.label,
324
479
  accessibilityRole: "button",
325
480
  accessibilityState: { selected, disabled },
481
+ "aria-pressed": ariaPressed,
326
482
  disabled,
327
483
  hitSlop: CHIP_HIT_SLOP,
328
- style: styles3.chipWrapper,
329
- testID: `chip-selector-chip-${String(option.value)}`,
484
+ style: styles4.chipWrapper,
485
+ testID,
330
486
  onPress,
331
- children: /* @__PURE__ */ jsx(View, { style: [styles3.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles3.chipText, { color: textColor }], children: option.label }) })
487
+ children: /* @__PURE__ */ jsx(View, { style: [styles4.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles4.chipText, { color: textColor }], children: option.label }) })
332
488
  }
333
489
  );
334
490
  }
491
+ var IS_WEB3 = Platform.OS === "web";
492
+ function groupA11yProps(describedById, hasError) {
493
+ if (!IS_WEB3) return {};
494
+ return { "aria-invalid": hasError ? true : void 0, "aria-describedby": describedById };
495
+ }
335
496
  var ChipSelector = ({
336
497
  label,
337
498
  options,
@@ -340,59 +501,53 @@ var ChipSelector = ({
340
501
  multiple = false,
341
502
  disabled = false,
342
503
  variant = "solid",
343
- containerStyle
504
+ containerStyle,
505
+ required = false,
506
+ error,
507
+ testID,
508
+ chipTestIDPrefix = DEFAULT_CHIP_TESTID_PREFIX,
509
+ optionAccessibilityHint
344
510
  }) => {
345
511
  const { theme } = useUi();
346
512
  const { colors, palette } = theme;
347
- const hasLabel = typeof label === "string" && label !== "";
348
- const chipColors = React3.useMemo(() => {
513
+ const chipColors = React.useMemo(() => {
349
514
  const primary = palette.primary["500"];
350
515
  return {
351
516
  border: colors.border,
352
517
  textStrong: colors.text,
353
518
  textMuted: colors.textSecondary,
354
519
  primary,
520
+ onPrimary: onBrandInk(palette),
355
521
  muted: colors.background,
356
522
  tint: withAlpha2(primary, OUTLINE_TINT_ALPHA)
357
523
  };
358
- }, [colors.border, colors.text, colors.textSecondary, colors.background, palette.primary]);
359
- const labelStyle = React3.useMemo(() => ({ color: colors.text }), [colors.text]);
524
+ }, [colors.border, colors.text, colors.textSecondary, colors.background, palette]);
360
525
  function isSelected(optionValue) {
361
526
  if (multiple && Array.isArray(value))
362
527
  return value.includes(optionValue);
363
528
  return value === optionValue;
364
529
  }
365
- return /* @__PURE__ */ jsxs(View, { style: containerStyle, children: [
366
- hasLabel ? /* @__PURE__ */ jsx(Text, { style: [styles3.label, labelStyle], children: label }) : null,
367
- /* @__PURE__ */ jsx(View, { style: styles3.chipContainer, children: options.map((option) => {
368
- const selected = isSelected(option.value);
369
- const onPress = () => onChange(option.value);
370
- return variant === "outline" ? /* @__PURE__ */ jsx(
371
- OutlineChip,
372
- {
373
- colors: chipColors,
374
- disabled,
375
- option,
376
- selected,
377
- onPress
378
- },
379
- String(option.value)
380
- ) : /* @__PURE__ */ jsx(
381
- SolidChip,
382
- {
383
- colors: chipColors,
384
- disabled,
385
- option,
386
- selected,
387
- onPress
388
- },
389
- String(option.value)
390
- );
391
- }) })
392
- ] });
530
+ function renderChip(option) {
531
+ const selected = isSelected(option.value);
532
+ const chipProps = {
533
+ colors: chipColors,
534
+ disabled,
535
+ option,
536
+ selected,
537
+ ...chipIdentity(option, selected, {
538
+ prefix: chipTestIDPrefix,
539
+ hint: optionAccessibilityHint,
540
+ multiple
541
+ }),
542
+ onPress: () => onChange(option.value)
543
+ };
544
+ const key = String(option.value);
545
+ return variant === "outline" ? /* @__PURE__ */ jsx(OutlineChip, { ...chipProps }, key) : /* @__PURE__ */ jsx(SolidChip, { ...chipProps }, key);
546
+ }
547
+ return /* @__PURE__ */ jsx(Field, { containerStyle, error, label, required, testID, children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(View, { style: styles4.chipContainer, ...groupA11yProps(describedById, hasError), children: options.map(renderChip) }) });
393
548
  };
394
549
  var DESCRIPTION_FONT_SIZE = 12;
395
- var styles4 = StyleSheet.create({
550
+ var styles5 = StyleSheet.create({
396
551
  container: {
397
552
  flexDirection: "row",
398
553
  justifyContent: "space-between",
@@ -423,7 +578,7 @@ var FormSwitch = ({
423
578
  const { theme } = useUi();
424
579
  const { colors } = theme;
425
580
  const hasDescription = typeof description === "string" && description !== "";
426
- const themeStyles = React3.useMemo(() => {
581
+ const themeStyles = React.useMemo(() => {
427
582
  const labelStyle = { color: colors.text };
428
583
  const descriptionStyle = { color: colors.textSecondary };
429
584
  return { label: labelStyle, description: descriptionStyle };
@@ -432,13 +587,13 @@ var FormSwitch = ({
432
587
  View,
433
588
  {
434
589
  style: [
435
- styles4.container,
590
+ styles5.container,
436
591
  containerStyle
437
592
  ],
438
593
  children: [
439
- /* @__PURE__ */ jsxs(View, { style: styles4.textColumn, children: [
440
- /* @__PURE__ */ jsx(Text, { style: [styles4.label, themeStyles.label], children: label }),
441
- hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles4.description, themeStyles.description], children: description }) : null
594
+ /* @__PURE__ */ jsxs(View, { style: styles5.textColumn, children: [
595
+ /* @__PURE__ */ jsx(Text, { style: [styles5.label, themeStyles.label], children: label }),
596
+ hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles5.description, themeStyles.description], children: description }) : null
442
597
  ] }),
443
598
  /* @__PURE__ */ jsx(
444
599
  Switch,
@@ -458,12 +613,12 @@ var FormSwitch = ({
458
613
  );
459
614
  };
460
615
  var ACTIVATION_KEYS = ["Enter", " ", "Spacebar"];
461
- var HINT_FONT_SIZE = 12;
616
+ var HINT_FONT_SIZE2 = 12;
462
617
  var BOX_SIZE = 20;
463
618
  var BOX_RADIUS = 5;
464
619
  var CHECK_FONT_SIZE = 14;
465
620
  var DISABLED_OPACITY = 0.5;
466
- var styles5 = StyleSheet.create({
621
+ var styles6 = StyleSheet.create({
467
622
  container: {
468
623
  flexDirection: "row",
469
624
  alignItems: "flex-start",
@@ -492,7 +647,7 @@ var styles5 = StyleSheet.create({
492
647
  fontWeight: "600"
493
648
  },
494
649
  hint: {
495
- fontSize: HINT_FONT_SIZE,
650
+ fontSize: HINT_FONT_SIZE2,
496
651
  marginTop: 2
497
652
  }
498
653
  });
@@ -514,7 +669,7 @@ var FormCheckbox = ({
514
669
  const brand = palette.primary["500"];
515
670
  const hintText = typeof hint === "string" && hint !== "" ? hint : description;
516
671
  const hasHint = typeof hintText === "string" && hintText !== "";
517
- const themeStyles = React3.useMemo(() => {
672
+ const themeStyles = React.useMemo(() => {
518
673
  const boxOn = { backgroundColor: brand, borderColor: brand };
519
674
  const boxOff = { backgroundColor: "transparent", borderColor: colors.border };
520
675
  const labelStyle = { color: colors.text };
@@ -545,20 +700,20 @@ var FormCheckbox = ({
545
700
  disabled,
546
701
  onPress: toggle,
547
702
  role: "checkbox",
548
- style: [styles5.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
703
+ style: [styles6.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
549
704
  testID,
550
705
  ...keyboardProps,
551
706
  children: [
552
- /* @__PURE__ */ jsx(View, { style: [styles5.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsx(Text, { style: [styles5.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
553
- /* @__PURE__ */ jsxs(View, { style: styles5.textColumn, children: [
554
- /* @__PURE__ */ jsx(Text, { style: [styles5.label, themeStyles.label], children: label }),
555
- hasHint ? /* @__PURE__ */ jsx(Text, { style: [styles5.hint, themeStyles.hint], children: hintText }) : null
707
+ /* @__PURE__ */ jsx(View, { style: [styles6.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsx(Text, { style: [styles6.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
708
+ /* @__PURE__ */ jsxs(View, { style: styles6.textColumn, children: [
709
+ /* @__PURE__ */ jsx(Text, { style: [styles6.label, themeStyles.label], children: label }),
710
+ hasHint ? /* @__PURE__ */ jsx(Text, { style: [styles6.hint, themeStyles.hint], children: hintText }) : null
556
711
  ] })
557
712
  ]
558
713
  }
559
714
  );
560
715
  };
561
716
 
562
- export { ChipSelector, Field, FormCheckbox, FormField, FormSwitch, ThemedTextInput, useThemedInput };
717
+ export { ChipSelector, DEFAULT_OPTIONAL_LABEL, Field, FormActions, FormCheckbox, FormField, FormSwitch, ThemedTextInput, useThemedInput };
563
718
  //# sourceMappingURL=index.mjs.map
564
719
  //# sourceMappingURL=index.mjs.map