@vellira-ui/react-native 2.56.0 → 2.58.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.
Files changed (2) hide show
  1. package/dist/index.js +1079 -701
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Children, cloneElement, createContext, forwardRef, isValidElement, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
2
2
  import { Check, ChevronDown, Close, Search } from "@vellira-ui/icons";
3
- import { AccessibilityInfo, ActivityIndicator, Animated, Dimensions, Easing, FlatList, Modal as Modal$1, Platform, Pressable, ScrollView, StyleSheet, Text, TextInput, View, findNodeHandle, useWindowDimensions } from "react-native";
3
+ import { AccessibilityInfo, ActivityIndicator, Animated, BackHandler, Dimensions, Easing, FlatList, Modal as Modal$1, Platform, Pressable, ScrollView, StyleSheet, Text, TextInput, View, findNodeHandle, useWindowDimensions } from "react-native";
4
+ import { createConsoleOverlayDiagnostics, createOverlayZIndexPolicy, resolveOverlayZIndex } from "@vellira-ui/core";
4
5
  import { darkTheme, highContrastTheme, lightTheme } from "@vellira-ui/tokens";
5
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  //#region src/theme/fontWeight.ts
@@ -23,241 +24,264 @@ function toNativeFontWeight(weight) {
23
24
  return normalizedWeight;
24
25
  }
25
26
  //#endregion
