@dloizides/ui-forms 1.9.1 → 1.12.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,19 +1,57 @@
1
- import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
2
- import { StyleSheet, Platform, TextInput, View, Text, Switch, Pressable, TouchableOpacity } from 'react-native';
1
+ import React2, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
2
+ import { Platform, StyleSheet, 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
5
  import { Button } from '@dloizides/ui-buttons';
6
+ import { MIN_TOUCH_TARGET_PX } from '@dloizides/a11y';
6
7
 
7
8
  // src/Field/Field.tsx
9
+ function attachControlId(child, candidateId, describedById) {
10
+ if (!React2.isValidElement(child) || child.type === React2.Fragment) {
11
+ return { node: child };
12
+ }
13
+ const props = child.props;
14
+ const existing = props.id ?? props.nativeID;
15
+ const element = child;
16
+ const controlId = existing !== void 0 && existing !== "" ? existing : candidateId;
17
+ const injected = { id: controlId };
18
+ if (describedById !== void 0 && props.describedById === void 0) {
19
+ injected.describedById = describedById;
20
+ }
21
+ return { node: React2.cloneElement(element, injected), controlId };
22
+ }
23
+ var IS_WEB = Platform.OS === "web";
24
+ function isAttributeHost(node) {
25
+ return typeof node === "object" && node !== null && typeof node.setAttribute === "function";
26
+ }
27
+ function useHtmlFor(controlId) {
28
+ const ref = useRef(null);
29
+ useEffect(() => {
30
+ const node = ref.current;
31
+ if (!IS_WEB || !isAttributeHost(node)) return;
32
+ const target = controlId !== void 0 && controlId !== "" ? node.ownerDocument?.getElementById(controlId) : null;
33
+ if (target === null || target === void 0) {
34
+ node.removeAttribute("for");
35
+ return;
36
+ }
37
+ node.setAttribute("for", controlId);
38
+ }, [controlId]);
39
+ return ref;
40
+ }
41
+ var IS_WEB2 = Platform.OS === "web";
8
42
  function FieldLabel({
9
43
  label,
10
44
  marker,
11
45
  optionalLabel,
46
+ controlId,
47
+ labelId,
12
48
  style,
13
49
  requiredMarkStyle,
14
50
  optionalMarkStyle
15
51
  }) {
16
- return /* @__PURE__ */ jsxs(Text, { style, children: [
52
+ const webProps = IS_WEB2 ? { role: "label" } : {};
53
+ const labelRef = useHtmlFor(controlId);
54
+ return /* @__PURE__ */ jsxs(Text, { id: labelId, ref: labelRef, style, ...webProps, children: [
17
55
  label,
18
56
  " ",
19
57
  marker === "required" ? (
@@ -90,15 +128,15 @@ var labelVariantStyles = StyleSheet.create({
90
128
  function resolveLabelVariantStyle(variant) {
91
129
  return variant === "control" ? labelVariantStyles.control : labelVariantStyles.field;
92
130
  }
93
- var FormGridContext = React.createContext(null);
94
- var GapOwnedContext = React.createContext(false);
131
+ var FormGridContext = React2.createContext(null);
132
+ var GapOwnedContext = React2.createContext(false);
95
133
  var FormGridProvider = FormGridContext.Provider;
96
134
  var GapOwnedProvider = GapOwnedContext.Provider;
97
135
  function useFormGridMetrics() {
98
- return React.useContext(FormGridContext);
136
+ return React2.useContext(FormGridContext);
99
137
  }
100
138
  function useIsGapOwned() {
101
- return React.useContext(GapOwnedContext);
139
+ return React2.useContext(GapOwnedContext);
102
140
  }
103
141
  var ERROR_FONT_SIZE = 12;
104
142
  var HINT_FONT_SIZE = 12;
@@ -116,10 +154,15 @@ var styles = StyleSheet.create({
116
154
  });
117
155
  var fieldSeq = 0;
118
156
  function useFieldIds() {
119
- const [ids] = React.useState(() => {
157
+ const [ids] = React2.useState(() => {
120
158
  fieldSeq += 1;
121
159
  const seq = String(fieldSeq);
122
- return { hintId: `field-hint-${seq}`, errorId: `field-error-${seq}` };
160
+ return {
161
+ hintId: `field-hint-${seq}`,
162
+ errorId: `field-error-${seq}`,
163
+ controlId: `field-control-${seq}`,
164
+ labelId: `field-label-${seq}`
165
+ };
123
166
  });
124
167
  return ids;
125
168
  }
@@ -157,8 +200,8 @@ var Field = ({
157
200
  const labelText = label ?? "";
158
201
  const hasLabel = labelText !== "";
159
202
  const marker = resolveFieldMarker(required, optional);
160
- const { hintId, errorId } = useFieldIds();
161
- const themeStyles = React.useMemo(
203
+ const { hintId, errorId, controlId, labelId } = useFieldIds();
204
+ const themeStyles = React2.useMemo(
162
205
  () => ({
163
206
  label: { color: colors.textSecondary },
164
207
  requiredMark: { color: errorColor },
@@ -170,12 +213,16 @@ var Field = ({
170
213
  [colors.textSecondary, errorColor]
171
214
  );
172
215
  const describedById = joinDescribedBy(hasHint ? hintId : void 0, hasError ? errorId : void 0);
173
- const control = typeof children === "function" ? children({ describedById, hasError }) : children;
216
+ const isRenderChild = typeof children === "function";
217
+ const attached = isRenderChild ? { node: children({ describedById, hasError, controlId, labelId }), controlId } : attachControlId(children, controlId, describedById);
218
+ const control = attached.node;
174
219
  return /* @__PURE__ */ jsxs(View, { style: [spacingStyles.container, containerStyle], testID, children: [
175
220
  hasLabel ? /* @__PURE__ */ jsx(
176
221
  FieldLabel,
177
222
  {
223
+ controlId: attached.controlId,
178
224
  label: labelText,
225
+ labelId,
179
226
  marker,
180
227
  optionalLabel,
181
228
  optionalMarkStyle: [styles.optionalMark, themeStyles.optionalMark],
@@ -205,7 +252,7 @@ var FormGrid = ({
205
252
  style,
206
253
  testID
207
254
  }) => {
208
- const metrics = React.useMemo(() => ({ minWidth, basis }), [minWidth, basis]);
255
+ const metrics = React2.useMemo(() => ({ minWidth, basis }), [minWidth, basis]);
209
256
  return /* @__PURE__ */ jsx(FormGridProvider, { value: metrics, children: /* @__PURE__ */ jsx(GapOwnedProvider, { value: true, children: /* @__PURE__ */ jsx(View, { style: [styles2.grid, { gap }, style], testID, children }) }) });
210
257
  };
211
258
  var GROW = 1;
@@ -240,7 +287,7 @@ var styles3 = StyleSheet.create({
240
287
  });
241
288
  var sectionSeq = 0;
242
289
  function useSectionLegendId() {
243
- const [id] = React.useState(() => {
290
+ const [id] = React2.useState(() => {
244
291
  sectionSeq += 1;
245
292
  return `form-section-legend-${String(sectionSeq)}`;
246
293
  });
@@ -327,7 +374,90 @@ var FormActions = ({
327
374
  }
328
375
  )
329
376
  ] });
330
- var IS_WEB = Platform.OS === "web";
377
+ var FIELD_GAP = 4;
378
+ var LABEL_FONT = 11;
379
+ var LABEL_LETTER_SPACING = 0.4;
380
+ var INPUT_RADIUS = 8;
381
+ var INPUT_PAD_H = 12;
382
+ var INPUT_PAD_V = 10;
383
+ var INPUT_FONT = 14;
384
+ var INPUT_BORDER = 1;
385
+ var DATE_ROW_GAP = 8;
386
+ var CHEVRON_FONT = 10;
387
+ var MENU_TOP_GAP = 4;
388
+ var MENU_RADIUS = 8;
389
+ var MENU_PAD_V = 4;
390
+ var MENU_OPT_PAD_H = 12;
391
+ var MENU_OPT_PAD_V = 8;
392
+ var MENU_MIN_WIDTH = 160;
393
+ var MENU_MAX_HEIGHT = 260;
394
+ var MENU_Z = 1e3;
395
+ var MENU_ELEVATION = 8;
396
+ var MENU_BOX_SHADOW = "0px 2px 8px rgba(0, 0, 0, 0.15)";
397
+ var OPTION_FONT = 14;
398
+ var ERROR_FONT = 12;
399
+ var ERROR_GAP = 3;
400
+ var controlStyles = StyleSheet.create({
401
+ /**
402
+ * The shared bordered box every dense text control renders in.
403
+ *
404
+ * `minHeight` is the kit floor: padding alone produced a ~38px box, and a bare input with no
405
+ * box style at all measured **19px** in production. Height is a hit-target guarantee, so it
406
+ * is stated rather than left to emerge from font size + padding.
407
+ */
408
+ input: {
409
+ borderWidth: INPUT_BORDER,
410
+ borderRadius: INPUT_RADIUS,
411
+ paddingHorizontal: INPUT_PAD_H,
412
+ paddingVertical: INPUT_PAD_V,
413
+ fontSize: INPUT_FONT,
414
+ minHeight: MIN_TOUCH_TARGET_PX
415
+ },
416
+ /** The select trigger: a bordered field box matching the text inputs + a chevron. */
417
+ selectTrigger: {
418
+ minHeight: MIN_TOUCH_TARGET_PX,
419
+ flexDirection: "row",
420
+ alignItems: "center",
421
+ justifyContent: "space-between",
422
+ gap: DATE_ROW_GAP,
423
+ borderWidth: INPUT_BORDER,
424
+ borderRadius: INPUT_RADIUS,
425
+ paddingHorizontal: INPUT_PAD_H,
426
+ paddingVertical: INPUT_PAD_V
427
+ },
428
+ selectTriggerText: { fontSize: INPUT_FONT, flexShrink: 1 },
429
+ chevron: { fontSize: CHEVRON_FONT },
430
+ dateRow: { flexDirection: "row", gap: DATE_ROW_GAP },
431
+ dateCol: { gap: FIELD_GAP, flex: 1 },
432
+ /**
433
+ * The micro-caption above a sub-control (a date range's From/To) and beside a switch. Note it
434
+ * is deliberately NOT the `control` label voice: it carries no `700`/uppercase, because it
435
+ * names a HALF of a control that already has a `control`-voice label above it. Uppercasing it
436
+ * too would make one field shout twice.
437
+ */
438
+ subLabel: { fontSize: LABEL_FONT, letterSpacing: LABEL_LETTER_SPACING },
439
+ /** Positioning context for a floating `AnchoredMenu` (`position:absolute; top:100%`). */
440
+ anchor: { position: "relative" },
441
+ menu: {
442
+ position: "absolute",
443
+ top: "100%",
444
+ left: 0,
445
+ marginTop: MENU_TOP_GAP,
446
+ minWidth: MENU_MIN_WIDTH,
447
+ maxHeight: MENU_MAX_HEIGHT,
448
+ borderWidth: INPUT_BORDER,
449
+ borderRadius: MENU_RADIUS,
450
+ paddingVertical: MENU_PAD_V,
451
+ zIndex: MENU_Z,
452
+ boxShadow: MENU_BOX_SHADOW,
453
+ elevation: MENU_ELEVATION,
454
+ overflow: "hidden"
455
+ },
456
+ option: { paddingHorizontal: MENU_OPT_PAD_H, paddingVertical: MENU_OPT_PAD_V },
457
+ optionText: { fontSize: OPTION_FONT },
458
+ error: { fontSize: ERROR_FONT, marginTop: ERROR_GAP }
459
+ });
460
+ var IS_WEB3 = Platform.OS === "web";
331
461
  var TRANSITION_PROPERTY = "border-color, background-color, box-shadow";
332
462
  var TRANSITION_DURATION = "0.14s";
333
463
  var TRANSITION_TIMING = "ease";
@@ -342,7 +472,7 @@ var GREEN_START = 3;
342
472
  var BLUE_START = 5;
343
473
  var CHANNEL_END_OFFSET = 2;
344
474
  function prefersReducedMotion() {
345
- if (!IS_WEB || typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
475
+ if (!IS_WEB3 || typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
346
476
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
347
477
  }
348
478
  function withAlpha(color, alpha) {
@@ -385,7 +515,7 @@ function useThemedInput(options = {}) {
385
515
  const borderColor = isFocused ? focusBorder : isHovered ? withAlpha(colors.text, HOVER_BORDER_ALPHA) : restBorder;
386
516
  const backgroundColor = isFocused ? colors.surface : colors.surfaceElevated;
387
517
  const base = { color: colors.text, borderColor, backgroundColor };
388
- if (!IS_WEB) return base;
518
+ if (!IS_WEB3) return base;
389
519
  const ringColor = hasError ? errorColor : primary;
390
520
  const ring = isFocused ? `0 0 0 ${FOCUS_RING_WIDTH}px ${withAlpha(ringColor, FOCUS_RING_ALPHA)}` : "none";
391
521
  const webStyle = {
@@ -410,7 +540,7 @@ function useThemedInput(options = {}) {
410
540
  ]);
411
541
  const focusBind = useMemo(() => ({ onFocus: handleFocus, onBlur: handleBlur }), [handleFocus, handleBlur]);
412
542
  const hoverBind = useMemo(
413
- () => IS_WEB ? { onMouseEnter: handleHoverIn, onMouseLeave: handleHoverOut } : {},
543
+ () => IS_WEB3 ? { onMouseEnter: handleHoverIn, onMouseLeave: handleHoverOut } : {},
414
544
  [handleHoverIn, handleHoverOut]
415
545
  );
416
546
  return {
@@ -422,9 +552,9 @@ function useThemedInput(options = {}) {
422
552
  hoverBind
423
553
  };
424
554
  }
425
- var IS_WEB2 = Platform.OS === "web";
555
+ var IS_WEB4 = Platform.OS === "web";
426
556
  function webFieldA11y({ describedById, hasError = false, required = false }) {
427
- if (!IS_WEB2) {
557
+ if (!IS_WEB4) {
428
558
  return {};
429
559
  }
430
560
  return {
@@ -433,7 +563,24 @@ function webFieldA11y({ describedById, hasError = false, required = false }) {
433
563
  "aria-required": required ? true : void 0
434
564
  };
435
565
  }
436
- var ThemedTextInput = React.forwardRef(function ThemedTextInput2({ hasError = false, describedById, requiredField = false, onFocus, onBlur, style, placeholderTextColor, ...rest }, ref) {
566
+ var MULTILINE_MIN_HEIGHT = 96;
567
+ var boxStyles = StyleSheet.create({
568
+ multiline: {
569
+ minHeight: MULTILINE_MIN_HEIGHT,
570
+ textAlignVertical: "top"
571
+ }
572
+ });
573
+ var ThemedTextInput = React2.forwardRef(function ThemedTextInput2({
574
+ hasError = false,
575
+ describedById,
576
+ requiredField = false,
577
+ unstyledBox = false,
578
+ onFocus,
579
+ onBlur,
580
+ style,
581
+ placeholderTextColor,
582
+ ...rest
583
+ }, ref) {
437
584
  const {
438
585
  style: themedStyle,
439
586
  placeholderTextColor: themedPlaceholder,
@@ -441,6 +588,8 @@ var ThemedTextInput = React.forwardRef(function ThemedTextInput2({ hasError = fa
441
588
  hoverBind
442
589
  } = useThemedInput({ hasError, onFocus, onBlur });
443
590
  const webA11y = webFieldA11y({ describedById, hasError, required: requiredField });
591
+ const boxStyle = unstyledBox ? null : controlStyles.input;
592
+ const multilineStyle = rest.multiline === true && !unstyledBox ? boxStyles.multiline : null;
444
593
  return /* @__PURE__ */ jsx(
445
594
  TextInput,
446
595
  {
@@ -451,41 +600,39 @@ var ThemedTextInput = React.forwardRef(function ThemedTextInput2({ hasError = fa
451
600
  ...hoverBind,
452
601
  onFocus: focusBind.onFocus,
453
602
  onBlur: focusBind.onBlur,
454
- style: [style, themedStyle]
603
+ style: [boxStyle, multilineStyle, style, themedStyle]
455
604
  }
456
605
  );
457
606
  });
458
- var INPUT_PADDING_V = 10;
459
- var INPUT_PADDING_H = 12;
460
- var INPUT_BORDER_RADIUS = 8;
461
- var INPUT_BORDER_WIDTH = 1;
462
- var styles5 = StyleSheet.create({
463
- input: {
464
- paddingVertical: INPUT_PADDING_V,
465
- paddingHorizontal: INPUT_PADDING_H,
466
- borderRadius: INPUT_BORDER_RADIUS,
467
- borderWidth: INPUT_BORDER_WIDTH
468
- }
469
- });
470
607
  var FormField = ({
471
608
  label,
609
+ labelHidden = false,
472
610
  required = false,
473
611
  error,
474
612
  containerStyle,
475
613
  ...textInputProps
476
- }) => /* @__PURE__ */ jsx(Field, { containerStyle, error, label, required, children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(
477
- ThemedTextInput,
614
+ }) => /* @__PURE__ */ jsx(
615
+ Field,
478
616
  {
479
- accessibilityHint: `Enter ${label}`,
480
- accessibilityLabel: label,
481
- describedById,
482
- hasError,
483
- requiredField: required,
484
- style: styles5.input,
485
- testID: "form-field-input",
486
- ...textInputProps
617
+ containerStyle,
618
+ error,
619
+ label: labelHidden ? void 0 : label,
620
+ required,
621
+ children: ({ describedById, hasError, controlId }) => /* @__PURE__ */ jsx(
622
+ ThemedTextInput,
623
+ {
624
+ accessibilityHint: `Enter ${label}`,
625
+ accessibilityLabel: label,
626
+ describedById,
627
+ hasError,
628
+ id: controlId,
629
+ requiredField: required,
630
+ testID: "form-field-input",
631
+ ...textInputProps
632
+ }
633
+ )
487
634
  }
488
- ) });
635
+ );
489
636
  var TRANSPARENT_COLOR = "transparent";
490
637
  var WHITE_COLOR = "#fff";
491
638
  var SOLID_RADIUS = 20;
@@ -519,7 +666,7 @@ function withAlpha2(hex, alpha) {
519
666
  const isValid = !Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b);
520
667
  return isValid ? `rgba(${r}, ${g}, ${b}, ${alpha})` : hex;
521
668
  }
522
- var styles6 = StyleSheet.create({
669
+ var styles5 = StyleSheet.create({
523
670
  chipContainer: {
524
671
  flexDirection: "row",
525
672
  flexWrap: "wrap",
@@ -571,7 +718,7 @@ function OutlineChip({
571
718
  ariaPressed,
572
719
  onPress
573
720
  }) {
574
- const [hovered, setHovered] = React.useState(false);
721
+ const [hovered, setHovered] = React2.useState(false);
575
722
  const active = selected || hovered;
576
723
  const borderColor = active ? colors.primary : colors.border;
577
724
  const backgroundColor = selected ? colors.tint : colors.muted;
@@ -590,7 +737,7 @@ function OutlineChip({
590
737
  onHoverIn: () => setHovered(true),
591
738
  onHoverOut: () => setHovered(false),
592
739
  onPress,
593
- children: /* @__PURE__ */ jsx(View, { style: [styles6.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles6.chipText, styles6.chipTextOutline, { color: textColor }], children: option.label }) })
740
+ children: /* @__PURE__ */ jsx(View, { style: [styles5.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles5.chipText, styles5.chipTextOutline, { color: textColor }], children: option.label }) })
594
741
  }
595
742
  );
596
743
  }
@@ -618,7 +765,7 @@ function SolidChip({
618
765
  hitSlop: CHIP_HIT_SLOP,
619
766
  testID,
620
767
  onPress,
621
- children: /* @__PURE__ */ jsx(View, { style: [styles6.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles6.chipText, { color: textColor }], children: option.label }) })
768
+ children: /* @__PURE__ */ jsx(View, { style: [styles5.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles5.chipText, { color: textColor }], children: option.label }) })
622
769
  }
623
770
  );
624
771
  }
@@ -640,7 +787,7 @@ var ChipSelector = ({
640
787
  }) => {
641
788
  const { theme } = useUi();
642
789
  const { colors, palette } = theme;
643
- const chipColors = React.useMemo(() => {
790
+ const chipColors = React2.useMemo(() => {
644
791
  const primary = palette.primary["500"];
645
792
  return {
646
793
  border: colors.border,
@@ -683,12 +830,12 @@ var ChipSelector = ({
683
830
  required,
684
831
  spacing,
685
832
  testID,
686
- children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(View, { style: styles6.chipContainer, ...webFieldA11y({ describedById, hasError }), children: options.map(renderChip) })
833
+ children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(View, { style: styles5.chipContainer, ...webFieldA11y({ describedById, hasError }), children: options.map(renderChip) })
687
834
  }
688
835
  );
689
836
  };
690
837
  var DESCRIPTION_FONT_SIZE2 = 12;
691
- var styles7 = StyleSheet.create({
838
+ var styles6 = StyleSheet.create({
692
839
  container: {
693
840
  flexDirection: "row",
694
841
  justifyContent: "space-between",
@@ -719,7 +866,7 @@ var FormSwitch = ({
719
866
  const { theme } = useUi();
720
867
  const { colors } = theme;
721
868
  const hasDescription = typeof description === "string" && description !== "";
722
- const themeStyles = React.useMemo(() => {
869
+ const themeStyles = React2.useMemo(() => {
723
870
  const labelStyle = { color: colors.text };
724
871
  const descriptionStyle = { color: colors.textSecondary };
725
872
  return { label: labelStyle, description: descriptionStyle };
@@ -728,13 +875,13 @@ var FormSwitch = ({
728
875
  View,
729
876
  {
730
877
  style: [
731
- styles7.container,
878
+ styles6.container,
732
879
  containerStyle
733
880
  ],
734
881
  children: [
735
- /* @__PURE__ */ jsxs(View, { style: styles7.textColumn, children: [
736
- /* @__PURE__ */ jsx(Text, { style: [styles7.label, themeStyles.label], children: label }),
737
- hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles7.description, themeStyles.description], children: description }) : null
882
+ /* @__PURE__ */ jsxs(View, { style: styles6.textColumn, children: [
883
+ /* @__PURE__ */ jsx(Text, { style: [styles6.label, themeStyles.label], children: label }),
884
+ hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles6.description, themeStyles.description], children: description }) : null
738
885
  ] }),
739
886
  /* @__PURE__ */ jsx(
740
887
  Switch,
@@ -759,7 +906,7 @@ var BOX_SIZE = 20;
759
906
  var BOX_RADIUS = 5;
760
907
  var CHECK_FONT_SIZE = 14;
761
908
  var DISABLED_OPACITY = 0.5;
762
- var styles8 = StyleSheet.create({
909
+ var styles7 = StyleSheet.create({
763
910
  container: {
764
911
  flexDirection: "row",
765
912
  alignItems: "flex-start",
@@ -810,7 +957,7 @@ var FormCheckbox = ({
810
957
  const brand = palette.primary["500"];
811
958
  const hintText = typeof hint === "string" && hint !== "" ? hint : description;
812
959
  const hasHint = typeof hintText === "string" && hintText !== "";
813
- const themeStyles = React.useMemo(() => {
960
+ const themeStyles = React2.useMemo(() => {
814
961
  const boxOn = { backgroundColor: brand, borderColor: brand };
815
962
  const boxOff = { backgroundColor: "transparent", borderColor: colors.border };
816
963
  const labelStyle = { color: colors.text };
@@ -841,101 +988,26 @@ var FormCheckbox = ({
841
988
  disabled,
842
989
  onPress: toggle,
843
990
  role: "checkbox",
844
- style: [styles8.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
991
+ style: [styles7.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
845
992
  testID,
846
993
  ...keyboardProps,
847
994
  children: [
848
- /* @__PURE__ */ jsx(View, { style: [styles8.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsx(Text, { style: [styles8.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
849
- /* @__PURE__ */ jsxs(View, { style: styles8.textColumn, children: [
850
- /* @__PURE__ */ jsx(Text, { style: [styles8.label, themeStyles.label], children: label }),
851
- hasHint ? /* @__PURE__ */ jsx(Text, { style: [styles8.hint, themeStyles.hint], children: hintText }) : null
995
+ /* @__PURE__ */ jsx(View, { style: [styles7.box, value ? themeStyles.boxOn : themeStyles.boxOff], children: value ? /* @__PURE__ */ jsx(Text, { style: [styles7.check, { color: colors.surface }], accessibilityElementsHidden: true, children: "\u2713" }) : null }),
996
+ /* @__PURE__ */ jsxs(View, { style: styles7.textColumn, children: [
997
+ /* @__PURE__ */ jsx(Text, { style: [styles7.label, themeStyles.label], children: label }),
998
+ hasHint ? /* @__PURE__ */ jsx(Text, { style: [styles7.hint, themeStyles.hint], children: hintText }) : null
852
999
  ] })
853
1000
  ]
854
1001
  }
855
1002
  );
856
1003
  };
857
- var FIELD_GAP = 4;
858
- var LABEL_FONT = 11;
859
- var LABEL_LETTER_SPACING = 0.4;
860
- var INPUT_RADIUS = 8;
861
- var INPUT_PAD_H = 12;
862
- var INPUT_PAD_V = 10;
863
- var INPUT_FONT = 14;
864
- var INPUT_BORDER = 1;
865
- var DATE_ROW_GAP = 8;
866
- var CHEVRON_FONT = 10;
867
- var MENU_TOP_GAP = 4;
868
- var MENU_RADIUS = 8;
869
- var MENU_PAD_V = 4;
870
- var MENU_OPT_PAD_H = 12;
871
- var MENU_OPT_PAD_V = 8;
872
- var MENU_MIN_WIDTH = 160;
873
- var MENU_MAX_HEIGHT = 260;
874
- var MENU_Z = 1e3;
875
- var MENU_ELEVATION = 8;
876
- var MENU_BOX_SHADOW = "0px 2px 8px rgba(0, 0, 0, 0.15)";
877
- var OPTION_FONT = 14;
878
- var ERROR_FONT = 12;
879
- var ERROR_GAP = 3;
880
- var controlStyles = StyleSheet.create({
881
- /** The shared bordered box every dense text control renders in. */
882
- input: {
883
- borderWidth: INPUT_BORDER,
884
- borderRadius: INPUT_RADIUS,
885
- paddingHorizontal: INPUT_PAD_H,
886
- paddingVertical: INPUT_PAD_V,
887
- fontSize: INPUT_FONT
888
- },
889
- /** The select trigger: a bordered field box matching the text inputs + a chevron. */
890
- selectTrigger: {
891
- flexDirection: "row",
892
- alignItems: "center",
893
- justifyContent: "space-between",
894
- gap: DATE_ROW_GAP,
895
- borderWidth: INPUT_BORDER,
896
- borderRadius: INPUT_RADIUS,
897
- paddingHorizontal: INPUT_PAD_H,
898
- paddingVertical: INPUT_PAD_V
899
- },
900
- selectTriggerText: { fontSize: INPUT_FONT, flexShrink: 1 },
901
- chevron: { fontSize: CHEVRON_FONT },
902
- dateRow: { flexDirection: "row", gap: DATE_ROW_GAP },
903
- dateCol: { gap: FIELD_GAP, flex: 1 },
904
- /**
905
- * The micro-caption above a sub-control (a date range's From/To) and beside a switch. Note it
906
- * is deliberately NOT the `control` label voice: it carries no `700`/uppercase, because it
907
- * names a HALF of a control that already has a `control`-voice label above it. Uppercasing it
908
- * too would make one field shout twice.
909
- */
910
- subLabel: { fontSize: LABEL_FONT, letterSpacing: LABEL_LETTER_SPACING },
911
- /** Positioning context for a floating `AnchoredMenu` (`position:absolute; top:100%`). */
912
- anchor: { position: "relative" },
913
- menu: {
914
- position: "absolute",
915
- top: "100%",
916
- left: 0,
917
- marginTop: MENU_TOP_GAP,
918
- minWidth: MENU_MIN_WIDTH,
919
- maxHeight: MENU_MAX_HEIGHT,
920
- borderWidth: INPUT_BORDER,
921
- borderRadius: MENU_RADIUS,
922
- paddingVertical: MENU_PAD_V,
923
- zIndex: MENU_Z,
924
- boxShadow: MENU_BOX_SHADOW,
925
- elevation: MENU_ELEVATION,
926
- overflow: "hidden"
927
- },
928
- option: { paddingHorizontal: MENU_OPT_PAD_H, paddingVertical: MENU_OPT_PAD_V },
929
- optionText: { fontSize: OPTION_FONT },
930
- error: { fontSize: ERROR_FONT, marginTop: ERROR_GAP }
931
- });
932
- var IS_WEB3 = typeof document !== "undefined";
1004
+ var IS_WEB5 = typeof document !== "undefined";
933
1005
  var ESCAPE_KEY = "Escape";
934
1006
  var ACTIVE_WEIGHT = "700";
935
1007
  var IDLE_WEIGHT = "400";
936
1008
  function useDismiss(anchorRef, menuRef, onDismiss) {
937
1009
  useEffect(() => {
938
- if (!IS_WEB3) return void 0;
1010
+ if (!IS_WEB5) return void 0;
939
1011
  const onMouseDown = (event) => {
940
1012
  const anchorNode = anchorRef.current;
941
1013
  const menuNode = menuRef.current;
@@ -965,7 +1037,7 @@ function AnchoredMenu({
965
1037
  testID,
966
1038
  anchorRef
967
1039
  }) {
968
- const menuRef = React.useRef(null);
1040
+ const menuRef = React2.useRef(null);
969
1041
  useDismiss(anchorRef, menuRef, onDismiss);
970
1042
  return /* @__PURE__ */ jsx(
971
1043
  View,
@@ -1004,6 +1076,7 @@ function SelectControl({
1004
1076
  placeholder = "",
1005
1077
  accessibilityLabel,
1006
1078
  accessibilityHint,
1079
+ labelledById,
1007
1080
  optionHint = "",
1008
1081
  style,
1009
1082
  testID
@@ -1038,6 +1111,7 @@ function SelectControl({
1038
1111
  accessibilityHint,
1039
1112
  accessibilityState: { expanded: isOpen },
1040
1113
  "aria-expanded": isOpen,
1114
+ "aria-labelledby": labelledById,
1041
1115
  onPress: toggle,
1042
1116
  style: [controlStyles.selectTrigger, { borderColor: colors.border, backgroundColor: colors.surface }],
1043
1117
  testID: `${testID}-trigger`,