@dloizides/ui-forms 1.9.1 → 1.11.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.
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,37 +600,25 @@ 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,
472
609
  required = false,
473
610
  error,
474
611
  containerStyle,
475
612
  ...textInputProps
476
- }) => /* @__PURE__ */ jsx(Field, { containerStyle, error, label, required, children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(
613
+ }) => /* @__PURE__ */ jsx(Field, { containerStyle, error, label, required, children: ({ describedById, hasError, controlId }) => /* @__PURE__ */ jsx(
477
614
  ThemedTextInput,
478
615
  {
479
616
  accessibilityHint: `Enter ${label}`,
480
617
  accessibilityLabel: label,
481
618
  describedById,
482
619
  hasError,
620
+ id: controlId,
483
621
  requiredField: required,
484
- style: styles5.input,
485
622
  testID: "form-field-input",
486
623
  ...textInputProps
487
624
  }
@@ -519,7 +656,7 @@ function withAlpha2(hex, alpha) {
519
656
  const isValid = !Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b);
520
657
  return isValid ? `rgba(${r}, ${g}, ${b}, ${alpha})` : hex;
521
658
  }
522
- var styles6 = StyleSheet.create({
659
+ var styles5 = StyleSheet.create({
523
660
  chipContainer: {
524
661
  flexDirection: "row",
525
662
  flexWrap: "wrap",
@@ -571,7 +708,7 @@ function OutlineChip({
571
708
  ariaPressed,
572
709
  onPress
573
710
  }) {
574
- const [hovered, setHovered] = React.useState(false);
711
+ const [hovered, setHovered] = React2.useState(false);
575
712
  const active = selected || hovered;
576
713
  const borderColor = active ? colors.primary : colors.border;
577
714
  const backgroundColor = selected ? colors.tint : colors.muted;
@@ -590,7 +727,7 @@ function OutlineChip({
590
727
  onHoverIn: () => setHovered(true),
591
728
  onHoverOut: () => setHovered(false),
592
729
  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 }) })
730
+ children: /* @__PURE__ */ jsx(View, { style: [styles5.chipOutline, { borderColor, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles5.chipText, styles5.chipTextOutline, { color: textColor }], children: option.label }) })
594
731
  }
595
732
  );
596
733
  }
@@ -618,7 +755,7 @@ function SolidChip({
618
755
  hitSlop: CHIP_HIT_SLOP,
619
756
  testID,
620
757
  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 }) })
758
+ children: /* @__PURE__ */ jsx(View, { style: [styles5.chip, { borderColor: colors.border, backgroundColor }], children: /* @__PURE__ */ jsx(Text, { style: [styles5.chipText, { color: textColor }], children: option.label }) })
622
759
  }
623
760
  );
624
761
  }