26
- //#region src/managers/FloatingManager/computeFloatingPosition.ts
27
- function clamp(value, min, max) {
28
- return Math.min(Math.max(value, min), Math.max(min, max));
29
- }
30
- function parsePlacement(placement) {
31
- const [side, align = "center"] = placement.split("-");
32
- return {
33
- side,
34
- align
35
- };
36
- }
37
- function createPlacement(side, align) {
38
- return align === "center" ? side : `${side}-${align}`;
39
- }
40
- function getOppositeSide(side) {
41
- switch (side) {
42
- case "top": return "bottom";
43
- case "right": return "left";
44
- case "bottom": return "top";
45
- case "left": return "right";
46
- }
47
- }
48
- function computeBasePosition({ reference, floating, side, align, offset }) {
49
- const referenceCenterX = reference.x + reference.width / 2;
50
- const referenceCenterY = reference.y + reference.height / 2;
51
- const alignedLeft = align === "start" ? reference.x : align === "end" ? reference.x + reference.width - floating.width : referenceCenterX - floating.width / 2;
52
- const alignedTop = align === "start" ? reference.y : align === "end" ? reference.y + reference.height - floating.height : referenceCenterY - floating.height / 2;
53
- switch (side) {
54
- case "top": return {
55
- top: reference.y - floating.height - offset,
56
- left: alignedLeft
57
- };
58
- case "right": return {
59
- top: alignedTop,
60
- left: reference.x + reference.width + offset
61
- };
62
- case "bottom": return {
63
- top: reference.y + reference.height + offset,
64
- left: alignedLeft
65
- };
66
- case "left": return {
67
- top: alignedTop,
68
- left: reference.x - floating.width - offset
69
- };
70
- }
71
- }
72
- function getMainAxisOverflow({ side, position, floating, boundary, padding }) {
73
- switch (side) {
74
- case "top": return padding - position.top;
75
- case "right": return position.left + floating.width + padding - boundary.width;
76
- case "bottom": return position.top + floating.height + padding - boundary.height;
77
- case "left": return padding - position.left;
78
- }
79
- }
80
- function computeFloatingPosition({ reference, floating, boundary, placement, offset = 8, padding = 12, arrowPadding = 16, flip = true, shift = true }) {
81
- const parsed = parsePlacement(placement);
82
- let resolvedSide = parsed.side;
83
- let position = computeBasePosition({
84
- reference,
85
- floating,
86
- side: resolvedSide,
87
- align: parsed.align,
88
- offset
89
- });
90
- if (flip && getMainAxisOverflow({
91
- side: resolvedSide,
92
- position,
93
- floating,
94
- boundary,
95
- padding
96
- }) > 0) {
97
- const oppositeSide = getOppositeSide(resolvedSide);
98
- const oppositePosition = computeBasePosition({
99
- reference,
100
- floating,
101
- side: oppositeSide,
102
- align: parsed.align,
103
- offset
104
- });
105
- if (getMainAxisOverflow({
106
- side: oppositeSide,
107
- position: oppositePosition,
108
- floating,
109
- boundary,
110
- padding
111
- }) <= 0) {
112
- resolvedSide = oppositeSide;
113
- position = oppositePosition;
114
- }
115
- }
116
- if (shift) position = {
117
- top: clamp(position.top, padding, boundary.height - floating.height - padding),
118
- left: clamp(position.left, padding, boundary.width - floating.width - padding)
119
- };
120
- const referenceCenterX = reference.x + reference.width / 2;
121
- const referenceCenterY = reference.y + reference.height / 2;
122
- const arrowPosition = resolvedSide === "top" || resolvedSide === "bottom" ? { left: clamp(referenceCenterX - position.left, arrowPadding, floating.width - arrowPadding) } : { top: clamp(referenceCenterY - position.top, arrowPadding, floating.height - arrowPadding) };
27
+ //#region src/managers/OverlayManager/NativeOverlayManager.ts
28
+ const nativeOverlayZIndexPolicy = createOverlayZIndexPolicy({
29
+ defaultLevel: "modal",
30
+ levels: { modal: lightTheme.tokens.zIndex.modal }
31
+ });
32
+ const nativeOverlayDiagnostics = createConsoleOverlayDiagnostics("NativeOverlayManager");
33
+ const createNativeOverlayManager = () => {
34
+ let stack = [];
35
+ const dismissHandlers = /* @__PURE__ */ new Map();
36
+ const outsidePressHandlers = /* @__PURE__ */ new Map();
123
37
  return {
124
- position,
125
- arrowPosition,
126
- placement: createPlacement(resolvedSide, parsed.align)
127
- };
128
- }
129
- //#endregion
130
- //#region src/managers/FloatingManager/useNativeFloatingPosition.ts
131
- function useNativeFloatingPosition(placement = "top", offset = 8) {
132
- const [result, setResult] = useState({
133
- position: {
134
- top: 0,
135
- left: 0
38
+ register(id) {
39
+ if (stack.some((item) => item.id === id)) nativeOverlayDiagnostics.duplicateRegistration?.(id);
40
+ stack = stack.filter((item) => item.id !== id);
41
+ const entry = {
42
+ id,
43
+ zIndex: resolveOverlayZIndex({
44
+ level: nativeOverlayZIndexPolicy.defaultLevel,
45
+ order: stack.length,
46
+ policy: nativeOverlayZIndexPolicy
47
+ })
48
+ };
49
+ stack.push(entry);
50
+ return entry;
136
51
  },
137
- arrowPosition: {},
138
- placement
139
- });
140
- const floatingSizeRef = useRef({
141
- width: 0,
142
- height: 0
143
- });
144
- const lastTriggerRef = useRef(null);
145
- const lastContainerRef = useRef(null);
146
- const updatePosition = useCallback((triggerRef, containerRef, measuredSize = floatingSizeRef.current) => {
147
- lastTriggerRef.current = triggerRef;
148
- lastContainerRef.current = containerRef ?? null;
149
- const triggerNode = triggerRef.current;
150
- const containerNode = containerRef?.current;
151
- if (!triggerNode || typeof triggerNode.measureInWindow !== "function") {
152
- setResult((current) => ({
153
- ...current,
154
- position: {
155
- top: 0,
156
- left: 0
157
- }
158
- }));
159
- return;
160
- }
161
- triggerNode.measureInWindow((x, y, width, height) => {
162
- const commitPosition = (containerX, containerY, containerWidth, containerHeight) => {
163
- const nextResult = computeFloatingPosition({
164
- reference: {
165
- x: x - containerX,
166
- y: y - containerY,
167
- width,
168
- height
169
- },
170
- floating: measuredSize,
171
- boundary: {
172
- width: containerWidth,
173
- height: containerHeight
174
- },
175
- placement,
176
- offset
177
- });
178
- setResult(nextResult);
52
+ unregister(id) {
53
+ if (!stack.some((item) => item.id === id)) nativeOverlayDiagnostics.unknownUnregister?.(id);
54
+ dismissHandlers.delete(id);
55
+ outsidePressHandlers.delete(id);
56
+ stack = stack.filter((item) => item.id !== id);
57
+ },
58
+ isTop(id) {
59
+ return stack.at(-1)?.id === id;
60
+ },
61
+ getTop() {
62
+ return stack.at(-1);
63
+ },
64
+ getZIndex(id) {
65
+ return stack.find((item) => item.id === id)?.zIndex ?? nativeOverlayZIndexPolicy.levels[nativeOverlayZIndexPolicy.defaultLevel];
66
+ },
67
+ registerDismissHandler(id, handler) {
68
+ dismissHandlers.set(id, handler);
69
+ return () => {
70
+ if (dismissHandlers.get(id) !== handler) return;
71
+ dismissHandlers.delete(id);
179
72
  };
180
- if (!containerNode || typeof containerNode.measureInWindow !== "function") {
181
- const window = Dimensions.get("window");
182
- commitPosition(0, 0, window.width, window.height);
183
- return;
184
- }
185
- containerNode.measureInWindow((containerX, containerY, containerWidth, containerHeight) => {
186
- commitPosition(containerX, containerY, containerWidth, containerHeight);
187
- });
188
- });
189
- }, [placement, offset]);
190
- const onFloatingLayout = useCallback((event) => {
191
- const { width, height } = event.nativeEvent.layout;
192
- const nextSize = {
193
- width,
194
- height
195
- };
196
- floatingSizeRef.current = nextSize;
197
- if (lastTriggerRef.current) updatePosition(lastTriggerRef.current, lastContainerRef.current ?? void 0, nextSize);
198
- }, [updatePosition]);
199
- return {
200
- position: result.position,
201
- arrowPosition: result.arrowPosition,
202
- placement: result.placement,
203
- updatePosition,
204
- onFloatingLayout
73
+ },
74
+ registerOutsidePressHandler(id, handler) {
75
+ outsidePressHandlers.set(id, handler);
76
+ return () => {
77
+ if (outsidePressHandlers.get(id) !== handler) return;
78
+ outsidePressHandlers.delete(id);
79
+ };
80
+ },
81
+ dispatchTopDismiss() {
82
+ const top = this.getTop();
83
+ if (!top) return false;
84
+ const handler = dismissHandlers.get(top.id);
85
+ if (!handler) return false;
86
+ return handler();
87
+ },
88
+ dispatchTopOutsidePress() {
89
+ const top = this.getTop();
90
+ if (!top) return false;
91
+ const handler = outsidePressHandlers.get(top.id);
92
+ if (!handler) return false;
93
+ return handler();
94
+ },
95
+ clear() {
96
+ stack = [];
97
+ dismissHandlers.clear();
98
+ outsidePressHandlers.clear();
99
+ }
205
100
  };
206
- }
207
- //#endregion
208
- //#region src/managers/OverlayStack/NativeOverlayStack.ts
209
- let stack = [];
210
- const nativeOverlayStackStore = {
211
- add(id) {
212
- stack = stack.filter((item) => item !== id);
213
- stack.push(id);
214
- },
215
- remove(id) {
216
- stack = stack.filter((item) => item !== id);
217
- },
218
- isTop(id) {
219
- return stack[stack.length - 1] === id;
220
- }
221
101
  };
102
+ const nativeOverlayManager = createNativeOverlayManager();
222
103
  //#endregion
223
- //#region src/managers/OverlayStack/useNativeOverlayStack.ts
224
- const useNativeOverlayStack = ({ id, visible }) => {
104
+ //#region src/managers/OverlayManager/OverlayManagerProvider.tsx
105
+ const NativeOverlayManagerContext = createContext(null);
106
+ const useNativeOverlayManager = () => useContext(NativeOverlayManagerContext) ?? nativeOverlayManager;
107
+ //#endregion
108
+ //#region src/hooks/behavior/overlay/useOverlayRegistration.ts
109
+ const useOverlayRegistration = ({ active, id }) => {
110
+ const nativeOverlayManager = useNativeOverlayManager();
111
+ const [zIndex, setZIndex] = useState(() => nativeOverlayManager.getZIndex(id));
225
112
  useEffect(() => {
226
- if (!visible) return;
227
- nativeOverlayStackStore.add(id);
113
+ if (!active) return;
114
+ const entry = nativeOverlayManager.register(id);
115
+ setZIndex(entry.zIndex);
228
116
  return () => {
229
- nativeOverlayStackStore.remove(id);
117
+ nativeOverlayManager.unregister(id);
230
118
  };
231
- }, [id, visible]);
232
- return { isTopOverlay: useCallback(() => nativeOverlayStackStore.isTop(id), [id]) };
119
+ }, [
120
+ active,
121
+ id,
122
+ nativeOverlayManager
123
+ ]);
124
+ return {
125
+ zIndex,
126
+ isTopOverlay: useCallback(() => nativeOverlayManager.isTop(id), [id, nativeOverlayManager])
127
+ };
233
128
  };
234
129
  //#endregion
235
- //#region src/hooks/behavior/overlay/useOverlayStack.ts
236
- const useOverlayStack = ({ active, id }) => useNativeOverlayStack({
237
- id,
238
- visible: active
239
- });
240
- //#endregion
241
130
  //#region src/hooks/behavior/overlay/useOverlayDismiss.ts
242
- const useOverlayDismiss = ({ active, closeOnOutsidePress = true, id, requestClose }) => {
243
- const { isTopOverlay } = useOverlayStack({
131
+ const dismissListeners = /* @__PURE__ */ new Map();
132
+ function attachDismissListener(manager) {
133
+ if (dismissListeners.has(manager)) return;
134
+ if (Platform.OS === "web") {
135
+ const handleKeyDown = (event) => {
136
+ if (event.key !== "Escape") return;
137
+ manager.dispatchTopDismiss();
138
+ };
139
+ document.addEventListener("keydown", handleKeyDown);
140
+ dismissListeners.set(manager, {
141
+ count: 0,
142
+ detach: () => {
143
+ document.removeEventListener("keydown", handleKeyDown);
144
+ dismissListeners.delete(manager);
145
+ }
146
+ });
147
+ return;
148
+ }
149
+ const subscription = BackHandler.addEventListener("hardwareBackPress", () => manager.dispatchTopDismiss());
150
+ dismissListeners.set(manager, {
151
+ count: 0,
152
+ detach: () => {
153
+ subscription.remove();
154
+ dismissListeners.delete(manager);
155
+ }
156
+ });
157
+ }
158
+ function retainDismissListener(manager) {
159
+ attachDismissListener(manager);
160
+ const retained = dismissListeners.get(manager);
161
+ if (!retained) return () => void 0;
162
+ retained.count += 1;
163
+ return () => {
164
+ const current = dismissListeners.get(manager);
165
+ if (!current) return;
166
+ current.count = Math.max(0, current.count - 1);
167
+ if (current.count > 0) return;
168
+ current.detach();
169
+ };
170
+ }
171
+ const useOverlayDismiss = ({ active, closeOnEscape = true, closeOnOutsidePress = true, id, requestClose, requestOutsideClose }) => {
172
+ const nativeOverlayManager = useNativeOverlayManager();
173
+ const registration = useOverlayRegistration({
244
174
  active,
245
175
  id
246
176
  });
177
+ const { isTopOverlay } = registration;
247
178
  const requestTopClose = useCallback(() => {
248
179
  if (!isTopOverlay()) return;
249
180
  requestClose();
250
181
  }, [isTopOverlay, requestClose]);
182
+ const requestOutsideTopClose = useCallback(() => {
183
+ nativeOverlayManager.dispatchTopOutsidePress();
184
+ }, [nativeOverlayManager]);
185
+ useEffect(() => {
186
+ if (!active) return;
187
+ return nativeOverlayManager.registerOutsidePressHandler(id, () => {
188
+ if (!closeOnOutsidePress) return false;
189
+ if (!isTopOverlay()) return false;
190
+ if (requestOutsideClose) {
191
+ requestOutsideClose();
192
+ return true;
193
+ }
194
+ requestClose();
195
+ return true;
196
+ });
197
+ }, [
198
+ active,
199
+ closeOnOutsidePress,
200
+ id,
201
+ isTopOverlay,
202
+ nativeOverlayManager,
203
+ requestClose,
204
+ requestOutsideClose
205
+ ]);
206
+ useEffect(() => {
207
+ if (!active) return;
208
+ const releaseDismissListener = retainDismissListener(nativeOverlayManager);
209
+ const unregisterDismissHandler = nativeOverlayManager.registerDismissHandler(id, () => {
210
+ if (!closeOnEscape) return false;
211
+ requestTopClose();
212
+ return true;
213
+ });
214
+ return () => {
215
+ unregisterDismissHandler();
216
+ releaseDismissListener();
217
+ };
218
+ }, [
219
+ active,
220
+ closeOnEscape,
221
+ id,
222
+ nativeOverlayManager,
223
+ requestTopClose
224
+ ]);
251
225
  return {
226
+ zIndex: registration.zIndex,
252
227
  isTopOverlay,
253
228
  requestClose: requestTopClose,
254
- requestOutsideClose: useCallback(() => {
255
- if (!closeOnOutsidePress) return;
256
- requestTopClose();
257
- }, [closeOnOutsidePress, requestTopClose])
229
+ requestOutsideClose: requestOutsideTopClose
230
+ };
231
+ };
232
+ //#endregion
233
+ //#region src/hooks/behavior/overlay/useOverlayFocusRestore.ts
234
+ function isFocusableWebNode(node) {
235
+ return typeof node === "object" && node !== null && "focus" in node && typeof node.focus === "function";
236
+ }
237
+ const useOverlayFocusRestore = ({ active = false, enabled = true, finalFocus, triggerRef }) => {
238
+ const previouslyFocusedRef = useRef(null);
239
+ const saveFocusSnapshot = useCallback(() => {
240
+ if (Platform.OS !== "web" || typeof document === "undefined") return;
241
+ previouslyFocusedRef.current = document.activeElement;
242
+ }, []);
243
+ const restoreFocus = useCallback(() => {
244
+ if (!enabled) return;
245
+ if (Platform.OS === "web") {
246
+ const preferredNode = finalFocus?.current ?? triggerRef.current;
247
+ if (isFocusableWebNode(preferredNode)) {
248
+ preferredNode.focus();
249
+ return;
250
+ }
251
+ const previouslyFocused = previouslyFocusedRef.current;
252
+ if (previouslyFocused instanceof HTMLElement && previouslyFocused.isConnected) previouslyFocused.focus();
253
+ return;
254
+ }
255
+ if (typeof findNodeHandle !== "function") return;
256
+ const handle = findNodeHandle(finalFocus?.current ?? triggerRef.current);
257
+ if (handle && AccessibilityInfo.setAccessibilityFocus) AccessibilityInfo.setAccessibilityFocus(handle);
258
+ }, [
259
+ enabled,
260
+ finalFocus,
261
+ triggerRef
262
+ ]);
263
+ const restoreFocusAfterClose = useCallback(() => {
264
+ if (!enabled) return;
265
+ requestAnimationFrame(restoreFocus);
266
+ }, [enabled, restoreFocus]);
267
+ useEffect(() => {
268
+ if (!active) return;
269
+ saveFocusSnapshot();
270
+ }, [active, saveFocusSnapshot]);
271
+ return {
272
+ restoreFocus,
273
+ restoreFocusAfterClose,
274
+ saveFocusSnapshot
258
275
  };
259
276
  };
260
277
  //#endregion
278
+ //#region src/hooks/behavior/overlay/useOverlayPresentation.ts
279
+ function useOverlayPresentation(presentation = "auto", breakpoint = 768) {
280
+ const { width } = useWindowDimensions();
281
+ if (presentation === "auto") return width >= breakpoint ? "popover" : "sheet";
282
+ return presentation;
283
+ }
284
+ //#endregion
261
285
  //#region src/hooks/useControllableState.ts
262
286
  const useControllableState = ({ value, defaultValue, onChange }) => {
263
287
  const [internalValue, setInternalValue] = useState(defaultValue);
@@ -689,6 +713,16 @@ function useThemeStyles(createStyles) {
689
713
  return useMemo(() => createStyles(theme), [createStyles, theme]);
690
714
  }
691
715
  //#endregion
716
+ //#region src/components/Dropdown/internal/DropdownContext.tsx
717
+ const DropdownContext = createContext(null);
718
+ const DropdownProvider = DropdownContext.Provider;
719
+ const useDropdownContext = () => {
720
+ const context = useContext(DropdownContext);
721
+ if (!context) throw new Error("Dropdown components must be used inside Dropdown");
722
+ return context;
723
+ };
724
+ DropdownContext.displayName = "DropdownContext";
725
+ //#endregion
692
726
  //#region src/components/Dropdown/Content/DropdownContent.styles.ts
693
727
  const createStyles$21 = (theme) => StyleSheet.create({
694
728
  modalRoot: { flex: 1 },
@@ -773,13 +807,14 @@ const createStyles$21 = (theme) => StyleSheet.create({
773
807
  //#region src/components/Dropdown/Content/DropdownContent.tsx
774
808
  const nativePointerEventsBoxNone$1 = Platform.OS === "web" ? void 0 : { pointerEvents: "box-none" };
775
809
  const webPointerEventsBoxNone$1 = Platform.OS === "web" ? { pointerEvents: "box-none" } : void 0;
776
- function DropdownContent({ isOpen, children, onClose, color = "primary", contentStyle, accessibilityLabel, presentation, position, onFloatingLayout, searchable = false, searchValue = "", searchPlaceholder = "Search actions...", searchAccessibilityLabel, onSearchChange }) {
810
+ function DropdownContent({ children, contentStyle, accessibilityLabel }) {
811
+ const { open, color, presentation, zIndex, position, searchable, searchValue, searchPlaceholder, searchAccessibilityLabel, requestClose, requestOutsideClose, onSearchChange, onFloatingLayout } = useDropdownContext();
777
812
  const { theme } = useTheme();
778
813
  const styles = useThemeStyles(createStyles$21);
779
814
  const colorPalette = theme.components.dropdown[color];
780
815
  const isSheet = presentation === "sheet";
781
816
  const isPopover = presentation === "popover";
782
- const animation = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
817
+ const animation = useRef(new Animated.Value(open ? 1 : 0)).current;
783
818
  const [reduceMotion, setReduceMotion] = useState(false);
784
819
  useEffect(() => {
785
820
  AccessibilityInfo.isReduceMotionEnabled?.().then(setReduceMotion);
@@ -789,7 +824,7 @@ function DropdownContent({ isOpen, children, onClose, color = "primary", content
789
824
  };
790
825
  }, []);
791
826
  useEffect(() => {
792
- if (!isOpen) {
827
+ if (!open) {
793
828
  animation.setValue(0);
794
829
  return;
795
830
  }
@@ -805,7 +840,7 @@ function DropdownContent({ isOpen, children, onClose, color = "primary", content
805
840
  }).start();
806
841
  }, [
807
842
  animation,
808
- isOpen,
843
+ open,
809
844
  isSheet,
810
845
  reduceMotion
811
846
  ]);
@@ -829,11 +864,15 @@ function DropdownContent({ isOpen, children, onClose, color = "primary", content
829
864
  }, [animation, isSheet]);
830
865
  return /* @__PURE__ */ jsx(Modal$1, {
831
866
  transparent: true,
832
- visible: isOpen,
867
+ visible: open,
833
868
  animationType: "none",
834
- onRequestClose: onClose,
869
+ onRequestClose: requestClose,
835
870
  children: /* @__PURE__ */ jsxs(View, {
836
- style: [styles.modalRoot, styles[presentation]],
871
+ style: [
872
+ styles.modalRoot,
873
+ styles[presentation],
874
+ Platform.OS === "web" && { zIndex }
875
+ ],
837
876
  children: [/* @__PURE__ */ jsx(Animated.View, {
838
877
  ...nativePointerEventsBoxNone$1,
839
878
  style: [
@@ -845,7 +884,7 @@ function DropdownContent({ isOpen, children, onClose, color = "primary", content
845
884
  accessibilityRole: "button",
846
885
  accessibilityLabel: "Close menu",
847
886
  style: StyleSheet.absoluteFill,
848
- onPress: onClose
887
+ onPress: requestOutsideClose
849
888
  })
850
889
  }), /* @__PURE__ */ jsxs(Animated.View, {
851
890
  accessibilityRole: "menu",
@@ -993,6 +1032,279 @@ function getItemLabel(children) {
993
1032
  return labelParts.join("").trim();
994
1033
  }
995
1034
  //#endregion
1035
+ //#region src/hooks/behavior/dropdown/useDropdownAccessibility.ts
1036
+ const useDropdownAccessibility = ({ accessibilityLabel, label, open }) => {
1037
+ const menuAccessibilityLabel = useMemo(() => {
1038
+ if (accessibilityLabel) return accessibilityLabel;
1039
+ return typeof label === "string" ? label : "Menu";
1040
+ }, [accessibilityLabel, label]);
1041
+ useEffect(() => {
1042
+ if (!open) return;
1043
+ AccessibilityInfo.announceForAccessibility(`${menuAccessibilityLabel} opened`);
1044
+ }, [menuAccessibilityLabel, open]);
1045
+ return { menuAccessibilityLabel };
1046
+ };
1047
+ //#endregion
1048
+ //#region src/hooks/behavior/dropdown/useDropdownEntries.ts
1049
+ const useDropdownEntries = ({ parsed, filteredParsed, loading, loadingText, isSearchable, empty }) => {
1050
+ return {
1051
+ navigableItems: useMemo(() => parsed.items.map((item) => ({
1052
+ disabled: item.disabled,
1053
+ label: item.label,
1054
+ value: item.id
1055
+ })), [parsed.items]),
1056
+ data: useMemo(() => {
1057
+ if (loading) return [{
1058
+ type: "loading",
1059
+ id: "loading",
1060
+ props: { children: loadingText }
1061
+ }];
1062
+ if (isSearchable && filteredParsed.items.length === 0) return [{
1063
+ type: "empty",
1064
+ id: "empty",
1065
+ props: { children: empty ?? "No actions found" }
1066
+ }];
1067
+ return filteredParsed.entries;
1068
+ }, [
1069
+ empty,
1070
+ filteredParsed.entries,
1071
+ filteredParsed.items.length,
1072
+ isSearchable,
1073
+ loading,
1074
+ loadingText
1075
+ ])
1076
+ };
1077
+ };
1078
+ //#endregion
1079
+ //#region src/components/Dropdown/internal/DropdownUtils.ts
1080
+ function createDropdownSelectEvent() {
1081
+ let defaultPrevented = false;
1082
+ return {
1083
+ preventDefault: () => {
1084
+ defaultPrevented = true;
1085
+ },
1086
+ get defaultPrevented() {
1087
+ return defaultPrevented;
1088
+ }
1089
+ };
1090
+ }
1091
+ function filterDropdownEntries(parsed, searchValue) {
1092
+ const normalizedSearch = searchValue.trim().toLocaleLowerCase();
1093
+ const matchedItems = new Set(parsed.items.filter((item) => item.label.toLocaleLowerCase().includes(normalizedSearch)).map((item) => item.id));
1094
+ return {
1095
+ ...parsed,
1096
+ items: parsed.items.filter((item) => matchedItems.has(item.id)),
1097
+ entries: parsed.entries.filter((entry) => entry.type !== "item" || matchedItems.has(entry.id))
1098
+ };
1099
+ }
1100
+ //#endregion
1101
+ //#region src/hooks/behavior/dropdown/useDropdownSearch.ts
1102
+ const useDropdownSearch = ({ parsed, searchable, command, searchValue, defaultSearchValue, onSearch }) => {
1103
+ const [uncontrolledSearchValue, setUncontrolledSearchValue] = useState(defaultSearchValue);
1104
+ const resolvedSearchValue = searchValue ?? uncontrolledSearchValue;
1105
+ const contentCommand = parsed.contentProps?.command ?? false;
1106
+ const isSearchable = searchable || command || contentCommand || Boolean(parsed.searchProps);
1107
+ return {
1108
+ contentCommand,
1109
+ filteredParsed: useMemo(() => {
1110
+ if (!isSearchable || !resolvedSearchValue.trim()) return parsed;
1111
+ return filterDropdownEntries(parsed, resolvedSearchValue);
1112
+ }, [
1113
+ isSearchable,
1114
+ parsed,
1115
+ resolvedSearchValue
1116
+ ]),
1117
+ handleSearchChange: useCallback((value) => {
1118
+ if (searchValue === void 0) setUncontrolledSearchValue(value);
1119
+ onSearch?.(value);
1120
+ }, [onSearch, searchValue]),
1121
+ isSearchable,
1122
+ resolvedSearchValue
1123
+ };
1124
+ };
1125
+ //#endregion
1126
+ //#region src/managers/FloatingManager/computeFloatingPosition.ts
1127
+ function clamp(value, min, max) {
1128
+ return Math.min(Math.max(value, min), Math.max(min, max));
1129
+ }
1130
+ function parsePlacement(placement) {
1131
+ const [side, align = "center"] = placement.split("-");
1132
+ return {
1133
+ side,
1134
+ align
1135
+ };
1136
+ }
1137
+ function createPlacement(side, align) {
1138
+ return align === "center" ? side : `${side}-${align}`;
1139
+ }
1140
+ function getOppositeSide(side) {
1141
+ switch (side) {
1142
+ case "top": return "bottom";
1143
+ case "right": return "left";
1144
+ case "bottom": return "top";
1145
+ case "left": return "right";
1146
+ }
1147
+ }
1148
+ function computeBasePosition({ reference, floating, side, align, offset }) {
1149
+ const referenceCenterX = reference.x + reference.width / 2;
1150
+ const referenceCenterY = reference.y + reference.height / 2;
1151
+ const alignedLeft = align === "start" ? reference.x : align === "end" ? reference.x + reference.width - floating.width : referenceCenterX - floating.width / 2;
1152
+ const alignedTop = align === "start" ? reference.y : align === "end" ? reference.y + reference.height - floating.height : referenceCenterY - floating.height / 2;
1153
+ switch (side) {
1154
+ case "top": return {
1155
+ top: reference.y - floating.height - offset,
1156
+ left: alignedLeft
1157
+ };
1158
+ case "right": return {
1159
+ top: alignedTop,
1160
+ left: reference.x + reference.width + offset
1161
+ };
1162
+ case "bottom": return {
1163
+ top: reference.y + reference.height + offset,
1164
+ left: alignedLeft
1165
+ };
1166
+ case "left": return {
1167
+ top: alignedTop,
1168
+ left: reference.x - floating.width - offset
1169
+ };
1170
+ }
1171
+ }
1172
+ function getMainAxisOverflow({ side, position, floating, boundary, padding }) {
1173
+ switch (side) {
1174
+ case "top": return padding - position.top;
1175
+ case "right": return position.left + floating.width + padding - boundary.width;
1176
+ case "bottom": return position.top + floating.height + padding - boundary.height;
1177
+ case "left": return padding - position.left;
1178
+ }
1179
+ }
1180
+ function computeFloatingPosition({ reference, floating, boundary, placement, offset = 8, padding = 12, arrowPadding = 16, flip = true, shift = true }) {
1181
+ const parsed = parsePlacement(placement);
1182
+ let resolvedSide = parsed.side;
1183
+ let position = computeBasePosition({
1184
+ reference,
1185
+ floating,
1186
+ side: resolvedSide,
1187
+ align: parsed.align,
1188
+ offset
1189
+ });
1190
+ if (flip && getMainAxisOverflow({
1191
+ side: resolvedSide,
1192
+ position,
1193
+ floating,
1194
+ boundary,
1195
+ padding
1196
+ }) > 0) {
1197
+ const oppositeSide = getOppositeSide(resolvedSide);
1198
+ const oppositePosition = computeBasePosition({
1199
+ reference,
1200
+ floating,
1201
+ side: oppositeSide,
1202
+ align: parsed.align,
1203
+ offset
1204
+ });
1205
+ if (getMainAxisOverflow({
1206
+ side: oppositeSide,
1207
+ position: oppositePosition,
1208
+ floating,
1209
+ boundary,
1210
+ padding
1211
+ }) <= 0) {
1212
+ resolvedSide = oppositeSide;
1213
+ position = oppositePosition;
1214
+ }
1215
+ }
1216
+ if (shift) position = {
1217
+ top: clamp(position.top, padding, boundary.height - floating.height - padding),
1218
+ left: clamp(position.left, padding, boundary.width - floating.width - padding)
1219
+ };
1220
+ const referenceCenterX = reference.x + reference.width / 2;
1221
+ const referenceCenterY = reference.y + reference.height / 2;
1222
+ const arrowPosition = resolvedSide === "top" || resolvedSide === "bottom" ? { left: clamp(referenceCenterX - position.left, arrowPadding, floating.width - arrowPadding) } : { top: clamp(referenceCenterY - position.top, arrowPadding, floating.height - arrowPadding) };
1223
+ return {
1224
+ position,
1225
+ arrowPosition,
1226
+ placement: createPlacement(resolvedSide, parsed.align)
1227
+ };
1228
+ }
1229
+ //#endregion
1230
+ //#region src/managers/FloatingManager/useNativeFloatingPosition.ts
1231
+ function useNativeFloatingPosition(placement = "top", offset = 8) {
1232
+ const [result, setResult] = useState({
1233
+ position: {
1234
+ top: 0,
1235
+ left: 0
1236
+ },
1237
+ arrowPosition: {},
1238
+ placement
1239
+ });
1240
+ const floatingSizeRef = useRef({
1241
+ width: 0,
1242
+ height: 0
1243
+ });
1244
+ const lastTriggerRef = useRef(null);
1245
+ const lastContainerRef = useRef(null);
1246
+ const updatePosition = useCallback((triggerRef, containerRef, measuredSize = floatingSizeRef.current) => {
1247
+ lastTriggerRef.current = triggerRef;
1248
+ lastContainerRef.current = containerRef ?? null;
1249
+ const triggerNode = triggerRef.current;
1250
+ const containerNode = containerRef?.current;
1251
+ if (!triggerNode || typeof triggerNode.measureInWindow !== "function") {
1252
+ setResult((current) => ({
1253
+ ...current,
1254
+ position: {
1255
+ top: 0,
1256
+ left: 0
1257
+ }
1258
+ }));
1259
+ return;
1260
+ }
1261
+ triggerNode.measureInWindow((x, y, width, height) => {
1262
+ const commitPosition = (containerX, containerY, containerWidth, containerHeight) => {
1263
+ const nextResult = computeFloatingPosition({
1264
+ reference: {
1265
+ x: x - containerX,
1266
+ y: y - containerY,
1267
+ width,
1268
+ height
1269
+ },
1270
+ floating: measuredSize,
1271
+ boundary: {
1272
+ width: containerWidth,
1273
+ height: containerHeight
1274
+ },
1275
+ placement,
1276
+ offset
1277
+ });
1278
+ setResult(nextResult);
1279
+ };
1280
+ if (!containerNode || typeof containerNode.measureInWindow !== "function") {
1281
+ const window = Dimensions.get("window");
1282
+ commitPosition(0, 0, window.width, window.height);
1283
+ return;
1284
+ }
1285
+ containerNode.measureInWindow((containerX, containerY, containerWidth, containerHeight) => {
1286
+ commitPosition(containerX, containerY, containerWidth, containerHeight);
1287
+ });
1288
+ });
1289
+ }, [placement, offset]);
1290
+ const onFloatingLayout = useCallback((event) => {
1291
+ const { width, height } = event.nativeEvent.layout;
1292
+ const nextSize = {
1293
+ width,
1294
+ height
1295
+ };
1296
+ floatingSizeRef.current = nextSize;
1297
+ if (lastTriggerRef.current) updatePosition(lastTriggerRef.current, lastContainerRef.current ?? void 0, nextSize);
1298
+ }, [updatePosition]);
1299
+ return {
1300
+ position: result.position,
1301
+ arrowPosition: result.arrowPosition,
1302
+ placement: result.placement,
1303
+ updatePosition,
1304
+ onFloatingLayout
1305
+ };
1306
+ }
1307
+ //#endregion
996
1308
  //#region src/components/Dropdown/Dropdown.styles.ts
997
1309
  const createStyles$20 = (theme) => StyleSheet.create({
998
1310
  root: { alignSelf: "flex-start" },
@@ -1028,28 +1340,6 @@ function DropdownGroup({ label }) {
1028
1340
  }
1029
1341
  DropdownGroup.displayName = "DropdownGroup";
1030
1342
  //#endregion
1031
- //#region src/components/Dropdown/internal/DropdownUtils.ts
1032
- function createDropdownSelectEvent() {
1033
- let defaultPrevented = false;
1034
- return {
1035
- preventDefault: () => {
1036
- defaultPrevented = true;
1037
- },
1038
- get defaultPrevented() {
1039
- return defaultPrevented;
1040
- }
1041
- };
1042
- }
1043
- function filterDropdownEntries(parsed, searchValue) {
1044
- const normalizedSearch = searchValue.trim().toLocaleLowerCase();
1045
- const matchedItems = new Set(parsed.items.filter((item) => item.label.toLocaleLowerCase().includes(normalizedSearch)).map((item) => item.id));
1046
- return {
1047
- ...parsed,
1048
- items: parsed.items.filter((item) => matchedItems.has(item.id)),
1049
- entries: parsed.entries.filter((entry) => entry.type !== "item" || matchedItems.has(entry.id))
1050
- };
1051
- }
1052
- //#endregion
1053
1343
  //#region src/components/Dropdown/Item/DropdownItem.styles.ts
1054
1344
  const createStyles$18 = (theme) => StyleSheet.create({
1055
1345
  item: {
@@ -1074,7 +1364,8 @@ const createStyles$18 = (theme) => StyleSheet.create({
1074
1364
  });
1075
1365
  //#endregion
1076
1366
  //#region src/components/Dropdown/Item/DropdownItem.tsx
1077
- function DropdownItem({ label, value, rootColor = "primary", color = "default", icon, disabled = false, textWrap = "truncate", itemStyle, textStyle, onSelect }) {
1367
+ function DropdownItem({ label, value, color = "default", icon, disabled = false, textWrap = "truncate", onSelect }) {
1368
+ const { color: rootColor, itemStyle, textStyle } = useDropdownContext();
1078
1369
  const { theme } = useTheme();
1079
1370
  const styles = useThemeStyles(createStyles$18);
1080
1371
  const rootColorPalette = theme.components.dropdown[rootColor];
@@ -1228,7 +1519,8 @@ const createStyles$16 = (theme) => StyleSheet.create({
1228
1519
  });
1229
1520
  //#endregion
1230
1521
  //#region src/components/Dropdown/Trigger/DropdownTrigger.tsx
1231
- function DropdownTrigger({ asChild = false, label, trigger, children, icon, arrowIcon, showArrow = true, color = "primary", size = "md", disabled = false, isOpen, triggerStyle, triggerRef, accessibilityLabel, accessibilityHint, onPress }) {
1522
+ function DropdownTrigger({ asChild = false, label, trigger, children, icon, arrowIcon, showArrow = true, triggerStyle, triggerRef, accessibilityLabel, accessibilityHint }) {
1523
+ const { open, color, disabled, size, toggle } = useDropdownContext();
1232
1524
  const { theme } = useTheme();
1233
1525
  const styles = useThemeStyles(createStyles$16);
1234
1526
  const colorPalette = theme.components.dropdown[color];
@@ -1245,14 +1537,14 @@ function DropdownTrigger({ asChild = false, label, trigger, children, icon, arro
1245
1537
  const hasIcon = Boolean(icon);
1246
1538
  const isIconOnly = !trigger && hasIcon && !showArrow;
1247
1539
  const [isPressed, setIsPressed] = useState(false);
1248
- const rotateAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
1540
+ const rotateAnim = useRef(new Animated.Value(open ? 1 : 0)).current;
1249
1541
  useEffect(() => {
1250
1542
  Animated.timing(rotateAnim, {
1251
- toValue: isOpen ? 1 : 0,
1543
+ toValue: open ? 1 : 0,
1252
1544
  duration: 180,
1253
1545
  useNativeDriver: Platform.OS !== "web"
1254
1546
  }).start();
1255
- }, [isOpen, rotateAnim]);
1547
+ }, [open, rotateAnim]);
1256
1548
  const arrowRotate = rotateAnim.interpolate({
1257
1549
  inputRange: [0, 1],
1258
1550
  outputRange: ["0deg", "180deg"]
@@ -1288,12 +1580,12 @@ function DropdownTrigger({ asChild = false, label, trigger, children, icon, arro
1288
1580
  accessibilityLabel: accessibilityLabel ?? (typeof label === "string" ? label : void 0),
1289
1581
  accessibilityHint,
1290
1582
  accessibilityState: {
1291
- expanded: isOpen,
1583
+ expanded: open,
1292
1584
  disabled: isChildDisabled
1293
1585
  },
1294
1586
  onPress: () => {
1295
1587
  child.props.onPress?.();
1296
- if (!isChildDisabled) onPress();
1588
+ if (!isChildDisabled) toggle();
1297
1589
  }
1298
1590
  });
1299
1591
  }
@@ -1304,10 +1596,10 @@ function DropdownTrigger({ asChild = false, label, trigger, children, icon, arro
1304
1596
  accessibilityLabel: accessibilityLabel ?? (typeof label === "string" ? label : void 0),
1305
1597
  accessibilityHint,
1306
1598
  accessibilityState: {
1307
- expanded: isOpen,
1599
+ expanded: open,
1308
1600
  disabled
1309
1601
  },
1310
- onPress,
1602
+ onPress: toggle,
1311
1603
  onPressIn: () => setIsPressed(true),
1312
1604
  onPressOut: () => setIsPressed(false),
1313
1605
  style: [
@@ -1354,9 +1646,6 @@ DropdownTrigger.displayName = "DropdownTrigger";
1354
1646
  function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, showArrow = true, open, defaultOpen = false, onOpenChange, presentation = "auto", placement = "bottom-start", offset = 8, closeOnSelect = true, color = "primary", disabled = false, loading = false, loadingText = "Loading actions...", searchable = false, command = false, searchValue, defaultSearchValue = "", searchPlaceholder, onSearch, empty, size = "md", style, triggerStyle, contentStyle, itemStyle, textStyle, accessibilityLabel, accessibilityHint }) {
1355
1647
  const styles = useThemeStyles(createStyles$20);
1356
1648
  const overlayId = useId();
1357
- const [uncontrolledSearchValue, setUncontrolledSearchValue] = useState(defaultSearchValue);
1358
- const resolvedSearchValue = searchValue ?? uncontrolledSearchValue;
1359
- const { width } = useWindowDimensions();
1360
1649
  const triggerRef = useRef(null);
1361
1650
  const setTriggerRef = useCallback((node) => {
1362
1651
  if (node && typeof node === "object" && "measureInWindow" in node && typeof node.measureInWindow === "function") {
@@ -1366,29 +1655,26 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
1366
1655
  triggerRef.current = null;
1367
1656
  }, []);
1368
1657
  const parsed = useMemo(() => parseDropdownChildren(children), [children]);
1369
- const contentCommand = parsed.contentProps?.command ?? false;
1370
- const isSearchable = searchable || command || contentCommand || !!parsed.searchProps;
1371
- const resolvedPresentation = presentation === "auto" ? width < 768 ? "sheet" : "popover" : presentation;
1658
+ const { contentCommand, filteredParsed, handleSearchChange, isSearchable, resolvedSearchValue } = useDropdownSearch({
1659
+ parsed,
1660
+ searchable,
1661
+ command,
1662
+ searchValue,
1663
+ defaultSearchValue,
1664
+ onSearch
1665
+ });
1666
+ const { navigableItems, data } = useDropdownEntries({
1667
+ parsed,
1668
+ filteredParsed,
1669
+ loading,
1670
+ loadingText,
1671
+ isSearchable,
1672
+ empty
1673
+ });
1674
+ const resolvedPresentation = useOverlayPresentation(presentation);
1372
1675
  const contentStyleFromSlot = parsed.contentProps?.style;
1373
1676
  const contentPresentation = (parsed.contentProps?.presentation === "auto" ? void 0 : parsed.contentProps?.presentation) ?? resolvedPresentation;
1374
1677
  const { position, updatePosition, onFloatingLayout } = useNativeFloatingPosition(placement, offset);
1375
- const menuAccessibilityLabel = useMemo(() => {
1376
- if (accessibilityLabel) return accessibilityLabel;
1377
- return typeof label === "string" ? label : "Menu";
1378
- }, [accessibilityLabel, label]);
1379
- const navigableItems = useMemo(() => parsed.items.map((item) => ({
1380
- disabled: item.disabled,
1381
- label: item.label,
1382
- value: item.id
1383
- })), [parsed.items]);
1384
- const filteredParsed = useMemo(() => {
1385
- if (!isSearchable || !resolvedSearchValue.trim()) return parsed;
1386
- return filterDropdownEntries(parsed, resolvedSearchValue);
1387
- }, [
1388
- isSearchable,
1389
- parsed,
1390
- resolvedSearchValue
1391
- ]);
1392
1678
  const { isOpen, closeDropdown, toggleDropdown } = useDropdown({
1393
1679
  items: navigableItems,
1394
1680
  open,
@@ -1398,6 +1684,11 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
1398
1684
  getItemValue: (item) => item.value,
1399
1685
  getItemText: (item) => typeof item.label === "string" ? item.label : item.value
1400
1686
  });
1687
+ const { menuAccessibilityLabel } = useDropdownAccessibility({
1688
+ accessibilityLabel,
1689
+ label,
1690
+ open: isOpen
1691
+ });
1401
1692
  useEffect(() => {
1402
1693
  if (!isOpen || contentPresentation !== "popover") return;
1403
1694
  updatePosition(triggerRef);
@@ -1406,24 +1697,14 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
1406
1697
  contentPresentation,
1407
1698
  updatePosition
1408
1699
  ]);
1409
- useEffect(() => {
1410
- if (!isOpen) return;
1411
- AccessibilityInfo.announceForAccessibility(`${menuAccessibilityLabel} opened`);
1412
- }, [isOpen, menuAccessibilityLabel]);
1413
- const focusTrigger = useCallback(() => {
1414
- if (Platform.OS === "web") {
1415
- const triggerNode = triggerRef.current;
1416
- if (triggerNode && typeof triggerNode === "object" && "focus" in triggerNode && typeof triggerNode.focus === "function") triggerNode.focus();
1417
- return;
1418
- }
1419
- if (typeof findNodeHandle !== "function") return;
1420
- const handle = findNodeHandle(triggerRef.current);
1421
- if (handle && AccessibilityInfo.setAccessibilityFocus) AccessibilityInfo.setAccessibilityFocus(handle);
1422
- }, []);
1700
+ const { restoreFocusAfterClose } = useOverlayFocusRestore({
1701
+ active: isOpen,
1702
+ triggerRef
1703
+ });
1423
1704
  const closeAndFocusTrigger = useCallback(() => {
1424
1705
  closeDropdown();
1425
- requestAnimationFrame(focusTrigger);
1426
- }, [closeDropdown, focusTrigger]);
1706
+ restoreFocusAfterClose();
1707
+ }, [closeDropdown, restoreFocusAfterClose]);
1427
1708
  const dismiss = useOverlayDismiss({
1428
1709
  id: overlayId,
1429
1710
  active: isOpen,
@@ -1443,10 +1724,6 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
1443
1724
  const handleTriggerPress = useCallback(() => {
1444
1725
  toggleDropdown();
1445
1726
  }, [toggleDropdown]);
1446
- const handleSearchChange = useCallback((value) => {
1447
- if (searchValue === void 0) setUncontrolledSearchValue(value);
1448
- onSearch?.(value);
1449
- }, [onSearch, searchValue]);
1450
1727
  const renderEntry = useCallback(({ item }) => {
1451
1728
  if (item.type === "label") return /* @__PURE__ */ jsx(DropdownGroup, { label: item.props.children });
1452
1729
  if (item.type === "separator") return /* @__PURE__ */ jsx(DropdownSeparator, {});
@@ -1461,71 +1738,82 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
1461
1738
  return /* @__PURE__ */ jsx(DropdownItem, {
1462
1739
  label: item.props.children,
1463
1740
  value: item.props.value ?? item.id,
1464
- rootColor: color,
1465
1741
  color: item.props.color,
1466
1742
  icon: item.props.icon,
1467
1743
  disabled: item.props.disabled,
1468
1744
  textWrap: item.props.textWrap,
1469
- itemStyle,
1470
- textStyle,
1471
1745
  onSelect: () => handleSelect(item)
1472
1746
  });
1473
- }, [
1474
- color,
1475
- handleSelect,
1476
- itemStyle,
1477
- styles.emptyText,
1478
- textStyle
1479
- ]);
1480
- const data = loading ? [{
1481
- type: "loading",
1482
- id: "loading",
1483
- props: { children: loadingText }
1484
- }] : isSearchable && filteredParsed.items.length === 0 ? [{
1485
- type: "empty",
1486
- id: "empty",
1487
- props: { children: empty ?? "No actions found" }
1488
- }] : filteredParsed.entries;
1489
- return /* @__PURE__ */ jsxs(View, {
1490
- style: [styles.root, style],
1491
- children: [/* @__PURE__ */ jsx(DropdownTrigger, {
1492
- asChild: Boolean(parsed.trigger),
1493
- label,
1494
- trigger: trigger ?? parsed.trigger,
1495
- icon,
1496
- arrowIcon,
1497
- showArrow,
1747
+ }, [handleSelect, styles.emptyText]);
1748
+ const resolvedSearchPlaceholder = parsed.searchProps?.placeholder ?? searchPlaceholder ?? (command || contentCommand ? "Type a command..." : "Search actions...");
1749
+ const searchAccessibilityLabel = parsed.searchProps?.accessibilityLabel;
1750
+ return /* @__PURE__ */ jsx(DropdownProvider, {
1751
+ value: useMemo(() => ({
1752
+ open: isOpen,
1753
+ disabled,
1754
+ loading,
1498
1755
  color,
1499
- disabled: disabled || parsed.triggerProps?.disabled,
1500
- isOpen,
1501
1756
  size,
1502
- triggerRef: setTriggerRef,
1503
- triggerStyle,
1504
- accessibilityLabel,
1505
- accessibilityHint,
1506
- onPress: handleTriggerPress
1507
- }), /* @__PURE__ */ jsx(DropdownContent, {
1508
- isOpen,
1509
- onClose: dismiss.requestClose,
1510
- color,
1511
- contentStyle: [contentStyle, contentStyleFromSlot],
1512
- accessibilityLabel: menuAccessibilityLabel,
1513
1757
  presentation: contentPresentation,
1514
1758
  position,
1515
- onFloatingLayout,
1759
+ zIndex: dismiss.zIndex,
1516
1760
  searchable: isSearchable,
1517
1761
  searchValue: resolvedSearchValue,
1518
- searchPlaceholder: parsed.searchProps?.placeholder ?? searchPlaceholder ?? (command || contentCommand ? "Type a command..." : "Search actions..."),
1519
- searchAccessibilityLabel: parsed.searchProps?.accessibilityLabel,
1762
+ searchPlaceholder: resolvedSearchPlaceholder,
1763
+ searchAccessibilityLabel,
1764
+ itemStyle,
1765
+ textStyle,
1766
+ requestClose: dismiss.requestClose,
1767
+ requestOutsideClose: dismiss.requestOutsideClose,
1768
+ toggle: handleTriggerPress,
1520
1769
  onSearchChange: handleSearchChange,
1521
- children: /* @__PURE__ */ jsx(FlatList, {
1522
- data,
1523
- keyExtractor: (item) => item.id,
1524
- renderItem: renderEntry,
1525
- keyboardShouldPersistTaps: "handled",
1526
- removeClippedSubviews: data.length > 24
1527
- })
1528
- })]
1770
+ onFloatingLayout
1771
+ }), [
1772
+ color,
1773
+ contentPresentation,
1774
+ disabled,
1775
+ dismiss.zIndex,
1776
+ dismiss.requestClose,
1777
+ dismiss.requestOutsideClose,
1778
+ handleSearchChange,
1779
+ handleTriggerPress,
1780
+ isOpen,
1781
+ isSearchable,
1782
+ itemStyle,
1783
+ loading,
1784
+ onFloatingLayout,
1785
+ position,
1786
+ resolvedSearchPlaceholder,
1787
+ resolvedSearchValue,
1788
+ searchAccessibilityLabel,
1789
+ size,
1790
+ textStyle
1791
+ ]),
1792
+ children: /* @__PURE__ */ jsxs(View, {
1793
+ style: [styles.root, style],
1794
+ children: [/* @__PURE__ */ jsx(DropdownTrigger, {
1795
+ asChild: Boolean(parsed.trigger),
1796
+ label,
1797
+ trigger: trigger ?? parsed.trigger,
1798
+ icon,
1799
+ arrowIcon,
1800
+ showArrow,
1801
+ triggerRef: setTriggerRef,
1802
+ triggerStyle,
1803
+ accessibilityLabel,
1804
+ accessibilityHint
1805
+ }), /* @__PURE__ */ jsx(DropdownContent, {
1806
+ contentStyle: [contentStyle, contentStyleFromSlot],
1807
+ accessibilityLabel: menuAccessibilityLabel,
1808
+ children: /* @__PURE__ */ jsx(FlatList, {
1809
+ data,
1810
+ keyExtractor: (item) => item.id,
1811
+ renderItem: renderEntry,
1812
+ keyboardShouldPersistTaps: "handled",
1813
+ removeClippedSubviews: data.length > 24
1814
+ })
1815
+ })]
1816
+ })
1529
1817
  });
1530
1818
  }
1531
1819
  DropdownRoot.displayName = "Dropdown";
@@ -1617,13 +1905,14 @@ const createStyles$14 = (theme) => StyleSheet.create({
1617
1905
  });
1618
1906
  //#endregion
1619
1907
  //#region src/components/Modal/internal/ModalContext.tsx
1620
- const ModalContext = createContext(void 0);
1621
- ModalContext.displayName = "ModalContext";
1908
+ const ModalContext = createContext(null);
1909
+ const ModalProvider = ModalContext.Provider;
1622
1910
  const useModalContext = () => {
1623
1911
  const context = useContext(ModalContext);
1624
1912
  if (!context) throw new Error("Modal compound components must be used inside Modal");
1625
1913
  return context;
1626
1914
  };
1915
+ ModalContext.displayName = "ModalContext";
1627
1916
  //#endregion
1628
1917
  //#region src/components/Modal/Close/ModalClose.tsx
1629
1918
  const ModalClose = ({ children, accessibilityLabel, style }) => {
@@ -1753,7 +2042,7 @@ const createStyles$11 = (theme) => StyleSheet.create({
1753
2042
  //#region src/components/Modal/Overlay/ModalOverlay.tsx
1754
2043
  const ModalOverlay = ({ children, overlayStyle }) => {
1755
2044
  const styles = useThemeStyles(createStyles$11);
1756
- const { animation, animationProgress, closeOnOutsidePress, onClose, onOutsideClose, shouldRender } = useModalContext();
2045
+ const { animation, animationProgress, closeOnOutsidePress, zIndex, onClose, onOutsideClose, shouldRender } = useModalContext();
1757
2046
  const backdropStyle = animation === "none" ? void 0 : { opacity: animationProgress };
1758
2047
  return /* @__PURE__ */ jsx(Modal$1, {
1759
2048
  visible: shouldRender,
@@ -1761,7 +2050,11 @@ const ModalOverlay = ({ children, overlayStyle }) => {
1761
2050
  animationType: "none",
1762
2051
  onRequestClose: onClose,
1763
2052
  children: /* @__PURE__ */ jsxs(View, {
1764
- style: [styles.overlay, overlayStyle],
2053
+ style: [
2054
+ styles.overlay,
2055
+ Platform.OS === "web" && { zIndex },
2056
+ overlayStyle
2057
+ ],
1765
2058
  children: [/* @__PURE__ */ jsx(Animated.View, {
1766
2059
  style: [styles.backdrop, backdropStyle],
1767
2060
  children: /* @__PURE__ */ jsx(Pressable, {
@@ -1800,6 +2093,7 @@ const resolveDuration = (duration) => {
1800
2093
  const ModalRoot = ({ open, defaultOpen = false, onOpenChange, animation = "scale", duration, easing = "standard", closeOnOutsidePress = true, children }) => {
1801
2094
  const initialOpen = open ?? defaultOpen;
1802
2095
  const animationProgress = useRef(new Animated.Value(initialOpen ? 1 : 0));
2096
+ const triggerRef = useRef(null);
1803
2097
  const [shouldRender, setShouldRender] = useState(initialOpen);
1804
2098
  const [reduceMotion, setReduceMotion] = useState(false);
1805
2099
  const modal = useModal({
@@ -1808,6 +2102,15 @@ const ModalRoot = ({ open, defaultOpen = false, onOpenChange, animation = "scale
1808
2102
  onOpenChange,
1809
2103
  closeOnOutsidePress
1810
2104
  });
2105
+ const { restoreFocusAfterClose } = useOverlayFocusRestore({
2106
+ active: modal.open,
2107
+ triggerRef
2108
+ });
2109
+ const previousOpenRef = useRef(modal.open);
2110
+ useEffect(() => {
2111
+ if (previousOpenRef.current && !modal.open) restoreFocusAfterClose();
2112
+ previousOpenRef.current = modal.open;
2113
+ }, [modal.open, restoreFocusAfterClose]);
1811
2114
  const dismiss = useOverlayDismiss({
1812
2115
  id: modal.contentId,
1813
2116
  active: modal.open,
@@ -1860,16 +2163,18 @@ const ModalRoot = ({ open, defaultOpen = false, onOpenChange, animation = "scale
1860
2163
  modal.open,
1861
2164
  shouldAnimate
1862
2165
  ]);
1863
- return /* @__PURE__ */ jsx(ModalContext.Provider, {
2166
+ return /* @__PURE__ */ jsx(ModalProvider, {
1864
2167
  value: {
1865
2168
  animation,
1866
2169
  animationProgress: animationProgress.current,
1867
2170
  closeOnOutsidePress: modal.closeOnOutsidePress,
2171
+ zIndex: dismiss.zIndex,
1868
2172
  onClose: dismiss.requestClose,
1869
2173
  onOutsideClose: dismiss.requestOutsideClose,
1870
2174
  open: modal.open,
1871
2175
  setOpen: modal.setOpen,
1872
- shouldRender
2176
+ shouldRender,
2177
+ triggerRef
1873
2178
  },
1874
2179
  children
1875
2180
  });
@@ -1877,15 +2182,26 @@ const ModalRoot = ({ open, defaultOpen = false, onOpenChange, animation = "scale
1877
2182
  ModalRoot.displayName = "ModalRoot";
1878
2183
  //#endregion
1879
2184
  //#region src/components/Modal/Trigger/ModalTrigger.tsx
2185
+ const composeRefs = (...refs) => (node) => {
2186
+ for (const ref of refs) {
2187
+ if (typeof ref === "function") {
2188
+ ref(node);
2189
+ continue;
2190
+ }
2191
+ if (ref) ref.current = node;
2192
+ }
2193
+ };
1880
2194
  const ModalTrigger = ({ children, asChild = false, disabled = false, accessibilityLabel, style, testID }) => {
1881
2195
  const root = useModalContext();
1882
2196
  const child = asChild && isValidElement(children) ? children : void 0;
2197
+ const composedTriggerRef = child ? composeRefs(root.triggerRef, child.props.ref) : root.triggerRef;
1883
2198
  const handlePress = (event) => {
1884
2199
  if (disabled || child?.props.disabled) return;
1885
2200
  child?.props.onPress?.(event);
1886
2201
  root.setOpen(true);
1887
2202
  };
1888
2203
  if (child) return cloneElement(child, {
2204
+ ref: composedTriggerRef,
1889
2205
  onPress: handlePress,
1890
2206
  accessibilityRole: child.props.accessibilityRole ?? "button",
1891
2207
  accessibilityState: {
@@ -1898,6 +2214,7 @@ const ModalTrigger = ({ children, asChild = false, disabled = false, accessibili
1898
2214
  style: child.props.style
1899
2215
  });
1900
2216
  return /* @__PURE__ */ jsx(Pressable, {
2217
+ ref: root.triggerRef,
1901
2218
  accessibilityRole: "button",
1902
2219
  accessibilityState: {
1903
2220
  expanded: root.open,
@@ -2067,7 +2384,7 @@ PortalProvider.displayName = "PortalProvider";
2067
2384
  //#endregion
2068
2385
  //#region src/components/Popover/Content/PopoverContent.styles.ts
2069
2386
  const styles = StyleSheet.create({
2070
- layer: { flex: 1 },
2387
+ root: { flex: 1 },
2071
2388
  backdrop: StyleSheet.absoluteFill,
2072
2389
  content: { position: "absolute" }
2073
2390
  });
@@ -2099,7 +2416,7 @@ function PopoverContent({ children, style, ...contentProps }) {
2099
2416
  const { theme } = useTheme();
2100
2417
  const layerRef = useRef(null);
2101
2418
  const themedStyles = useMemo(() => createPopoverContentStyles(theme), [theme]);
2102
- const { open, position, onFloatingLayout, updatePosition, setOpen, closeOnOutsidePress } = usePopoverContext("Popover.Content");
2419
+ const { open, zIndex, position, onFloatingLayout, updatePosition, requestClose, requestOutsideClose, closeOnOutsidePress } = usePopoverContext("Popover.Content");
2103
2420
  useEffect(() => {
2104
2421
  if (!open) return;
2105
2422
  requestAnimationFrame(() => {
@@ -2108,20 +2425,16 @@ function PopoverContent({ children, style, ...contentProps }) {
2108
2425
  }, [open, updatePosition]);
2109
2426
  return /* @__PURE__ */ jsx(Portal, {
2110
2427
  visible: open,
2111
- onRequestClose: () => {
2112
- setOpen(false, { reason: "escape-key" });
2113
- },
2428
+ onRequestClose: requestClose,
2114
2429
  children: /* @__PURE__ */ jsxs(View, {
2115
2430
  ref: layerRef,
2116
2431
  pointerEvents: "box-none",
2117
- style: styles.layer,
2432
+ style: [styles.root, { zIndex }],
2118
2433
  children: [/* @__PURE__ */ jsx(Pressable, {
2119
2434
  testID: "popover-backdrop",
2120
2435
  accessibilityLabel: closeOnOutsidePress ? "Close popover" : void 0,
2121
2436
  accessibilityRole: closeOnOutsidePress ? "button" : void 0,
2122
- onPress: closeOnOutsidePress ? () => {
2123
- setOpen(false, { reason: "outside-press" });
2124
- } : void 0,
2437
+ onPress: closeOnOutsidePress ? requestOutsideClose : void 0,
2125
2438
  style: styles.backdrop
2126
2439
  }), /* @__PURE__ */ jsx(View, {
2127
2440
  ...contentProps,
@@ -2167,6 +2480,7 @@ function getNativePlacement(side, align) {
2167
2480
  function PopoverRoot({ children, open: openProp, defaultOpen = false, onOpenChange, side = "bottom", align = "center", sideOffset = 8, closeOnOutsidePress = true }) {
2168
2481
  const triggerRef = useRef(null);
2169
2482
  const anchorRef = useRef(null);
2483
+ const overlayId = useId();
2170
2484
  const getReferenceRef = useCallback(() => anchorRef.current ? anchorRef : triggerRef, []);
2171
2485
  const openChangeDetailsRef = useRef({ reason: "programmatic" });
2172
2486
  const [open, setOpenState] = useControllableState({
@@ -2176,11 +2490,30 @@ function PopoverRoot({ children, open: openProp, defaultOpen = false, onOpenChan
2176
2490
  onOpenChange?.(nextOpen, openChangeDetailsRef.current);
2177
2491
  }
2178
2492
  });
2493
+ const { restoreFocusAfterClose } = useOverlayFocusRestore({
2494
+ active: open,
2495
+ triggerRef
2496
+ });
2179
2497
  const { position, arrowPosition, placement, updatePosition: updateFloatingPosition, onFloatingLayout } = useNativeFloatingPosition(getNativePlacement(side, align), sideOffset);
2180
2498
  const setOpen = useCallback((nextOpen, details) => {
2181
2499
  openChangeDetailsRef.current = details;
2182
2500
  setOpenState(nextOpen);
2183
- }, [setOpenState]);
2501
+ if (!nextOpen) restoreFocusAfterClose();
2502
+ }, [restoreFocusAfterClose, setOpenState]);
2503
+ const dismiss = useOverlayDismiss({
2504
+ id: overlayId,
2505
+ active: open,
2506
+ closeOnOutsidePress,
2507
+ requestClose: () => {
2508
+ setOpen(false, { reason: "escape-key" });
2509
+ },
2510
+ requestOutsideClose: () => {
2511
+ setOpen(false, { reason: "outside-press" });
2512
+ }
2513
+ });
2514
+ const updatePosition = useCallback((containerRef) => {
2515
+ updateFloatingPosition(getReferenceRef(), containerRef);
2516
+ }, [getReferenceRef, updateFloatingPosition]);
2184
2517
  return /* @__PURE__ */ jsx(PopoverProvider, {
2185
2518
  value: {
2186
2519
  open,
@@ -2190,12 +2523,13 @@ function PopoverRoot({ children, open: openProp, defaultOpen = false, onOpenChan
2190
2523
  side,
2191
2524
  align,
2192
2525
  placement,
2526
+ zIndex: dismiss.zIndex,
2193
2527
  position,
2194
2528
  arrowPosition,
2529
+ requestClose: dismiss.requestClose,
2530
+ requestOutsideClose: dismiss.requestOutsideClose,
2195
2531
  onFloatingLayout,
2196
- updatePosition: useCallback((containerRef) => {
2197
- updateFloatingPosition(getReferenceRef(), containerRef);
2198
- }, [getReferenceRef, updateFloatingPosition]),
2532
+ updatePosition,
2199
2533
  setOpen
2200
2534
  },
2201
2535
  children
@@ -2319,8 +2653,8 @@ const createStyles$10 = (theme) => StyleSheet.create({
2319
2653
  });
2320
2654
  //#endregion
2321
2655
  //#region src/primitives/Radio/Radio.tsx
2322
- const nativePointerEventsNone$4 = Platform.OS === "web" ? void 0 : { pointerEvents: "none" };
2323
- const webPointerEventsNone$4 = Platform.OS === "web" ? { pointerEvents: "none" } : void 0;
2656
+ const nativePointerEventsNone$3 = Platform.OS === "web" ? void 0 : { pointerEvents: "none" };
2657
+ const webPointerEventsNone$3 = Platform.OS === "web" ? { pointerEvents: "none" } : void 0;
2324
2658
  const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: disabledProp = false, required: requiredProp = false, size: sizeProp, color: colorProp, onCheckedChange, label, description, icon, error, accessibilityLabel, accessibilityHint, containerStyle, labelStyle, descriptionStyle, errorStyle, style, ...rest }, ref) => {
2325
2659
  const { theme } = useTheme();
2326
2660
  const styles = createStyles$10(theme);
@@ -2379,10 +2713,10 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
2379
2713
  onPress: handlePress,
2380
2714
  style: resolvePressableStyle,
2381
2715
  children: (state) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(View, {
2382
- ...nativePointerEventsNone$4,
2716
+ ...nativePointerEventsNone$3,
2383
2717
  style: [
2384
2718
  styles.control,
2385
- webPointerEventsNone$4,
2719
+ webPointerEventsNone$3,
2386
2720
  {
2387
2721
  width: radioSize.controlSize,
2388
2722
  height: radioSize.controlSize,
@@ -2411,8 +2745,8 @@ const Radio = forwardRef(({ value, checked, defaultChecked = false, disabled: di
2411
2745
  resolvedDisabled && styles.indicatorDisabled
2412
2746
  ] }))
2413
2747
  }), (label || description) && /* @__PURE__ */ jsxs(View, {
2414
- ...nativePointerEventsNone$4,
2415
- style: [styles.content, webPointerEventsNone$4],
2748
+ ...nativePointerEventsNone$3,
2749
+ style: [styles.content, webPointerEventsNone$3],
2416
2750
  children: [label && (typeof label === "string" ? /* @__PURE__ */ jsx(Text, {
2417
2751
  style: [
2418
2752
  styles.label,
@@ -2825,6 +3159,78 @@ RadioGroupRoot.displayName = "RadioGroup.Root";
2825
3159
  const RadioGroup = Object.assign(RadioGroupRoot, { Item: RadioGroupItem });
2826
3160
  RadioGroup.displayName = "RadioGroup";
2827
3161
  //#endregion
3162
+ //#region src/components/Select/Content/SelectContent.styles.ts
3163
+ const createContentStyles = (theme) => StyleSheet.create({
3164
+ toolbar: {
3165
+ minHeight: 52,
3166
+ flexDirection: "row",
3167
+ alignItems: "center",
3168
+ justifyContent: "space-between",
3169
+ paddingHorizontal: theme.tokens.spacing[4],
3170
+ borderBottomColor: theme.components.select.dropdown.separator.bg,
3171
+ borderBottomWidth: 1
3172
+ },
3173
+ title: {
3174
+ flex: 1,
3175
+ marginHorizontal: theme.tokens.spacing[3],
3176
+ color: theme.components.select.dropdown.fg,
3177
+ fontFamily: theme.tokens.typography.family.medium,
3178
+ fontSize: theme.tokens.typography.size.md,
3179
+ lineHeight: theme.tokens.typography.lineHeight.md,
3180
+ textAlign: "center"
3181
+ },
3182
+ toolbarAction: {
3183
+ minWidth: 64,
3184
+ minHeight: 44,
3185
+ alignItems: "center",
3186
+ justifyContent: "center"
3187
+ },
3188
+ cancelText: {
3189
+ color: theme.components.select.trigger.placeholder.fg,
3190
+ fontFamily: theme.tokens.typography.family.medium,
3191
+ fontSize: theme.tokens.typography.size.md,
3192
+ lineHeight: theme.tokens.typography.lineHeight.md
3193
+ },
3194
+ doneText: {
3195
+ color: theme.semantic.text.interactive,
3196
+ fontFamily: theme.tokens.typography.family.medium,
3197
+ fontSize: theme.tokens.typography.size.md,
3198
+ lineHeight: theme.tokens.typography.lineHeight.md
3199
+ },
3200
+ list: { maxHeight: 420 },
3201
+ listContent: {
3202
+ paddingHorizontal: theme.tokens.spacing[2],
3203
+ paddingVertical: theme.tokens.spacing[2]
3204
+ },
3205
+ empty: {
3206
+ minHeight: 72,
3207
+ alignItems: "center",
3208
+ justifyContent: "center",
3209
+ padding: theme.tokens.spacing[4]
3210
+ },
3211
+ emptyText: {
3212
+ color: theme.components.select.dropdown.empty.fg,
3213
+ fontFamily: theme.tokens.typography.family.regular,
3214
+ fontSize: theme.tokens.typography.size.md,
3215
+ lineHeight: theme.tokens.typography.lineHeight.md,
3216
+ textAlign: "center"
3217
+ },
3218
+ loading: {
3219
+ minHeight: 72,
3220
+ flexDirection: "row",
3221
+ alignItems: "center",
3222
+ justifyContent: "center",
3223
+ gap: theme.tokens.spacing[2],
3224
+ padding: theme.tokens.spacing[4]
3225
+ },
3226
+ loadingText: {
3227
+ color: theme.components.select.dropdown.fg,
3228
+ fontFamily: theme.tokens.typography.family.regular,
3229
+ fontSize: theme.tokens.typography.size.md,
3230
+ lineHeight: theme.tokens.typography.lineHeight.md
3231
+ }
3232
+ });
3233
+ //#endregion
2828
3234
  //#region src/components/Select/internal/types.ts
2829
3235
  const selectSlotName = Symbol("VelliraNativeSelectSlot");
2830
3236
  //#endregion
@@ -2952,6 +3358,33 @@ const parseSelectChildren = (children) => {
2952
3358
  };
2953
3359
  };
2954
3360
  //#endregion
3361
+ //#region src/components/Select/internal/SelectContext.tsx
3362
+ const SelectContext = createContext(null);
3363
+ const useSelectContext = () => {
3364
+ const context = useContext(SelectContext);
3365
+ if (!context) throw new Error("Select compound components must be used inside Select");
3366
+ return context;
3367
+ };
3368
+ //#endregion
3369
+ //#region src/components/Select/Empty/SelectEmpty.tsx
3370
+ const renderText$1 = (node, style) => {
3371
+ if (typeof node === "string" || typeof node === "number") return /* @__PURE__ */ jsx(Text, {
3372
+ style,
3373
+ children: node
3374
+ });
3375
+ return node;
3376
+ };
3377
+ const SelectEmpty = createSelectSlot("empty", "Select.Empty");
3378
+ const SelectEmptyState = () => {
3379
+ const styles = useThemeStyles(createContentStyles);
3380
+ const { empty } = useSelectContext();
3381
+ return /* @__PURE__ */ jsx(View, {
3382
+ style: styles.empty,
3383
+ children: renderText$1(empty, styles.emptyText)
3384
+ });
3385
+ };
3386
+ SelectEmptyState.displayName = "Select.EmptyState";
3387
+ //#endregion
2955
3388
  //#region src/components/Select/Group/SelectGroup.styles.ts
2956
3389
  const createGroupStyles = (theme) => StyleSheet.create({
2957
3390
  groupLabel: {
@@ -3035,24 +3468,6 @@ const SelectGroupActionRow = ({ label, selectLabel, selectedCount, itemCount, on
3035
3468
  SelectGroupLabelRow.displayName = "Select.GroupLabelRow";
3036
3469
  SelectGroupActionRow.displayName = "Select.GroupActionRow";
3037
3470
  //#endregion
3038
- //#region src/components/Select/Group/SelectLabel.tsx
3039
- const SelectLabel = createSelectSlot("label", "Select.Label");
3040
- //#endregion
3041
- //#region src/components/Select/Group/SelectSeparator.tsx
3042
- const SelectSeparator = createSelectSlot("separator", "Select.Separator");
3043
- const SelectSeparatorRow = () => {
3044
- return /* @__PURE__ */ jsx(View, { style: useThemeStyles(createGroupStyles).separator });
3045
- };
3046
- SelectSeparatorRow.displayName = "Select.SeparatorRow";
3047
- //#endregion
3048
- //#region src/components/Select/internal/SelectContext.tsx
3049
- const SelectContext = createContext(null);
3050
- const useSelectContext = () => {
3051
- const context = useContext(SelectContext);
3052
- if (!context) throw new Error("Select compound components must be used inside Select");
3053
- return context;
3054
- };
3055
- //#endregion
3056
3471
  //#region src/components/Select/Item/SelectItem.styles.ts
3057
3472
  const createItemStyles = (theme) => StyleSheet.create({
3058
3473
  option: {
@@ -3204,14 +3619,29 @@ const SelectItemRow = ({ option, isSelected, isDisabled, optionStyle, onSelect }
3204
3619
  };
3205
3620
  SelectItemRow.displayName = "Select.ItemRow";
3206
3621
  //#endregion
3207
- //#region src/components/Select/Item/SelectItemBadge.tsx
3208
- const SelectItemBadge = createSelectSlot("itemBadge", "Select.ItemBadge");
3209
- //#endregion
3210
- //#region src/components/Select/Item/SelectItemDescription.tsx
3211
- const SelectItemDescription = createSelectSlot("itemDescription", "Select.ItemDescription");
3212
- //#endregion
3213
- //#region src/components/Select/Item/SelectItemIcon.tsx
3214
- const SelectItemIcon = createSelectSlot("itemIcon", "Select.ItemIcon");
3622
+ //#region src/components/Select/Loading/SelectLoading.tsx
3623
+ const renderText = (node, style) => {
3624
+ if (typeof node === "string" || typeof node === "number") return /* @__PURE__ */ jsx(Text, {
3625
+ style,
3626
+ children: node
3627
+ });
3628
+ return node;
3629
+ };
3630
+ const SelectLoading = createSelectSlot("loading", "Select.Loading");
3631
+ const SelectLoadingState = () => {
3632
+ const { theme } = useTheme();
3633
+ const styles = useThemeStyles(createContentStyles);
3634
+ const { loadingContent } = useSelectContext();
3635
+ return /* @__PURE__ */ jsxs(View, {
3636
+ style: styles.loading,
3637
+ children: [/* @__PURE__ */ jsx(ActivityIndicator, {
3638
+ testID: "select-content-loading-indicator",
3639
+ size: "small",
3640
+ color: theme.components.select.dropdown.fg
3641
+ }), renderText(loadingContent, styles.loadingText)]
3642
+ });
3643
+ };
3644
+ SelectLoadingState.displayName = "Select.LoadingState";
3215
3645
  //#endregion
3216
3646
  //#region src/components/Select/Presentation/SelectPresentation.styles.ts
3217
3647
  const createPresentationStyles = (theme) => StyleSheet.create({
@@ -3222,9 +3652,6 @@ const createPresentationStyles = (theme) => StyleSheet.create({
3222
3652
  justifyContent: "center",
3223
3653
  padding: theme.tokens.spacing[4]
3224
3654
  },
3225
- popoverRoot: { padding: theme.tokens.spacing[4] },
3226
- popoverRootTop: { justifyContent: "flex-start" },
3227
- popoverRootBottom: { justifyContent: "flex-end" },
3228
3655
  backdrop: {
3229
3656
  ...StyleSheet.absoluteFill,
3230
3657
  backgroundColor: theme.semantic.overlay.backdrop
@@ -3251,17 +3678,8 @@ const createPresentationStyles = (theme) => StyleSheet.create({
3251
3678
  width: "100%",
3252
3679
  maxWidth: 420,
3253
3680
  maxHeight: "60%",
3254
- alignSelf: "center",
3255
3681
  borderRadius: theme.tokens.radius.lg
3256
3682
  },
3257
- popoverTop: {
3258
- marginBottom: theme.tokens.spacing[8],
3259
- alignSelf: "center"
3260
- },
3261
- popoverBottom: {
3262
- marginTop: theme.tokens.spacing[8],
3263
- alignSelf: "center"
3264
- },
3265
3683
  handleWrap: {
3266
3684
  alignItems: "center",
3267
3685
  paddingTop: theme.tokens.spacing[3]
@@ -3297,46 +3715,18 @@ const SelectHandle = () => {
3297
3715
  SelectHandle.displayName = "Select.Handle";
3298
3716
  //#endregion
3299
3717
  //#region src/components/Select/Presentation/SelectModal.tsx
3300
- const SelectModal = ({ visible, onClose, dismissOnBackdropPress, contentStyle, children }) => {
3718
+ const SelectModal = ({ visible, onClose, dismissOnBackdropPress, zIndex, contentStyle, children }) => {
3301
3719
  const styles = useThemeStyles(createPresentationStyles);
3302
3720
  return /* @__PURE__ */ jsx(Modal$1, {
3303
3721
  transparent: true,
3304
3722
  visible,
3305
3723
  animationType: "slide",
3306
3724
  onRequestClose: onClose,
3307
- children: /* @__PURE__ */ jsxs(View, {
3308
- style: [styles.modalRoot, styles.modalPresentationRoot],
3309
- testID: "select-content-root",
3310
- children: [/* @__PURE__ */ jsx(SelectBackdrop, {
3311
- onClose,
3312
- dismissOnBackdropPress
3313
- }), /* @__PURE__ */ jsx(View, {
3314
- style: [
3315
- styles.content,
3316
- styles.modalPresentation,
3317
- contentStyle
3318
- ],
3319
- testID: "select-modal",
3320
- children
3321
- })]
3322
- })
3323
- });
3324
- };
3325
- SelectModal.displayName = "Select.Modal";
3326
- //#endregion
3327
- //#region src/components/Select/Presentation/SelectPopover.tsx
3328
- const SelectPopover = ({ visible, onClose, dismissOnBackdropPress, placement, matchTriggerWidth, triggerWidth, contentStyle, children }) => {
3329
- const styles = useThemeStyles(createPresentationStyles);
3330
- return /* @__PURE__ */ jsx(Modal$1, {
3331
- transparent: true,
3332
- visible,
3333
- animationType: "fade",
3334
- onRequestClose: onClose,
3335
3725
  children: /* @__PURE__ */ jsxs(View, {
3336
3726
  style: [
3337
3727
  styles.modalRoot,
3338
- styles.popoverRoot,
3339
- placement === "top" ? styles.popoverRootTop : styles.popoverRootBottom
3728
+ styles.modalPresentationRoot,
3729
+ Platform.OS === "web" && { zIndex }
3340
3730
  ],
3341
3731
  testID: "select-content-root",
3342
3732
  children: [/* @__PURE__ */ jsx(SelectBackdrop, {
@@ -3345,163 +3735,85 @@ const SelectPopover = ({ visible, onClose, dismissOnBackdropPress, placement, ma
3345
3735
  }), /* @__PURE__ */ jsx(View, {
3346
3736
  style: [
3347
3737
  styles.content,
3348
- styles.popover,
3349
- placement === "top" ? styles.popoverTop : styles.popoverBottom,
3350
- matchTriggerWidth && triggerWidth ? { width: triggerWidth } : null,
3738
+ styles.modalPresentation,
3351
3739
  contentStyle
3352
3740
  ],
3353
- testID: "select-popover",
3741
+ testID: "select-modal",
3354
3742
  children
3355
3743
  })]
3356
- })
3357
- });
3358
- };
3359
- SelectPopover.displayName = "Select.Popover";
3360
- //#endregion
3361
- //#region src/components/Select/Presentation/SelectSheet.tsx
3362
- const SelectSheet = ({ visible, onClose, dismissOnBackdropPress, contentStyle, children }) => {
3363
- const styles = useThemeStyles(createPresentationStyles);
3364
- return /* @__PURE__ */ jsx(Modal$1, {
3365
- transparent: true,
3366
- visible,
3367
- animationType: "slide",
3368
- onRequestClose: onClose,
3369
- children: /* @__PURE__ */ jsxs(View, {
3370
- style: [styles.modalRoot, styles.sheetRoot],
3371
- testID: "select-content-root",
3372
- children: [/* @__PURE__ */ jsx(SelectBackdrop, {
3373
- onClose,
3374
- dismissOnBackdropPress
3375
- }), /* @__PURE__ */ jsxs(View, {
3376
- style: [
3377
- styles.content,
3378
- styles.sheet,
3379
- contentStyle
3380
- ],
3381
- testID: "select-sheet",
3382
- children: [/* @__PURE__ */ jsx(SelectHandle, {}), children]
3383
- })]
3384
- })
3385
- });
3386
- };
3387
- SelectSheet.displayName = "Select.Sheet";
3388
- //#endregion
3389
- //#region src/components/Select/Content/SelectContent.styles.ts
3390
- const createContentStyles = (theme) => StyleSheet.create({
3391
- toolbar: {
3392
- minHeight: 52,
3393
- flexDirection: "row",
3394
- alignItems: "center",
3395
- justifyContent: "space-between",
3396
- paddingHorizontal: theme.tokens.spacing[4],
3397
- borderBottomColor: theme.components.select.dropdown.separator.bg,
3398
- borderBottomWidth: 1
3399
- },
3400
- title: {
3401
- flex: 1,
3402
- marginHorizontal: theme.tokens.spacing[3],
3403
- color: theme.components.select.dropdown.fg,
3404
- fontFamily: theme.tokens.typography.family.medium,
3405
- fontSize: theme.tokens.typography.size.md,
3406
- lineHeight: theme.tokens.typography.lineHeight.md,
3407
- textAlign: "center"
3408
- },
3409
- toolbarAction: {
3410
- minWidth: 64,
3411
- minHeight: 44,
3412
- alignItems: "center",
3413
- justifyContent: "center"
3414
- },
3415
- cancelText: {
3416
- color: theme.components.select.trigger.placeholder.fg,
3417
- fontFamily: theme.tokens.typography.family.medium,
3418
- fontSize: theme.tokens.typography.size.md,
3419
- lineHeight: theme.tokens.typography.lineHeight.md
3420
- },
3421
- doneText: {
3422
- color: theme.semantic.text.interactive,
3423
- fontFamily: theme.tokens.typography.family.medium,
3424
- fontSize: theme.tokens.typography.size.md,
3425
- lineHeight: theme.tokens.typography.lineHeight.md
3426
- },
3427
- list: { maxHeight: 420 },
3428
- listContent: {
3429
- paddingHorizontal: theme.tokens.spacing[2],
3430
- paddingVertical: theme.tokens.spacing[2]
3431
- },
3432
- empty: {
3433
- minHeight: 72,
3434
- alignItems: "center",
3435
- justifyContent: "center",
3436
- padding: theme.tokens.spacing[4]
3437
- },
3438
- emptyText: {
3439
- color: theme.components.select.dropdown.empty.fg,
3440
- fontFamily: theme.tokens.typography.family.regular,
3441
- fontSize: theme.tokens.typography.size.md,
3442
- lineHeight: theme.tokens.typography.lineHeight.md,
3443
- textAlign: "center"
3444
- },
3445
- loading: {
3446
- minHeight: 72,
3447
- flexDirection: "row",
3448
- alignItems: "center",
3449
- justifyContent: "center",
3450
- gap: theme.tokens.spacing[2],
3451
- padding: theme.tokens.spacing[4]
3452
- },
3453
- loadingText: {
3454
- color: theme.components.select.dropdown.fg,
3455
- fontFamily: theme.tokens.typography.family.regular,
3456
- fontSize: theme.tokens.typography.size.md,
3457
- lineHeight: theme.tokens.typography.lineHeight.md
3458
- }
3459
- });
3460
- //#endregion
3461
- //#region src/components/Select/Content/SelectEmpty.tsx
3462
- const renderText$1 = (node, style) => {
3463
- if (typeof node === "string" || typeof node === "number") return /* @__PURE__ */ jsx(Text, {
3464
- style,
3465
- children: node
3466
- });
3467
- return node;
3468
- };
3469
- const SelectEmpty = createSelectSlot("empty", "Select.Empty");
3470
- const SelectEmptyState = () => {
3471
- const styles = useThemeStyles(createContentStyles);
3472
- const { empty } = useSelectContext();
3473
- return /* @__PURE__ */ jsx(View, {
3474
- style: styles.empty,
3475
- children: renderText$1(empty, styles.emptyText)
3744
+ })
3476
3745
  });
3477
3746
  };
3478
- SelectEmptyState.displayName = "Select.EmptyState";
3747
+ SelectModal.displayName = "Select.Modal";
3479
3748
  //#endregion
3480
- //#region src/components/Select/Content/SelectLoading.tsx
3481
- const renderText = (node, style) => {
3482
- if (typeof node === "string" || typeof node === "number") return /* @__PURE__ */ jsx(Text, {
3483
- style,
3484
- children: node
3749
+ //#region src/components/Select/Presentation/SelectPopover.tsx
3750
+ const SelectPopover = ({ visible, onClose, dismissOnBackdropPress, zIndex, position, onFloatingLayout, matchTriggerWidth, triggerWidth, contentStyle, children }) => {
3751
+ const styles = useThemeStyles(createPresentationStyles);
3752
+ return /* @__PURE__ */ jsx(Modal$1, {
3753
+ transparent: true,
3754
+ visible,
3755
+ animationType: "fade",
3756
+ onRequestClose: onClose,
3757
+ children: /* @__PURE__ */ jsxs(View, {
3758
+ style: [styles.modalRoot, Platform.OS === "web" && { zIndex }],
3759
+ testID: "select-content-root",
3760
+ children: [/* @__PURE__ */ jsx(SelectBackdrop, {
3761
+ onClose,
3762
+ dismissOnBackdropPress
3763
+ }), /* @__PURE__ */ jsx(View, {
3764
+ onLayout: onFloatingLayout,
3765
+ style: [
3766
+ styles.content,
3767
+ styles.popover,
3768
+ {
3769
+ position: "absolute",
3770
+ top: position.top,
3771
+ left: position.left
3772
+ },
3773
+ matchTriggerWidth && triggerWidth ? { width: triggerWidth } : null,
3774
+ contentStyle
3775
+ ],
3776
+ testID: "select-popover",
3777
+ children
3778
+ })]
3779
+ })
3485
3780
  });
3486
- return node;
3487
3781
  };
3488
- const SelectLoading = createSelectSlot("loading", "Select.Loading");
3489
- const SelectLoadingState = () => {
3490
- const { theme } = useTheme();
3491
- const styles = useThemeStyles(createContentStyles);
3492
- const { loadingContent } = useSelectContext();
3493
- return /* @__PURE__ */ jsxs(View, {
3494
- style: styles.loading,
3495
- children: [/* @__PURE__ */ jsx(ActivityIndicator, {
3496
- testID: "select-content-loading-indicator",
3497
- size: "small",
3498
- color: theme.components.select.dropdown.fg
3499
- }), renderText(loadingContent, styles.loadingText)]
3782
+ SelectPopover.displayName = "Select.Popover";
3783
+ //#endregion
3784
+ //#region src/components/Select/Presentation/SelectSheet.tsx
3785
+ const SelectSheet = ({ visible, onClose, dismissOnBackdropPress, zIndex, contentStyle, children }) => {
3786
+ const styles = useThemeStyles(createPresentationStyles);
3787
+ return /* @__PURE__ */ jsx(Modal$1, {
3788
+ transparent: true,
3789
+ visible,
3790
+ animationType: "slide",
3791
+ onRequestClose: onClose,
3792
+ children: /* @__PURE__ */ jsxs(View, {
3793
+ style: [
3794
+ styles.modalRoot,
3795
+ styles.sheetRoot,
3796
+ Platform.OS === "web" && { zIndex }
3797
+ ],
3798
+ testID: "select-content-root",
3799
+ children: [/* @__PURE__ */ jsx(SelectBackdrop, {
3800
+ onClose,
3801
+ dismissOnBackdropPress
3802
+ }), /* @__PURE__ */ jsxs(View, {
3803
+ style: [
3804
+ styles.content,
3805
+ styles.sheet,
3806
+ contentStyle
3807
+ ],
3808
+ testID: "select-sheet",
3809
+ children: [/* @__PURE__ */ jsx(SelectHandle, {}), children]
3810
+ })]
3811
+ })
3500
3812
  });
3501
3813
  };
3502
- SelectLoadingState.displayName = "Select.LoadingState";
3814
+ SelectSheet.displayName = "Select.Sheet";
3503
3815
  //#endregion
3504
- //#region src/components/Select/Content/SelectSearch.styles.ts
3816
+ //#region src/components/Select/Search/SelectSearch.styles.ts
3505
3817
  const createSearchStyles = (theme) => StyleSheet.create({
3506
3818
  searchWrap: {
3507
3819
  flexDirection: "row",
@@ -3543,7 +3855,7 @@ const createSearchStyles = (theme) => StyleSheet.create({
3543
3855
  }
3544
3856
  });
3545
3857
  //#endregion
3546
- //#region src/components/Select/Content/SelectSearch.tsx
3858
+ //#region src/components/Select/Search/SelectSearch.tsx
3547
3859
  const SelectSearch = createSelectSlot("search", "Select.Search");
3548
3860
  const SelectSearchField = () => {
3549
3861
  const { theme } = useTheme();
@@ -3598,6 +3910,13 @@ const SelectSearchField = () => {
3598
3910
  };
3599
3911
  SelectSearchField.displayName = "Select.SearchField";
3600
3912
  //#endregion
3913
+ //#region src/components/Select/Separator/SelectSeparator.tsx
3914
+ const SelectSeparator = createSelectSlot("separator", "Select.Separator");
3915
+ const SelectSeparatorRow = () => {
3916
+ return /* @__PURE__ */ jsx(View, { style: useThemeStyles(createGroupStyles).separator });
3917
+ };
3918
+ SelectSeparatorRow.displayName = "Select.SeparatorRow";
3919
+ //#endregion
3601
3920
  //#region src/components/Select/Content/SelectContent.tsx
3602
3921
  const SelectContent = createSelectSlot("content", "Select.Content");
3603
3922
  const SelectContentSurface = () => {
@@ -3605,7 +3924,7 @@ const SelectContentSurface = () => {
3605
3924
  const wasOpenRef = useRef(false);
3606
3925
  const [openCycle, setOpenCycle] = useState(0);
3607
3926
  const context = useSelectContext();
3608
- const { isOpen, resolvedPresentation, placement, dismissOnBackdropPress, contentStyle, matchTriggerWidth, triggerWidth, resolvedLabel, closeContent, searchable, loading, filteredRows, selectedValues, selectedOptions, maxSelected, optionStyle, selectOption, selectGroup, itemHeight, selectedRowIndex, query } = context;
3927
+ const { isOpen, resolvedPresentation, zIndex, position, onFloatingLayout, dismissOnBackdropPress, contentStyle, matchTriggerWidth, triggerWidth, resolvedLabel, closeContent, searchable, loading, filteredRows, selectedValues, selectedOptions, maxSelected, optionStyle, selectOption, selectGroup, itemHeight, selectedRowIndex, query } = context;
3609
3928
  const initialScrollIndex = Boolean(context.virtual) && selectedRowIndex > 0 && query === "" ? selectedRowIndex : void 0;
3610
3929
  useEffect(() => {
3611
3930
  if (isOpen && !wasOpenRef.current) setOpenCycle((cycle) => cycle + 1);
@@ -3696,6 +4015,7 @@ const SelectContentSurface = () => {
3696
4015
  visible: isOpen,
3697
4016
  onClose: closeContent,
3698
4017
  dismissOnBackdropPress,
4018
+ zIndex,
3699
4019
  contentStyle,
3700
4020
  children: body
3701
4021
  });
@@ -3703,7 +4023,9 @@ const SelectContentSurface = () => {
3703
4023
  visible: isOpen,
3704
4024
  onClose: closeContent,
3705
4025
  dismissOnBackdropPress,
3706
- placement,
4026
+ position,
4027
+ zIndex,
4028
+ onFloatingLayout,
3707
4029
  matchTriggerWidth,
3708
4030
  triggerWidth,
3709
4031
  contentStyle,
@@ -3713,26 +4035,29 @@ const SelectContentSurface = () => {
3713
4035
  visible: isOpen,
3714
4036
  onClose: closeContent,
3715
4037
  dismissOnBackdropPress,
4038
+ zIndex,
3716
4039
  contentStyle,
3717
4040
  children: body
3718
4041
  });
3719
4042
  };
3720
4043
  SelectContentSurface.displayName = "Select.ContentSurface";
3721
4044
  //#endregion
3722
- //#region src/components/Select/internal/useSelectAccessibility.ts
3723
- const useSelectAccessibility = ({ accessibilityLabel, accessibilityHint, label, description, error, invalid, placeholder, selectedLabel, hasFieldContext, fieldDescribedBy }) => {
3724
- const descriptionText = typeof description === "string" || typeof description === "number" ? String(description) : void 0;
3725
- const errorText = typeof error === "string" || typeof error === "number" ? String(error) : void 0;
3726
- return {
3727
- resolvedLabel: accessibilityLabel ?? label ?? selectedLabel ?? placeholder ?? "Select",
3728
- resolvedHint: accessibilityHint ?? (invalid && errorText ? errorText : descriptionText ? descriptionText : hasFieldContext && fieldDescribedBy ? "Opens a list of options" : void 0),
3729
- announce: (message) => {
3730
- AccessibilityInfo.announceForAccessibility?.(message);
3731
- }
3732
- };
3733
- };
4045
+ //#region src/components/Select/Icon/SelectIcon.tsx
4046
+ const SelectIcon = createSelectSlot("icon", "Select.Icon");
3734
4047
  //#endregion
3735
- //#region src/components/Select/internal/useSelectCollection.ts
4048
+ //#region src/components/Select/ItemBadge/SelectItemBadge.tsx
4049
+ const SelectItemBadge = createSelectSlot("itemBadge", "Select.ItemBadge");
4050
+ //#endregion
4051
+ //#region src/components/Select/ItemDescription/SelectItemDescription.tsx
4052
+ const SelectItemDescription = createSelectSlot("itemDescription", "Select.ItemDescription");
4053
+ //#endregion
4054
+ //#region src/components/Select/ItemIcon/SelectItemIcon.tsx
4055
+ const SelectItemIcon = createSelectSlot("itemIcon", "Select.ItemIcon");
4056
+ //#endregion
4057
+ //#region src/components/Select/Label/SelectLabel.tsx
4058
+ const SelectLabel = createSelectSlot("label", "Select.Label");
4059
+ //#endregion
4060
+ //#region src/hooks/behavior/select/useSelectCollection.ts
3736
4061
  const useSelectCollection = (children, optionsProp) => {
3737
4062
  const parsedChildren = useMemo(() => parseSelectChildren(children), [children]);
3738
4063
  const options = useMemo(() => [...optionsProp ?? [], ...parsedChildren.options], [optionsProp, parsedChildren.options]);
@@ -3753,14 +4078,7 @@ const useSelectCollection = (children, optionsProp) => {
3753
4078
  };
3754
4079
  };
3755
4080
  //#endregion
3756
- //#region src/components/Select/internal/useSelectPresentation.ts
3757
- const useSelectPresentation = (presentation = "auto") => {
3758
- const { width } = useWindowDimensions();
3759
- if (presentation === "auto") return width >= 768 ? "popover" : "sheet";
3760
- return presentation;
3761
- };
3762
- //#endregion
3763
- //#region src/components/Select/internal/useSelectSearch.ts
4081
+ //#region src/hooks/behavior/select/useSelectSearch.ts
3764
4082
  const useSelectSearch = ({ rows, isOpen, searchable, searchableFromChildren, onSearch, filterOptions, filter = defaultSelectFilter }) => {
3765
4083
  const [query, setQuery] = useState("");
3766
4084
  const shouldSearch = searchable ?? searchableFromChildren ?? Boolean(onSearch);
@@ -3815,8 +4133,18 @@ const useSelectSearch = ({ rows, isOpen, searchable, searchableFromChildren, onS
3815
4133
  };
3816
4134
  };
3817
4135
  //#endregion
3818
- //#region src/components/Select/Trigger/SelectIcon.tsx
3819
- const SelectIcon = createSelectSlot("icon", "Select.Icon");
4136
+ //#region src/components/Select/internal/resolveSelectAccessibility.ts
4137
+ const resolveSelectAccessibility = ({ accessibilityLabel, accessibilityHint, label, description, error, invalid, placeholder, selectedLabel, hasFieldContext, fieldDescribedBy }) => {
4138
+ const descriptionText = typeof description === "string" || typeof description === "number" ? String(description) : void 0;
4139
+ const errorText = typeof error === "string" || typeof error === "number" ? String(error) : void 0;
4140
+ return {
4141
+ resolvedLabel: accessibilityLabel ?? label ?? selectedLabel ?? placeholder ?? "Select",
4142
+ resolvedHint: accessibilityHint ?? (invalid && errorText ? errorText : descriptionText ? descriptionText : hasFieldContext && fieldDescribedBy ? "Opens a list of options" : void 0),
4143
+ announce: (message) => {
4144
+ AccessibilityInfo.announceForAccessibility?.(message);
4145
+ }
4146
+ };
4147
+ };
3820
4148
  //#endregion
3821
4149
  //#region src/components/Select/Trigger/SelectTrigger.styles.ts
3822
4150
  const createTriggerStyles = (theme) => StyleSheet.create({
@@ -3913,9 +4241,9 @@ const createTriggerStyles = (theme) => StyleSheet.create({
3913
4241
  //#endregion
3914
4242
  //#region src/components/Select/Trigger/SelectTrigger.tsx
3915
4243
  const SelectTriggerSlot = createSelectSlot("trigger", "Select.Trigger");
3916
- const nativePointerEventsNone$3 = Platform.OS === "web" ? void 0 : { pointerEvents: "none" };
4244
+ const nativePointerEventsNone$2 = Platform.OS === "web" ? void 0 : { pointerEvents: "none" };
3917
4245
  const nativePointerEventsBoxNone = Platform.OS === "web" ? void 0 : { pointerEvents: "box-none" };
3918
- const webPointerEventsNone$3 = Platform.OS === "web" ? { pointerEvents: "none" } : void 0;
4246
+ const webPointerEventsNone$2 = Platform.OS === "web" ? { pointerEvents: "none" } : void 0;
3919
4247
  const webPointerEventsBoxNone = Platform.OS === "web" ? { pointerEvents: "box-none" } : void 0;
3920
4248
  function SelectTrigger({ displayText, isPlaceholder, isOpen, size = "md", color = "primary", variant = "outline", disabled = false, required = false, hasError = false, hasValue = false, loading = false, clearable = false, startIcon, endIcon, prefix, suffix, accessibilityLabel, accessibilityHint, nativeID, accessibilityLabelledBy, ariaDescribedBy, triggerStyle, textStyle, onPress, onClear }) {
3921
4249
  const { theme } = useTheme();
@@ -4010,8 +4338,8 @@ function SelectTrigger({ displayText, isPlaceholder, isOpen, size = "md", color
4010
4338
  ],
4011
4339
  children: [
4012
4340
  startIcon && /* @__PURE__ */ jsx(View, {
4013
- ...nativePointerEventsNone$3,
4014
- style: [styles.startIcon, webPointerEventsNone$3],
4341
+ ...nativePointerEventsNone$2,
4342
+ style: [styles.startIcon, webPointerEventsNone$2],
4015
4343
  accessibilityElementsHidden: true,
4016
4344
  importantForAccessibility: "no",
4017
4345
  children: renderIcon(startIcon)
@@ -4033,8 +4361,8 @@ function SelectTrigger({ displayText, isPlaceholder, isOpen, size = "md", color
4033
4361
  size: "small",
4034
4362
  color: iconColor
4035
4363
  }) : showClearButton ? null : endIcon ? /* @__PURE__ */ jsx(View, {
4036
- ...nativePointerEventsNone$3,
4037
- style: [styles.endIcon, webPointerEventsNone$3],
4364
+ ...nativePointerEventsNone$2,
4365
+ style: [styles.endIcon, webPointerEventsNone$2],
4038
4366
  accessibilityElementsHidden: true,
4039
4367
  importantForAccessibility: "no",
4040
4368
  children: renderIcon(endIcon)
@@ -4073,19 +4401,18 @@ function SelectTrigger({ displayText, isPlaceholder, isOpen, size = "md", color
4073
4401
  }
4074
4402
  SelectTrigger.displayName = "SelectTrigger";
4075
4403
  //#endregion
4076
- //#region src/components/Select/Trigger/SelectValue.tsx
4077
- const SelectValue = createSelectSlot("value", "Select.Value");
4078
- //#endregion
4079
4404
  //#region src/components/Select/Root/SelectRoot.tsx
4080
4405
  function SelectRoot(props) {
4081
- const { label, description, error, invalid = false, required = false, disabled = false, placeholder = "Select...", color = "primary", variant = "outline", size, open, defaultOpen, onOpenChange, clearable = false, searchable: searchableProp, searchPlaceholder, loading = false, loadingText = "Loading...", onSearch, filterOptions, filter, empty, startIcon, endIcon, prefix, suffix, renderValue, renderOption, closeOnSelect, maxSelected, presentation = "auto", placement = "bottom", matchTriggerWidth = false, dismissOnBackdropPress = true, virtual, options: optionsProp, children, style, triggerStyle, textStyle, contentStyle, optionStyle, searchStyle, accessibilityLabel, accessibilityHint, testID } = props;
4406
+ const { label, description, error, invalid = false, required = false, disabled = false, placeholder = "Select...", color = "primary", variant = "outline", size, open, defaultOpen, onOpenChange, clearable = false, searchable: searchableProp, searchPlaceholder, loading = false, loadingText = "Loading...", onSearch, filterOptions, filter, empty, startIcon, endIcon, prefix, suffix, renderValue, renderOption, closeOnSelect, maxSelected, presentation = "auto", placement = "bottom-start", offset = 8, matchTriggerWidth = false, dismissOnBackdropPress = true, virtual, options: optionsProp, children, style, triggerStyle, textStyle, contentStyle, optionStyle, searchStyle, accessibilityLabel, accessibilityHint, testID } = props;
4082
4407
  const field = useFormFieldContext();
4083
4408
  const overlayId = useId();
4084
4409
  const hasOwnField = Boolean(label || description || error);
4085
4410
  const [triggerWidth, setTriggerWidth] = useState();
4411
+ const triggerRef = useRef(null);
4412
+ const { position, onFloatingLayout } = useNativeFloatingPosition(placement, offset);
4086
4413
  const searchInputRef = useRef(null);
4087
4414
  const selectedFocusValueRef = useRef(void 0);
4088
- const resolvedPresentation = useSelectPresentation(presentation);
4415
+ const resolvedPresentation = useOverlayPresentation(presentation);
4089
4416
  const { options, rows, searchableFromChildren, searchPlaceholderFromChildren, emptyFromChildren, loadingFromChildren } = useSelectCollection(children, optionsProp);
4090
4417
  const resolvedSize = size ?? field?.size ?? "md";
4091
4418
  const isInvalid = invalid || Boolean(error) || !hasOwnField && Boolean(field?.invalid);
@@ -4110,7 +4437,18 @@ function SelectRoot(props) {
4110
4437
  defaultOpen,
4111
4438
  onOpenChange
4112
4439
  });
4113
- const selectedValues = Array.isArray(selectedValue) ? selectedValue : selectedValue ? [selectedValue] : [];
4440
+ const { restoreFocusAfterClose } = useOverlayFocusRestore({
4441
+ active: isOpen,
4442
+ triggerRef
4443
+ });
4444
+ const closeAndFocusTrigger = useCallback(() => {
4445
+ closeDropdown();
4446
+ restoreFocusAfterClose();
4447
+ }, [closeDropdown, restoreFocusAfterClose]);
4448
+ const selectedValues = useMemo(() => {
4449
+ if (props.multiple) return Array.isArray(selectedValue) ? selectedValue : [];
4450
+ return typeof selectedValue === "string" && selectedValue ? [selectedValue] : [];
4451
+ }, [props.multiple, selectedValue]);
4114
4452
  const selectedOption = options.find((option) => selectedValues.includes(option.value));
4115
4453
  const selectedOptions = options.filter((option) => selectedValues.includes(option.value));
4116
4454
  const optionsByValue = useMemo(() => new Map(options.filter((option) => !option.disabled).map((option) => [option.value, option])), [options]);
@@ -4143,7 +4481,7 @@ function SelectRoot(props) {
4143
4481
  selectedOption,
4144
4482
  selectedOptions
4145
4483
  ]);
4146
- const { resolvedLabel, resolvedHint, announce } = useSelectAccessibility({
4484
+ const { resolvedLabel, resolvedHint, announce } = resolveSelectAccessibility({
4147
4485
  accessibilityLabel,
4148
4486
  accessibilityHint,
4149
4487
  label: !hasOwnField ? void 0 : label,
@@ -4160,22 +4498,28 @@ function SelectRoot(props) {
4160
4498
  id: overlayId,
4161
4499
  active: isOpen,
4162
4500
  closeOnOutsidePress: dismissOnBackdropPress,
4163
- requestClose: closeDropdown
4501
+ requestClose: closeAndFocusTrigger
4164
4502
  });
4165
4503
  const clearValue = () => {
4166
4504
  selectedFocusValueRef.current = void 0;
4167
4505
  selectValue("");
4168
4506
  announce("Selection cleared");
4169
4507
  };
4170
- const selectOption = (option) => {
4508
+ const selectOption = useCallback((option) => {
4171
4509
  if (option.disabled) return;
4172
4510
  const selectedBefore = selectedValues.includes(option.value);
4173
4511
  if (Boolean(props.multiple) && !selectedBefore && typeof maxSelected === "number" && selectedValues.length >= maxSelected) return;
4174
4512
  selectedFocusValueRef.current = option.value;
4175
4513
  selectValue(option.value);
4176
4514
  announce(`${option.label} selected`);
4177
- };
4178
- const selectGroup = (values) => {
4515
+ }, [
4516
+ announce,
4517
+ maxSelected,
4518
+ props.multiple,
4519
+ selectValue,
4520
+ selectedValues
4521
+ ]);
4522
+ const selectGroup = useCallback((values) => {
4179
4523
  if (!props.multiple || values.length === 0) return;
4180
4524
  const enabledValues = values.filter((value) => optionsByValue.has(value));
4181
4525
  const selectedGroupValues = enabledValues.filter((value) => selectedValues.includes(value));
@@ -4196,67 +4540,97 @@ function SelectRoot(props) {
4196
4540
  setSelectedValue(nextValues);
4197
4541
  selectedFocusValueRef.current = nextValues.at(-1);
4198
4542
  announce("Group selected");
4199
- if (closeOnSelect) closeDropdown();
4200
- };
4201
- const contextValue = {
4202
- label,
4203
- description,
4204
- error,
4205
- placeholder,
4543
+ if (closeOnSelect) closeAndFocusTrigger();
4544
+ }, [
4545
+ announce,
4546
+ closeAndFocusTrigger,
4547
+ closeOnSelect,
4548
+ maxSelected,
4549
+ optionsByValue,
4550
+ props.multiple,
4551
+ selectedValues,
4552
+ setSelectedValue
4553
+ ]);
4554
+ const resolvedSearchPlaceholder = searchPlaceholder ?? searchPlaceholderFromChildren ?? "Search...";
4555
+ const resolvedEmpty = empty ?? emptyFromChildren ?? "Nothing found";
4556
+ const resolvedLoadingContent = loadingFromChildren ?? loadingText;
4557
+ const contextValue = useMemo(() => ({
4206
4558
  color,
4207
4559
  variant,
4208
- size: resolvedSize,
4209
4560
  isOpen,
4210
- hasValue,
4211
4561
  loading,
4212
- clearable,
4213
4562
  searchable: shouldSearch,
4214
4563
  multiple: Boolean(props.multiple),
4215
4564
  maxSelected,
4216
4565
  virtual,
4217
4566
  resolvedLabel,
4218
- resolvedHint,
4219
4567
  resolvedPresentation,
4220
- placement,
4568
+ zIndex: dismiss.zIndex,
4569
+ position,
4570
+ onFloatingLayout,
4221
4571
  dismissOnBackdropPress,
4222
4572
  matchTriggerWidth,
4223
4573
  triggerWidth,
4224
4574
  selectedValues,
4225
4575
  selectedOptions,
4226
4576
  optionsByValue,
4227
- rows,
4228
4577
  filteredRows,
4229
4578
  selectedRowIndex,
4230
4579
  itemHeight,
4231
4580
  query,
4232
- searchPlaceholder: searchPlaceholder ?? searchPlaceholderFromChildren ?? "Search...",
4581
+ searchPlaceholder: resolvedSearchPlaceholder,
4233
4582
  searchInputRef,
4234
- empty: empty ?? emptyFromChildren ?? "Nothing found",
4235
- loadingContent: loadingFromChildren ?? loadingText,
4583
+ empty: resolvedEmpty,
4584
+ loadingContent: resolvedLoadingContent,
4236
4585
  closeContent: dismiss.requestClose,
4237
- openContent: openDropdown,
4238
- clearValue,
4239
4586
  selectOption,
4240
4587
  selectGroup,
4241
4588
  setQuery,
4242
- renderValue,
4243
4589
  renderOption,
4244
- startIcon,
4245
- endIcon,
4246
- prefix,
4247
- suffix,
4248
- triggerStyle,
4249
- textStyle,
4250
4590
  contentStyle,
4251
4591
  optionStyle,
4252
- searchStyle,
4253
- fieldControlId: !hasOwnField ? field?.controlId : void 0,
4254
- fieldLabelId: !hasOwnField ? field?.labelId : void 0,
4255
- fieldDescribedBy: !hasOwnField ? field?.ariaDescribedBy : void 0
4256
- };
4592
+ searchStyle
4593
+ }), [
4594
+ color,
4595
+ variant,
4596
+ isOpen,
4597
+ loading,
4598
+ shouldSearch,
4599
+ props.multiple,
4600
+ maxSelected,
4601
+ virtual,
4602
+ resolvedLabel,
4603
+ resolvedPresentation,
4604
+ dismiss.zIndex,
4605
+ position,
4606
+ onFloatingLayout,
4607
+ dismissOnBackdropPress,
4608
+ matchTriggerWidth,
4609
+ triggerWidth,
4610
+ selectedValues,
4611
+ selectedOptions,
4612
+ optionsByValue,
4613
+ filteredRows,
4614
+ selectedRowIndex,
4615
+ itemHeight,
4616
+ query,
4617
+ resolvedSearchPlaceholder,
4618
+ searchInputRef,
4619
+ resolvedEmpty,
4620
+ resolvedLoadingContent,
4621
+ dismiss.requestClose,
4622
+ selectOption,
4623
+ selectGroup,
4624
+ setQuery,
4625
+ renderOption,
4626
+ contentStyle,
4627
+ optionStyle,
4628
+ searchStyle
4629
+ ]);
4257
4630
  const control = /* @__PURE__ */ jsx(SelectContext.Provider, {
4258
4631
  value: contextValue,
4259
4632
  children: /* @__PURE__ */ jsxs(View, {
4633
+ ref: triggerRef,
4260
4634
  testID,
4261
4635
  onLayout: (event) => setTriggerWidth(event.nativeEvent.layout.width),
4262
4636
  children: [/* @__PURE__ */ jsx(SelectTrigger, {
@@ -4303,6 +4677,9 @@ function SelectRoot(props) {
4303
4677
  }
4304
4678
  SelectRoot.displayName = "Select";
4305
4679
  //#endregion
4680
+ //#region src/components/Select/Value/SelectValue.tsx
4681
+ const SelectValue = createSelectSlot("value", "Select.Value");
4682
+ //#endregion
4306
4683
  //#region src/components/Select/Select.tsx
4307
4684
  const Select = Object.assign(SelectRoot, {
4308
4685
  Trigger: SelectTriggerSlot,
@@ -4321,7 +4698,7 @@ const Select = Object.assign(SelectRoot, {
4321
4698
  Loading: SelectLoading
4322
4699
  });
4323
4700
  //#endregion
4324
- //#region src/components/Tabs/TabsContext.tsx
4701
+ //#region src/components/Tabs/internal/TabsContext.tsx
4325
4702
  const TabsContext = createContext(null);
4326
4703
  const TabsProvider = TabsContext.Provider;
4327
4704
  const useTabs = () => {
@@ -4384,8 +4761,8 @@ const COLLAPSED_SIZE = 8;
4384
4761
  const LINE_ANIMATION_DURATION = 360;
4385
4762
  const SURFACE_ANIMATION_DURATION = 220;
4386
4763
  const easing = Easing?.bezier?.(.22, 1, .36, 1) ?? ((value) => value);
4387
- const nativePointerEventsNone$2 = Platform.OS === "web" ? void 0 : { pointerEvents: "none" };
4388
- const webPointerEventsNone$2 = Platform.OS === "web" ? { pointerEvents: "none" } : void 0;
4764
+ const nativePointerEventsNone$1 = Platform.OS === "web" ? void 0 : { pointerEvents: "none" };
4765
+ const webPointerEventsNone$1 = Platform.OS === "web" ? { pointerEvents: "none" } : void 0;
4389
4766
  const animateValue = (value, toValue, duration) => Animated.timing(value, {
4390
4767
  toValue,
4391
4768
  duration,
@@ -4588,8 +4965,8 @@ const TabsIndicator = ({ children, style }) => {
4588
4965
  return /* @__PURE__ */ jsx(Animated.View, {
4589
4966
  accessibilityElementsHidden: true,
4590
4967
  importantForAccessibility: "no-hide-descendants",
4591
- ...nativePointerEventsNone$2,
4592
- style: [indicatorStyle, webPointerEventsNone$2],
4968
+ ...nativePointerEventsNone$1,
4969
+ style: [indicatorStyle, webPointerEventsNone$1],
4593
4970
  children
4594
4971
  });
4595
4972
  };
@@ -5091,13 +5468,45 @@ const useTooltipContext = () => {
5091
5468
  };
5092
5469
  TooltipContext.displayName = "TooltipContext";
5093
5470
  //#endregion
5471
+ //#region src/components/Tooltip/Arrow/TooltipArrow.tsx
5472
+ function TooltipArrow() {
5473
+ const { theme } = useTheme();
5474
+ const tooltip = useTooltipContext();
5475
+ const size = theme.components.tooltip.arrow.size;
5476
+ const side = tooltip.placement.split("-")[0];
5477
+ const staticSide = {
5478
+ top: "bottom",
5479
+ right: "left",
5480
+ bottom: "top",
5481
+ left: "right"
5482
+ }[side];
5483
+ const crossAxisStyle = side === "left" || side === "right" ? {
5484
+ top: tooltip.arrowPosition.top ?? 0,
5485
+ marginTop: -size / 2
5486
+ } : {
5487
+ left: tooltip.arrowPosition.left ?? 0,
5488
+ marginLeft: -size / 2
5489
+ };
5490
+ return /* @__PURE__ */ jsx(View, {
5491
+ pointerEvents: "none",
5492
+ style: {
5493
+ position: "absolute",
5494
+ width: size,
5495
+ height: size,
5496
+ backgroundColor: theme.components.tooltip.arrow.bg,
5497
+ transform: [{ rotate: "45deg" }],
5498
+ [staticSide]: -size / 2,
5499
+ ...crossAxisStyle
5500
+ }
5501
+ });
5502
+ }
5503
+ //#endregion
5094
5504
  //#region src/components/Tooltip/Tooltip.styles.ts
5095
5505
  const createStyles$3 = (theme) => StyleSheet.create({
5096
5506
  root: { alignSelf: "flex-start" },
5097
5507
  overlay: { ...StyleSheet.absoluteFill },
5098
5508
  bubble: {
5099
5509
  position: "absolute",
5100
- zIndex: 1e3,
5101
5510
  maxWidth: theme.components.tooltip.content.maxWidth,
5102
5511
  paddingHorizontal: theme.components.tooltip.content.paddingX,
5103
5512
  paddingVertical: theme.components.tooltip.content.paddingY,
@@ -5130,8 +5539,6 @@ const createStyles$3 = (theme) => StyleSheet.create({
5130
5539
  });
5131
5540
  //#endregion
5132
5541
  //#region src/components/Tooltip/Content/TooltipContent.tsx
5133
- const nativePointerEventsNone$1 = Platform.OS === "web" ? void 0 : { pointerEvents: "none" };
5134
- const webPointerEventsNone$1 = Platform.OS === "web" ? { pointerEvents: "none" } : void 0;
5135
5542
  const TooltipContent = ({ children, forceMount = false, withArrow = false, style, textStyle }) => {
5136
5543
  const styles = useThemeStyles(createStyles$3);
5137
5544
  const tooltip = useTooltipContext();
@@ -5139,13 +5546,13 @@ const TooltipContent = ({ children, forceMount = false, withArrow = false, style
5139
5546
  if (!forceMount && !visible) return null;
5140
5547
  const bubble = /* @__PURE__ */ jsxs(View, {
5141
5548
  nativeID: tooltip.contentId,
5142
- ...nativePointerEventsNone$1,
5549
+ pointerEvents: "none",
5143
5550
  style: [
5144
5551
  styles.bubble,
5145
- webPointerEventsNone$1,
5146
5552
  {
5147
5553
  top: tooltip.position.top,
5148
- left: tooltip.position.left
5554
+ left: tooltip.position.left,
5555
+ zIndex: tooltip.zIndex
5149
5556
  },
5150
5557
  !visible && { display: "none" },
5151
5558
  style
@@ -5154,7 +5561,7 @@ const TooltipContent = ({ children, forceMount = false, withArrow = false, style
5154
5561
  children: [Children.map(children, (child) => typeof child === "string" || typeof child === "number" ? /* @__PURE__ */ jsx(Text, {
5155
5562
  style: [styles.text, textStyle],
5156
5563
  children: child
5157
- }) : child), withArrow && /* @__PURE__ */ jsx(InternalArrow, {})]
5564
+ }) : child), withArrow && /* @__PURE__ */ jsx(TooltipArrow, {})]
5158
5565
  });
5159
5566
  if (!visible) return bubble;
5160
5567
  return /* @__PURE__ */ jsx(Modal$1, {
@@ -5170,39 +5577,8 @@ const TooltipContent = ({ children, forceMount = false, withArrow = false, style
5170
5577
  });
5171
5578
  };
5172
5579
  TooltipContent.displayName = "Tooltip.Content";
5173
- function InternalArrow() {
5174
- const { theme } = useTheme();
5175
- const tooltip = useTooltipContext();
5176
- const size = theme.components.tooltip.arrow.size;
5177
- const side = tooltip.placement.split("-")[0];
5178
- const staticSide = {
5179
- top: "bottom",
5180
- right: "left",
5181
- bottom: "top",
5182
- left: "right"
5183
- }[side];
5184
- const crossAxisStyle = side === "left" || side === "right" ? {
5185
- top: tooltip.arrowPosition.top ?? 0,
5186
- marginTop: -size / 2
5187
- } : {
5188
- left: tooltip.arrowPosition.left ?? 0,
5189
- marginLeft: -size / 2
5190
- };
5191
- return /* @__PURE__ */ jsx(View, {
5192
- ...nativePointerEventsNone$1,
5193
- style: [{
5194
- position: "absolute",
5195
- width: size,
5196
- height: size,
5197
- backgroundColor: theme.components.tooltip.arrow.bg,
5198
- transform: [{ rotate: "45deg" }],
5199
- [staticSide]: -size / 2,
5200
- ...crossAxisStyle
5201
- }, webPointerEventsNone$1]
5202
- });
5203
- }
5204
5580
  //#endregion
5205
- //#region src/components/Tooltip/internal/useTooltipDelay.ts
5581
+ //#region src/components/Tooltip/internal/resolveTooltipDelay.ts
5206
5582
  const resolveTooltipDelay = (delay) => {
5207
5583
  if (typeof delay === "number") return {
5208
5584
  open: delay,
@@ -5291,6 +5667,7 @@ const TooltipRoot = ({ children, open: openProp, defaultOpen = false, onOpenChan
5291
5667
  setOpen,
5292
5668
  show,
5293
5669
  hide,
5670
+ zIndex: dismiss.zIndex,
5294
5671
  requestClose: dismiss.requestClose,
5295
5672
  requestOutsideClose: dismiss.requestOutsideClose,
5296
5673
  onFloatingLayout
@@ -5298,6 +5675,7 @@ const TooltipRoot = ({ children, open: openProp, defaultOpen = false, onOpenChan
5298
5675
  arrowPosition,
5299
5676
  contentId,
5300
5677
  disabled,
5678
+ dismiss.zIndex,
5301
5679
  dismiss.requestClose,
5302
5680
  dismiss.requestOutsideClose,
5303
5681
  hide,