@dloizides/ui-forms 1.13.4 → 1.14.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,10 +1,10 @@
1
- import React2, { useState, useCallback, useMemo, useRef, useEffect, useLayoutEffect } from 'react';
2
- import { Platform, StyleSheet, TextInput, View, Text, Switch, Pressable, ScrollView, TouchableOpacity } from 'react-native';
1
+ import React2, { useState, useCallback, useMemo, useEffect, useRef, useLayoutEffect } from 'react';
2
+ import { Platform, StyleSheet, TextInput, View, Text, ScrollView, Pressable, Switch, 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';
7
6
  import { createPortal } from 'react-dom';
7
+ import { MIN_TOUCH_TARGET_PX } from '@dloizides/a11y';
8
8
 
9
9
  // src/Field/Field.tsx
10
10
  function attachControlId(child, candidateId, describedById) {
@@ -459,6 +459,142 @@ var controlStyles = StyleSheet.create({
459
459
  error: { fontSize: ERROR_FONT, marginTop: ERROR_GAP }
460
460
  });
461
461
  var IS_WEB3 = Platform.OS === "web";
462
+ var MENU_PORTAL_Z_INDEX = 1e3;
463
+ var MENU_TOP_GAP2 = 4;
464
+ function toDomNode(ref) {
465
+ const node = ref.current;
466
+ if (node === null || typeof node.getBoundingClientRect !== "function") return null;
467
+ return node;
468
+ }
469
+ function readAnchorRect(ref) {
470
+ if (!IS_WEB3) return null;
471
+ const node = toDomNode(ref);
472
+ if (node === null) return null;
473
+ const r = node.getBoundingClientRect();
474
+ return { top: r.top, left: r.left, width: r.width, bottom: r.bottom };
475
+ }
476
+ function buildPortalPopoverStyle(rect) {
477
+ return {
478
+ // RN's ViewStyle union omits 'fixed', but react-native-web honours it at runtime.
479
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- web-only position value
480
+ position: "fixed",
481
+ top: rect.bottom + MENU_TOP_GAP2,
482
+ left: rect.left,
483
+ width: rect.width,
484
+ zIndex: MENU_PORTAL_Z_INDEX
485
+ };
486
+ }
487
+ function useAnchorRect(ref, enabled) {
488
+ const [rect, setRect] = useState(null);
489
+ useLayoutEffect(() => {
490
+ if (!IS_WEB3 || !enabled) {
491
+ setRect(null);
492
+ return void 0;
493
+ }
494
+ let frame = null;
495
+ let disposed = false;
496
+ const apply = () => {
497
+ frame = null;
498
+ if (disposed) return;
499
+ setRect(readAnchorRect(ref));
500
+ };
501
+ const schedule = () => {
502
+ if (frame !== null) return;
503
+ frame = window.requestAnimationFrame(apply);
504
+ };
505
+ apply();
506
+ document.addEventListener("scroll", schedule, true);
507
+ window.addEventListener("resize", schedule);
508
+ const node = toDomNode(ref);
509
+ const observer = typeof ResizeObserver === "function" ? new ResizeObserver(schedule) : null;
510
+ if (observer !== null) {
511
+ if (node !== null) observer.observe(node);
512
+ observer.observe(document.body);
513
+ }
514
+ return () => {
515
+ disposed = true;
516
+ if (frame !== null) window.cancelAnimationFrame(frame);
517
+ document.removeEventListener("scroll", schedule, true);
518
+ window.removeEventListener("resize", schedule);
519
+ if (observer !== null) observer.disconnect();
520
+ };
521
+ }, [ref, enabled]);
522
+ return IS_WEB3 && enabled ? rect : null;
523
+ }
524
+ var IS_WEB4 = typeof document !== "undefined";
525
+ var ESCAPE_KEY = "Escape";
526
+ var ACTIVE_WEIGHT = "700";
527
+ var IDLE_WEIGHT = "400";
528
+ function useDismiss(anchorRef, menuRef, onDismiss) {
529
+ useEffect(() => {
530
+ if (!IS_WEB4) return void 0;
531
+ const onMouseDown = (event) => {
532
+ const anchorNode = anchorRef.current;
533
+ const menuNode = menuRef.current;
534
+ const target = event.target instanceof Node ? event.target : null;
535
+ const insideAnchor = anchorNode !== null && target !== null && anchorNode.contains(target);
536
+ const insideMenu = menuNode !== null && target !== null && menuNode.contains(target);
537
+ if (!insideAnchor && !insideMenu) onDismiss();
538
+ };
539
+ const onKeyDown = (event) => {
540
+ if (event.key === ESCAPE_KEY) onDismiss();
541
+ };
542
+ document.addEventListener("mousedown", onMouseDown);
543
+ document.addEventListener("keydown", onKeyDown);
544
+ return () => {
545
+ document.removeEventListener("mousedown", onMouseDown);
546
+ document.removeEventListener("keydown", onKeyDown);
547
+ };
548
+ }, [anchorRef, menuRef, onDismiss]);
549
+ }
550
+ function AnchoredMenu({
551
+ options,
552
+ selectedValue,
553
+ onSelect,
554
+ onDismiss,
555
+ colors,
556
+ optionHint,
557
+ accessibilityLabel,
558
+ testID,
559
+ anchorRef,
560
+ usePortal = true
561
+ }) {
562
+ const menuRef = React2.useRef(null);
563
+ useDismiss(anchorRef, menuRef, onDismiss);
564
+ const rect = useAnchorRect(anchorRef, usePortal);
565
+ const portalStyle = rect !== null ? buildPortalPopoverStyle(rect) : null;
566
+ const menu = /* @__PURE__ */ jsx(
567
+ View,
568
+ {
569
+ ref: menuRef,
570
+ accessibilityLabel,
571
+ accessibilityRole: "menu",
572
+ style: [controlStyles.menu, { borderColor: colors.border, backgroundColor: colors.surface }, portalStyle],
573
+ testID: `${testID}-menu`,
574
+ children: /* @__PURE__ */ jsx(ScrollView, { keyboardShouldPersistTaps: "handled", nestedScrollEnabled: true, children: options.map((opt) => {
575
+ const active = opt.value === selectedValue;
576
+ return /* @__PURE__ */ jsx(
577
+ Pressable,
578
+ {
579
+ accessibilityRole: "menuitem",
580
+ accessibilityLabel: opt.label,
581
+ accessibilityHint: optionHint,
582
+ accessibilityState: { selected: active },
583
+ "aria-selected": active,
584
+ onPress: () => onSelect(opt.value),
585
+ style: controlStyles.option,
586
+ testID: `${testID}-option-${opt.value}`,
587
+ children: /* @__PURE__ */ jsx(Text, { style: [controlStyles.optionText, { color: active ? colors.brand : colors.text, fontWeight: active ? ACTIVE_WEIGHT : IDLE_WEIGHT }], children: opt.label })
588
+ },
589
+ opt.value
590
+ );
591
+ }) })
592
+ }
593
+ );
594
+ if (usePortal && IS_WEB4) return createPortal(menu, document.body);
595
+ return menu;
596
+ }
597
+ var IS_WEB5 = Platform.OS === "web";
462
598
  var TRANSITION_PROPERTY = "border-color, background-color, box-shadow";
463
599
  var TRANSITION_DURATION = "0.14s";
464
600
  var TRANSITION_TIMING = "ease";
@@ -474,7 +610,7 @@ var GREEN_START = 3;
474
610
  var BLUE_START = 5;
475
611
  var CHANNEL_END_OFFSET = 2;
476
612
  function prefersReducedMotion() {
477
- if (!IS_WEB3 || typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
613
+ if (!IS_WEB5 || typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
478
614
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
479
615
  }
480
616
  function withAlpha(color, alpha) {
@@ -517,7 +653,7 @@ function useThemedInput(options = {}) {
517
653
  const borderColor = isFocused ? focusBorder : isHovered ? withAlpha(colors.text, HOVER_BORDER_ALPHA) : restBorder;
518
654
  const backgroundColor = isFocused ? colors.surface : colors.surfaceElevated;
519
655
  const base = { color: colors.text, borderColor, backgroundColor };
520
- if (!IS_WEB3) return base;
656
+ if (!IS_WEB5) return base;
521
657
  const ringColor = hasError ? errorColor : primary;
522
658
  const ring = isFocused ? `0 0 0 ${FOCUS_RING_WIDTH}px ${withAlpha(ringColor, FOCUS_RING_ALPHA)}` : "none";
523
659
  const webStyle = {
@@ -542,7 +678,7 @@ function useThemedInput(options = {}) {
542
678
  ]);
543
679
  const focusBind = useMemo(() => ({ onFocus: handleFocus, onBlur: handleBlur }), [handleFocus, handleBlur]);
544
680
  const hoverBind = useMemo(
545
- () => IS_WEB3 ? { onMouseEnter: handleHoverIn, onMouseLeave: handleHoverOut } : {},
681
+ () => IS_WEB5 ? { onMouseEnter: handleHoverIn, onMouseLeave: handleHoverOut } : {},
546
682
  [handleHoverIn, handleHoverOut]
547
683
  );
548
684
  return {
@@ -554,9 +690,9 @@ function useThemedInput(options = {}) {
554
690
  hoverBind
555
691
  };
556
692
  }
557
- var IS_WEB4 = Platform.OS === "web";
693
+ var IS_WEB6 = Platform.OS === "web";
558
694
  function webFieldA11y({ describedById, hasError = false, required = false }) {
559
- if (!IS_WEB4) {
695
+ if (!IS_WEB6) {
560
696
  return {};
561
697
  }
562
698
  return {
@@ -606,12 +742,187 @@ var ThemedTextInput = React2.forwardRef(function ThemedTextInput2({
606
742
  }
607
743
  );
608
744
  });
745
+
746
+ // src/useRecentValues/recentValuesCore.ts
747
+ var STORAGE_PREFIX = "@dloizides/ui-forms:recent:";
748
+ var DEFAULT_MAX_RECENTS = 10;
749
+ function recentStorageKey(key) {
750
+ return `${STORAGE_PREFIX}${key}`;
751
+ }
752
+ function getStorage() {
753
+ try {
754
+ if (typeof window === "undefined") return null;
755
+ const storage = window.localStorage;
756
+ return storage !== null && storage !== void 0 ? storage : null;
757
+ } catch {
758
+ return null;
759
+ }
760
+ }
761
+ function readRecents(key) {
762
+ const storage = getStorage();
763
+ if (storage === null) return [];
764
+ try {
765
+ const raw = storage.getItem(recentStorageKey(key));
766
+ if (raw === null) return [];
767
+ const parsed = JSON.parse(raw);
768
+ if (!Array.isArray(parsed)) return [];
769
+ return parsed.filter((entry) => typeof entry === "string");
770
+ } catch {
771
+ return [];
772
+ }
773
+ }
774
+ function writeRecents(key, values) {
775
+ const storage = getStorage();
776
+ if (storage === null) return;
777
+ try {
778
+ storage.setItem(recentStorageKey(key), JSON.stringify(values));
779
+ } catch {
780
+ }
781
+ }
782
+ function clearRecents(key) {
783
+ const storage = getStorage();
784
+ if (storage === null) return;
785
+ try {
786
+ storage.removeItem(recentStorageKey(key));
787
+ } catch {
788
+ }
789
+ }
790
+ function computeNextRecents(current, value, max) {
791
+ const trimmed = value.trim();
792
+ if (trimmed === "") return [...current];
793
+ const lower = trimmed.toLowerCase();
794
+ const withoutMatch = current.filter((entry) => entry.toLowerCase() !== lower);
795
+ const promoted = [trimmed, ...withoutMatch];
796
+ const cap = max > 0 ? max : 0;
797
+ return promoted.slice(0, cap);
798
+ }
799
+ function recentsEqual(a, b) {
800
+ if (a.length !== b.length) return false;
801
+ return a.every((entry, index) => entry === b[index]);
802
+ }
803
+
804
+ // src/useRecentValues/useRecentValues.ts
805
+ function useRecentValues(key, max = DEFAULT_MAX_RECENTS) {
806
+ const [recents, setRecents] = useState(() => readRecents(key).slice(0, max));
807
+ useEffect(() => {
808
+ setRecents(readRecents(key).slice(0, max));
809
+ }, [key, max]);
810
+ const remember = useCallback(
811
+ (value) => {
812
+ setRecents((current) => {
813
+ const next = computeNextRecents(current, value, max);
814
+ if (recentsEqual(next, current)) return current;
815
+ writeRecents(key, next);
816
+ return next;
817
+ });
818
+ },
819
+ [key, max]
820
+ );
821
+ const clear = useCallback(() => {
822
+ clearRecents(key);
823
+ setRecents([]);
824
+ }, [key]);
825
+ return { recents, remember, clear };
826
+ }
827
+ var CLOSE_DELAY_MS = 140;
828
+ function RecentInputField({
829
+ recentKey,
830
+ recentMax,
831
+ label,
832
+ value,
833
+ testID,
834
+ onChangeText,
835
+ onSubmitEditing,
836
+ onFocus,
837
+ onBlur,
838
+ ...rest
839
+ }) {
840
+ const { theme } = useUi();
841
+ const { colors, palette } = theme;
842
+ const { recents, remember } = useRecentValues(recentKey, recentMax);
843
+ const anchorRef = useRef(null);
844
+ const latestText = useRef(typeof value === "string" ? value : "");
845
+ const [isOpen, setIsOpen] = useState(false);
846
+ const handleChange = useCallback(
847
+ (text) => {
848
+ latestText.current = text;
849
+ onChangeText?.(text);
850
+ },
851
+ [onChangeText]
852
+ );
853
+ const handleFocus = useCallback(
854
+ (event) => {
855
+ setIsOpen(true);
856
+ onFocus?.(event);
857
+ },
858
+ [onFocus]
859
+ );
860
+ const handleBlur = useCallback(
861
+ (event) => {
862
+ if (latestText.current.trim() !== "") remember(latestText.current);
863
+ setTimeout(() => setIsOpen(false), CLOSE_DELAY_MS);
864
+ onBlur?.(event);
865
+ },
866
+ [remember, onBlur]
867
+ );
868
+ const handleSubmit = useCallback(
869
+ (event) => {
870
+ remember(latestText.current);
871
+ setIsOpen(false);
872
+ onSubmitEditing?.(event);
873
+ },
874
+ [remember, onSubmitEditing]
875
+ );
876
+ const pick = useCallback(
877
+ (picked) => {
878
+ latestText.current = picked;
879
+ onChangeText?.(picked);
880
+ remember(picked);
881
+ setIsOpen(false);
882
+ },
883
+ [onChangeText, remember]
884
+ );
885
+ const options = recents.map((entry) => ({ label: entry, value: entry }));
886
+ const showMenu = isOpen && options.length > 0;
887
+ const recentTestID = testID !== void 0 ? `${testID}-recent` : "form-field-recent";
888
+ return /* @__PURE__ */ jsxs(View, { ref: anchorRef, style: controlStyles.anchor, children: [
889
+ /* @__PURE__ */ jsx(
890
+ ThemedTextInput,
891
+ {
892
+ ...rest,
893
+ testID,
894
+ value,
895
+ onChangeText: handleChange,
896
+ onFocus: handleFocus,
897
+ onBlur: handleBlur,
898
+ onSubmitEditing: handleSubmit
899
+ }
900
+ ),
901
+ showMenu ? /* @__PURE__ */ jsx(
902
+ AnchoredMenu,
903
+ {
904
+ accessibilityLabel: `Recent ${label} values`,
905
+ anchorRef,
906
+ colors: { text: colors.text, border: colors.border, surface: colors.surface, brand: palette.primary["500"] },
907
+ onDismiss: () => setIsOpen(false),
908
+ onSelect: pick,
909
+ optionHint: `Fill ${label} with this recent value`,
910
+ options,
911
+ selectedValue: typeof value === "string" ? value : "",
912
+ testID: recentTestID,
913
+ usePortal: true
914
+ }
915
+ ) : null
916
+ ] });
917
+ }
609
918
  var FormField = ({
610
919
  label,
611
920
  labelHidden = false,
612
921
  required = false,
613
922
  error,
614
923
  containerStyle,
924
+ recentKey,
925
+ recentMax,
615
926
  ...textInputProps
616
927
  }) => /* @__PURE__ */ jsx(
617
928
  Field,
@@ -620,9 +931,8 @@ var FormField = ({
620
931
  error,
621
932
  label: labelHidden ? void 0 : label,
622
933
  required,
623
- children: ({ describedById, hasError, controlId }) => /* @__PURE__ */ jsx(
624
- ThemedTextInput,
625
- {
934
+ children: ({ describedById, hasError, controlId }) => {
935
+ const inputProps = {
626
936
  accessibilityHint: `Enter ${label}`,
627
937
  accessibilityLabel: label,
628
938
  describedById,
@@ -631,8 +941,9 @@ var FormField = ({
631
941
  requiredField: required,
632
942
  testID: "form-field-input",
633
943
  ...textInputProps
634
- }
635
- )
944
+ };
945
+ return recentKey !== void 0 && recentKey !== "" ? /* @__PURE__ */ jsx(RecentInputField, { label, recentKey, recentMax, ...inputProps }) : /* @__PURE__ */ jsx(ThemedTextInput, { ...inputProps });
946
+ }
636
947
  }
637
948
  );
638
949
  var TRANSPARENT_COLOR = "transparent";
@@ -1003,140 +1314,6 @@ var FormCheckbox = ({
1003
1314
  }
1004
1315
  );
1005
1316
  };
1006
- var IS_WEB5 = Platform.OS === "web";
1007
- var MENU_PORTAL_Z_INDEX = 1e3;
1008
- var MENU_TOP_GAP2 = 4;
1009
- function toDomNode(ref) {
1010
- const node = ref.current;
1011
- if (node === null || typeof node.getBoundingClientRect !== "function") return null;
1012
- return node;
1013
- }
1014
- function readAnchorRect(ref) {
1015
- if (!IS_WEB5) return null;
1016
- const node = toDomNode(ref);
1017
- if (node === null) return null;
1018
- const r = node.getBoundingClientRect();
1019
- return { top: r.top, left: r.left, width: r.width, bottom: r.bottom };
1020
- }
1021
- function buildPortalPopoverStyle(rect) {
1022
- return {
1023
- // RN's ViewStyle union omits 'fixed', but react-native-web honours it at runtime.
1024
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- web-only position value
1025
- position: "fixed",
1026
- top: rect.bottom + MENU_TOP_GAP2,
1027
- left: rect.left,
1028
- width: rect.width,
1029
- zIndex: MENU_PORTAL_Z_INDEX
1030
- };
1031
- }
1032
- function useAnchorRect(ref, enabled) {
1033
- const [rect, setRect] = useState(null);
1034
- useLayoutEffect(() => {
1035
- if (!IS_WEB5 || !enabled) {
1036
- setRect(null);
1037
- return void 0;
1038
- }
1039
- let frame = null;
1040
- let disposed = false;
1041
- const apply = () => {
1042
- frame = null;
1043
- if (disposed) return;
1044
- setRect(readAnchorRect(ref));
1045
- };
1046
- const schedule = () => {
1047
- if (frame !== null) return;
1048
- frame = window.requestAnimationFrame(apply);
1049
- };
1050
- apply();
1051
- document.addEventListener("scroll", schedule, true);
1052
- window.addEventListener("resize", schedule);
1053
- const node = toDomNode(ref);
1054
- const observer = typeof ResizeObserver === "function" ? new ResizeObserver(schedule) : null;
1055
- if (observer !== null) {
1056
- if (node !== null) observer.observe(node);
1057
- observer.observe(document.body);
1058
- }
1059
- return () => {
1060
- disposed = true;
1061
- if (frame !== null) window.cancelAnimationFrame(frame);
1062
- document.removeEventListener("scroll", schedule, true);
1063
- window.removeEventListener("resize", schedule);
1064
- if (observer !== null) observer.disconnect();
1065
- };
1066
- }, [ref, enabled]);
1067
- return IS_WEB5 && enabled ? rect : null;
1068
- }
1069
- var IS_WEB6 = typeof document !== "undefined";
1070
- var ESCAPE_KEY = "Escape";
1071
- var ACTIVE_WEIGHT = "700";
1072
- var IDLE_WEIGHT = "400";
1073
- function useDismiss(anchorRef, menuRef, onDismiss) {
1074
- useEffect(() => {
1075
- if (!IS_WEB6) return void 0;
1076
- const onMouseDown = (event) => {
1077
- const anchorNode = anchorRef.current;
1078
- const menuNode = menuRef.current;
1079
- const target = event.target instanceof Node ? event.target : null;
1080
- const insideAnchor = anchorNode !== null && target !== null && anchorNode.contains(target);
1081
- const insideMenu = menuNode !== null && target !== null && menuNode.contains(target);
1082
- if (!insideAnchor && !insideMenu) onDismiss();
1083
- };
1084
- const onKeyDown = (event) => {
1085
- if (event.key === ESCAPE_KEY) onDismiss();
1086
- };
1087
- document.addEventListener("mousedown", onMouseDown);
1088
- document.addEventListener("keydown", onKeyDown);
1089
- return () => {
1090
- document.removeEventListener("mousedown", onMouseDown);
1091
- document.removeEventListener("keydown", onKeyDown);
1092
- };
1093
- }, [anchorRef, menuRef, onDismiss]);
1094
- }
1095
- function AnchoredMenu({
1096
- options,
1097
- selectedValue,
1098
- onSelect,
1099
- onDismiss,
1100
- colors,
1101
- optionHint,
1102
- testID,
1103
- anchorRef,
1104
- usePortal = true
1105
- }) {
1106
- const menuRef = React2.useRef(null);
1107
- useDismiss(anchorRef, menuRef, onDismiss);
1108
- const rect = useAnchorRect(anchorRef, usePortal);
1109
- const portalStyle = rect !== null ? buildPortalPopoverStyle(rect) : null;
1110
- const menu = /* @__PURE__ */ jsx(
1111
- View,
1112
- {
1113
- ref: menuRef,
1114
- accessibilityRole: "menu",
1115
- style: [controlStyles.menu, { borderColor: colors.border, backgroundColor: colors.surface }, portalStyle],
1116
- testID: `${testID}-menu`,
1117
- children: /* @__PURE__ */ jsx(ScrollView, { keyboardShouldPersistTaps: "handled", nestedScrollEnabled: true, children: options.map((opt) => {
1118
- const active = opt.value === selectedValue;
1119
- return /* @__PURE__ */ jsx(
1120
- Pressable,
1121
- {
1122
- accessibilityRole: "menuitem",
1123
- accessibilityLabel: opt.label,
1124
- accessibilityHint: optionHint,
1125
- accessibilityState: { selected: active },
1126
- "aria-selected": active,
1127
- onPress: () => onSelect(opt.value),
1128
- style: controlStyles.option,
1129
- testID: `${testID}-option-${opt.value}`,
1130
- children: /* @__PURE__ */ jsx(Text, { style: [controlStyles.optionText, { color: active ? colors.brand : colors.text, fontWeight: active ? ACTIVE_WEIGHT : IDLE_WEIGHT }], children: opt.label })
1131
- },
1132
- opt.value
1133
- );
1134
- }) })
1135
- }
1136
- );
1137
- if (usePortal && IS_WEB6) return createPortal(menu, document.body);
1138
- return menu;
1139
- }
1140
1317
  var CHEVRON_DOWN = "\u25BE";
1141
1318
  var CHEVRON_UP = "\u25B4";
1142
1319
  function SelectControl({
@@ -1238,7 +1415,7 @@ function isUnmatched(options, text) {
1238
1415
  if (needle === "") return false;
1239
1416
  return !options.some((o) => o.value.toLowerCase() === needle || o.label.toLowerCase() === needle);
1240
1417
  }
1241
- var CLOSE_DELAY_MS = 140;
1418
+ var CLOSE_DELAY_MS2 = 140;
1242
1419
  var DEFAULT_TYPEAHEAD_MIN_CHARS = 1;
1243
1420
  var DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS = 8;
1244
1421
  function TypeaheadControl({
@@ -1282,7 +1459,7 @@ function TypeaheadControl({
1282
1459
  [options, onChange]
1283
1460
  );
1284
1461
  const handleBlur = useCallback(() => {
1285
- setTimeout(() => setIsOpen(false), CLOSE_DELAY_MS);
1462
+ setTimeout(() => setIsOpen(false), CLOSE_DELAY_MS2);
1286
1463
  }, []);
1287
1464
  const handleSubmit = useCallback(() => {
1288
1465
  setIsOpen(false);
@@ -1399,6 +1576,6 @@ function DateRangeControl({
1399
1576
  ] });
1400
1577
  }
1401
1578
 
1402
- export { AnchoredMenu, ChipSelector, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, Field, FormActions, FormCell, FormCheckbox, FormField, FormGrid, FormSection, FormSwitch, SelectControl, ThemedTextInput, TypeaheadControl, controlStyles, isUnmatched, suggestOptions, useThemedInput };
1579
+ export { AnchoredMenu, ChipSelector, DEFAULT_MAX_RECENTS, DEFAULT_OPTIONAL_LABEL, DEFAULT_TYPEAHEAD_MAX_SUGGESTIONS, DEFAULT_TYPEAHEAD_MIN_CHARS, DateRangeControl, Field, FormActions, FormCell, FormCheckbox, FormField, FormGrid, FormSection, FormSwitch, SelectControl, ThemedTextInput, TypeaheadControl, controlStyles, isUnmatched, suggestOptions, useRecentValues, useThemedInput };
1403
1580
  //# sourceMappingURL=index.mjs.map
1404
1581
  //# sourceMappingURL=index.mjs.map