@@ -640,7 +777,7 @@ var ChipSelector = ({
640
777
  }) => {
641
778
  const { theme } = useUi();
642
779
  const { colors, palette } = theme;
643
- const chipColors = React.useMemo(() => {
780
+ const chipColors = React2.useMemo(() => {
644
781
  const primary = palette.primary["500"];
645
782
  return {
646
783
  border: colors.border,
@@ -683,12 +820,12 @@ var ChipSelector = ({
683
820
  required,
684
821
  spacing,
685
822
  testID,
686
- children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(View, { style: styles6.chipContainer, ...webFieldA11y({ describedById, hasError }), children: options.map(renderChip) })
823
+ children: ({ describedById, hasError }) => /* @__PURE__ */ jsx(View, { style: styles5.chipContainer, ...webFieldA11y({ describedById, hasError }), children: options.map(renderChip) })
687
824
  }
688
825
  );
689
826
  };
690
827
  var DESCRIPTION_FONT_SIZE2 = 12;
691
- var styles7 = StyleSheet.create({
828
+ var styles6 = StyleSheet.create({
692
829
  container: {
693
830
  flexDirection: "row",
694
831
  justifyContent: "space-between",
@@ -719,7 +856,7 @@ var FormSwitch = ({
719
856
  const { theme } = useUi();
720
857
  const { colors } = theme;
721
858
  const hasDescription = typeof description === "string" && description !== "";
722
- const themeStyles = React.useMemo(() => {
859
+ const themeStyles = React2.useMemo(() => {
723
860
  const labelStyle = { color: colors.text };
724
861
  const descriptionStyle = { color: colors.textSecondary };
725
862
  return { label: labelStyle, description: descriptionStyle };
@@ -728,13 +865,13 @@ var FormSwitch = ({
728
865
  View,
729
866
  {
730
867
  style: [
731
- styles7.container,
868
+ styles6.container,
732
869
  containerStyle
733
870
  ],
734
871
  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
872
+ /* @__PURE__ */ jsxs(View, { style: styles6.textColumn, children: [
873
+ /* @__PURE__ */ jsx(Text, { style: [styles6.label, themeStyles.label], children: label }),
874
+ hasDescription ? /* @__PURE__ */ jsx(Text, { style: [styles6.description, themeStyles.description], children: description }) : null
738
875
  ] }),
739
876
  /* @__PURE__ */ jsx(
740
877
  Switch,
@@ -759,7 +896,7 @@ var BOX_SIZE = 20;
759
896
  var BOX_RADIUS = 5;
760
897
  var CHECK_FONT_SIZE = 14;
761
898
  var DISABLED_OPACITY = 0.5;
762
- var styles8 = StyleSheet.create({
899
+ var styles7 = StyleSheet.create({
763
900
  container: {
764
901
  flexDirection: "row",
765
902
  alignItems: "flex-start",
@@ -810,7 +947,7 @@ var FormCheckbox = ({
810
947
  const brand = palette.primary["500"];
811
948
  const hintText = typeof hint === "string" && hint !== "" ? hint : description;
812
949
  const hasHint = typeof hintText === "string" && hintText !== "";
813
- const themeStyles = React.useMemo(() => {
950
+ const themeStyles = React2.useMemo(() => {
814
951
  const boxOn = { backgroundColor: brand, borderColor: brand };
815
952
  const boxOff = { backgroundColor: "transparent", borderColor: colors.border };
816
953
  const labelStyle = { color: colors.text };
@@ -841,101 +978,26 @@ var FormCheckbox = ({
841
978
  disabled,
842
979
  onPress: toggle,
843
980
  role: "checkbox",
844
- style: [styles8.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
981
+ style: [styles7.container, containerStyle, disabled ? { opacity: DISABLED_OPACITY } : null],
845
982
  testID,
846
983
  ...keyboardProps,
847
984
  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
985
+ /* @__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 }),
986
+ /* @__PURE__ */ jsxs(View, { style: styles7.textColumn, children: [
987
+ /* @__PURE__ */ jsx(Text, { style: [styles7.label, themeStyles.label], children: label }),
988
+ hasHint ? /* @__PURE__ */ jsx(Text, { style: [styles7.hint, themeStyles.hint], children: hintText }) : null
852
989
  ] })
853
990
  ]
854
991
  }
855
992
  );
856
993
  };
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";
994
+ var IS_WEB5 = typeof document !== "undefined";
933
995
  var ESCAPE_KEY = "Escape";
934
996
  var ACTIVE_WEIGHT = "700";
935
997
  var IDLE_WEIGHT = "400";
936
998
  function useDismiss(anchorRef, menuRef, onDismiss) {
937
999
  useEffect(() => {
938
- if (!IS_WEB3) return void 0;
1000
+ if (!IS_WEB5) return void 0;
939
1001
  const onMouseDown = (event) => {
940
1002
  const anchorNode = anchorRef.current;
941
1003
  const menuNode = menuRef.current;
@@ -965,7 +1027,7 @@ function AnchoredMenu({
965
1027
  testID,
966
1028
  anchorRef
967
1029
  }) {
968
- const menuRef = React.useRef(null);
1030
+ const menuRef = React2.useRef(null);
969
1031
  useDismiss(anchorRef, menuRef, onDismiss);
970
1032
  return /* @__PURE__ */ jsx(
971
1033
  View,
@@ -1004,6 +1066,7 @@ function SelectControl({
1004
1066
  placeholder = "",
1005
1067
  accessibilityLabel,
1006
1068
  accessibilityHint,
1069
+ labelledById,
1007
1070
  optionHint = "",
1008
1071
  style,
1009
1072
  testID
@@ -1038,6 +1101,7 @@ function SelectControl({
1038
1101
  accessibilityHint,
1039
1102
  accessibilityState: { expanded: isOpen },
1040
1103
  "aria-expanded": isOpen,
1104
+ "aria-labelledby": labelledById,
1041
1105
  onPress: toggle,
1042
1106
  style: [controlStyles.selectTrigger, { borderColor: colors.border, backgroundColor: colors.surface }],
1043
1107
  testID: `${testID}-trigger`,