@vellira-ui/react-native 2.55.0 → 2.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +222 -142
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
4
|
import { darkTheme, highContrastTheme, lightTheme } from "@vellira-ui/tokens";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region src/theme/fontWeight.ts
|
|
@@ -205,41 +205,59 @@ function useNativeFloatingPosition(placement = "top", offset = 8) {
|
|
|
205
205
|
};
|
|
206
206
|
}
|
|
207
207
|
//#endregion
|
|
208
|
-
//#region src/managers/
|
|
208
|
+
//#region src/managers/OverlayManager/NativeOverlayManager.ts
|
|
209
|
+
const BASE_LAYER = 1e3;
|
|
210
|
+
const LAYER_STEP = 10;
|
|
209
211
|
let stack = [];
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
stack = stack.filter((item) => item !== id);
|
|
213
|
-
|
|
212
|
+
const nativeOverlayManager = {
|
|
213
|
+
register(id) {
|
|
214
|
+
stack = stack.filter((item) => item.id !== id);
|
|
215
|
+
const entry = {
|
|
216
|
+
id,
|
|
217
|
+
layer: BASE_LAYER + stack.length * LAYER_STEP
|
|
218
|
+
};
|
|
219
|
+
stack.push(entry);
|
|
220
|
+
return entry;
|
|
214
221
|
},
|
|
215
|
-
|
|
216
|
-
stack = stack.filter((item) => item !== id);
|
|
222
|
+
unregister(id) {
|
|
223
|
+
stack = stack.filter((item) => item.id !== id);
|
|
217
224
|
},
|
|
218
225
|
isTop(id) {
|
|
219
|
-
return stack
|
|
226
|
+
return stack.at(-1)?.id === id;
|
|
227
|
+
},
|
|
228
|
+
getTop() {
|
|
229
|
+
return stack.at(-1);
|
|
230
|
+
},
|
|
231
|
+
getLayer(id) {
|
|
232
|
+
return stack.find((item) => item.id === id)?.layer ?? BASE_LAYER;
|
|
220
233
|
}
|
|
221
234
|
};
|
|
222
235
|
//#endregion
|
|
223
|
-
//#region src/managers/
|
|
224
|
-
const
|
|
236
|
+
//#region src/managers/OverlayManager/useNativeOverlayRegistration.ts
|
|
237
|
+
const useNativeOverlayRegistration = ({ id, visible }) => {
|
|
238
|
+
const [layer, setLayer] = useState(() => nativeOverlayManager.getLayer(id));
|
|
225
239
|
useEffect(() => {
|
|
226
240
|
if (!visible) return;
|
|
227
|
-
|
|
241
|
+
const entry = nativeOverlayManager.register(id);
|
|
242
|
+
setLayer(entry.layer);
|
|
228
243
|
return () => {
|
|
229
|
-
|
|
244
|
+
nativeOverlayManager.unregister(id);
|
|
230
245
|
};
|
|
231
246
|
}, [id, visible]);
|
|
232
|
-
return {
|
|
247
|
+
return {
|
|
248
|
+
layer,
|
|
249
|
+
isTopOverlay: useCallback(() => nativeOverlayManager.isTop(id), [id])
|
|
250
|
+
};
|
|
233
251
|
};
|
|
234
252
|
//#endregion
|
|
235
253
|
//#region src/hooks/behavior/overlay/useOverlayStack.ts
|
|
236
|
-
const useOverlayStack = ({ active, id }) =>
|
|
254
|
+
const useOverlayStack = ({ active, id }) => useNativeOverlayRegistration({
|
|
237
255
|
id,
|
|
238
256
|
visible: active
|
|
239
257
|
});
|
|
240
258
|
//#endregion
|
|
241
259
|
//#region src/hooks/behavior/overlay/useOverlayDismiss.ts
|
|
242
|
-
const useOverlayDismiss = ({ active, closeOnOutsidePress = true, id, requestClose }) => {
|
|
260
|
+
const useOverlayDismiss = ({ active, closeOnEscape = true, closeOnOutsidePress = true, id, requestClose }) => {
|
|
243
261
|
const { isTopOverlay } = useOverlayStack({
|
|
244
262
|
active,
|
|
245
263
|
id
|
|
@@ -248,16 +266,73 @@ const useOverlayDismiss = ({ active, closeOnOutsidePress = true, id, requestClos
|
|
|
248
266
|
if (!isTopOverlay()) return;
|
|
249
267
|
requestClose();
|
|
250
268
|
}, [isTopOverlay, requestClose]);
|
|
269
|
+
const requestOutsideClose = useCallback(() => {
|
|
270
|
+
if (!closeOnOutsidePress) return;
|
|
271
|
+
requestTopClose();
|
|
272
|
+
}, [closeOnOutsidePress, requestTopClose]);
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
if (!active || !closeOnEscape) return;
|
|
275
|
+
if (Platform.OS === "web") {
|
|
276
|
+
const handleKeyDown = (event) => {
|
|
277
|
+
if (event.key !== "Escape") return;
|
|
278
|
+
requestTopClose();
|
|
279
|
+
};
|
|
280
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
281
|
+
return () => {
|
|
282
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const subscription = BackHandler.addEventListener("hardwareBackPress", () => {
|
|
286
|
+
if (!isTopOverlay()) return false;
|
|
287
|
+
requestClose();
|
|
288
|
+
return true;
|
|
289
|
+
});
|
|
290
|
+
return () => {
|
|
291
|
+
subscription.remove();
|
|
292
|
+
};
|
|
293
|
+
}, [
|
|
294
|
+
active,
|
|
295
|
+
closeOnEscape,
|
|
296
|
+
isTopOverlay,
|
|
297
|
+
requestClose,
|
|
298
|
+
requestTopClose
|
|
299
|
+
]);
|
|
251
300
|
return {
|
|
252
301
|
isTopOverlay,
|
|
253
302
|
requestClose: requestTopClose,
|
|
254
|
-
requestOutsideClose
|
|
255
|
-
if (!closeOnOutsidePress) return;
|
|
256
|
-
requestTopClose();
|
|
257
|
-
}, [closeOnOutsidePress, requestTopClose])
|
|
303
|
+
requestOutsideClose
|
|
258
304
|
};
|
|
259
305
|
};
|
|
260
306
|
//#endregion
|
|
307
|
+
//#region src/hooks/behavior/overlay/useOverlayFocusRestore.ts
|
|
308
|
+
const useOverlayFocusRestore = ({ enabled = true, triggerRef }) => {
|
|
309
|
+
const restoreFocus = useCallback(() => {
|
|
310
|
+
if (!enabled) return;
|
|
311
|
+
if (Platform.OS === "web") {
|
|
312
|
+
const triggerNode = triggerRef.current;
|
|
313
|
+
if (triggerNode && typeof triggerNode === "object" && "focus" in triggerNode && typeof triggerNode.focus === "function") triggerNode.focus();
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (typeof findNodeHandle !== "function") return;
|
|
317
|
+
const handle = findNodeHandle(triggerRef.current);
|
|
318
|
+
if (handle && AccessibilityInfo.setAccessibilityFocus) AccessibilityInfo.setAccessibilityFocus(handle);
|
|
319
|
+
}, [enabled, triggerRef]);
|
|
320
|
+
return {
|
|
321
|
+
restoreFocus,
|
|
322
|
+
restoreFocusAfterClose: useCallback(() => {
|
|
323
|
+
if (!enabled) return;
|
|
324
|
+
requestAnimationFrame(restoreFocus);
|
|
325
|
+
}, [enabled, restoreFocus])
|
|
326
|
+
};
|
|
327
|
+
};
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/hooks/behavior/overlay/useOverlayPresentation.ts
|
|
330
|
+
function useOverlayPresentation(presentation = "auto", breakpoint = 768) {
|
|
331
|
+
const { width } = useWindowDimensions();
|
|
332
|
+
if (presentation === "auto") return width >= breakpoint ? "popover" : "sheet";
|
|
333
|
+
return presentation;
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
261
336
|
//#region src/hooks/useControllableState.ts
|
|
262
337
|
const useControllableState = ({ value, defaultValue, onChange }) => {
|
|
263
338
|
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
@@ -698,11 +773,7 @@ const createStyles$21 = (theme) => StyleSheet.create({
|
|
|
698
773
|
justifyContent: "center",
|
|
699
774
|
padding: theme.tokens.spacing[4]
|
|
700
775
|
},
|
|
701
|
-
popover: {
|
|
702
|
-
alignItems: "center",
|
|
703
|
-
justifyContent: "center",
|
|
704
|
-
padding: theme.tokens.spacing[4]
|
|
705
|
-
},
|
|
776
|
+
popover: { flex: 1 },
|
|
706
777
|
backdrop: {
|
|
707
778
|
...StyleSheet.absoluteFill,
|
|
708
779
|
backgroundColor: theme.semantic.overlay.backdrop
|
|
@@ -777,11 +848,12 @@ const createStyles$21 = (theme) => StyleSheet.create({
|
|
|
777
848
|
//#region src/components/Dropdown/Content/DropdownContent.tsx
|
|
778
849
|
const nativePointerEventsBoxNone$1 = Platform.OS === "web" ? void 0 : { pointerEvents: "box-none" };
|
|
779
850
|
const webPointerEventsBoxNone$1 = Platform.OS === "web" ? { pointerEvents: "box-none" } : void 0;
|
|
780
|
-
function DropdownContent({ isOpen, children, onClose, color = "primary", contentStyle, accessibilityLabel, presentation, searchable = false, searchValue = "", searchPlaceholder = "Search actions...", searchAccessibilityLabel, onSearchChange }) {
|
|
851
|
+
function DropdownContent({ isOpen, children, onClose, color = "primary", contentStyle, accessibilityLabel, presentation, position, onFloatingLayout, searchable = false, searchValue = "", searchPlaceholder = "Search actions...", searchAccessibilityLabel, onSearchChange }) {
|
|
781
852
|
const { theme } = useTheme();
|
|
782
853
|
const styles = useThemeStyles(createStyles$21);
|
|
783
854
|
const colorPalette = theme.components.dropdown[color];
|
|
784
855
|
const isSheet = presentation === "sheet";
|
|
856
|
+
const isPopover = presentation === "popover";
|
|
785
857
|
const animation = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
|
|
786
858
|
const [reduceMotion, setReduceMotion] = useState(false);
|
|
787
859
|
useEffect(() => {
|
|
@@ -857,9 +929,15 @@ function DropdownContent({ isOpen, children, onClose, color = "primary", content
|
|
|
857
929
|
styles.menu,
|
|
858
930
|
styles[`${presentation}Menu`],
|
|
859
931
|
{ borderColor: colorPalette.content.border },
|
|
932
|
+
isPopover && {
|
|
933
|
+
position: "absolute",
|
|
934
|
+
top: position.top,
|
|
935
|
+
left: position.left
|
|
936
|
+
},
|
|
860
937
|
contentStyle,
|
|
861
938
|
menuAnimatedStyle
|
|
862
939
|
],
|
|
940
|
+
onLayout: isPopover ? onFloatingLayout : void 0,
|
|
863
941
|
children: [searchable && /* @__PURE__ */ jsxs(View, {
|
|
864
942
|
style: styles.searchWrap,
|
|
865
943
|
children: [/* @__PURE__ */ jsx(View, {
|
|
@@ -887,27 +965,6 @@ function DropdownContent({ isOpen, children, onClose, color = "primary", content
|
|
|
887
965
|
}
|
|
888
966
|
DropdownContent.displayName = "DropdownContent";
|
|
889
967
|
//#endregion
|
|
890
|
-
//#region src/components/Dropdown/Group/DropdownGroup.styles.ts
|
|
891
|
-
const createStyles$20 = (theme) => StyleSheet.create({ groupLabel: {
|
|
892
|
-
paddingHorizontal: theme.tokens.spacing[4],
|
|
893
|
-
paddingVertical: theme.tokens.spacing[2],
|
|
894
|
-
color: theme.components.dropdown.groupLabel.fg,
|
|
895
|
-
fontFamily: theme.tokens.typography.family.medium,
|
|
896
|
-
fontSize: theme.tokens.typography.size.xs,
|
|
897
|
-
lineHeight: theme.tokens.typography.lineHeight.sm,
|
|
898
|
-
textTransform: "uppercase"
|
|
899
|
-
} });
|
|
900
|
-
//#endregion
|
|
901
|
-
//#region src/components/Dropdown/Group/DropdownGroup.tsx
|
|
902
|
-
function DropdownGroup({ label }) {
|
|
903
|
-
return /* @__PURE__ */ jsx(Text, {
|
|
904
|
-
accessibilityRole: "header",
|
|
905
|
-
style: useThemeStyles(createStyles$20).groupLabel,
|
|
906
|
-
children: label
|
|
907
|
-
});
|
|
908
|
-
}
|
|
909
|
-
DropdownGroup.displayName = "DropdownGroup";
|
|
910
|
-
//#endregion
|
|
911
968
|
//#region src/components/Dropdown/internal/DropdownCollection.ts
|
|
912
969
|
function createDropdownSlot(name, displayName) {
|
|
913
970
|
const Slot = () => null;
|
|
@@ -1011,8 +1068,65 @@ function getItemLabel(children) {
|
|
|
1011
1068
|
return labelParts.join("").trim();
|
|
1012
1069
|
}
|
|
1013
1070
|
//#endregion
|
|
1071
|
+
//#region src/components/Dropdown/Dropdown.styles.ts
|
|
1072
|
+
const createStyles$20 = (theme) => StyleSheet.create({
|
|
1073
|
+
root: { alignSelf: "flex-start" },
|
|
1074
|
+
emptyText: {
|
|
1075
|
+
minHeight: 44,
|
|
1076
|
+
paddingHorizontal: theme.tokens.spacing[3],
|
|
1077
|
+
paddingVertical: theme.tokens.spacing[3],
|
|
1078
|
+
color: theme.components.dropdown.item.disabled.fg,
|
|
1079
|
+
fontFamily: theme.tokens.typography.family.regular,
|
|
1080
|
+
fontSize: theme.tokens.typography.size.md,
|
|
1081
|
+
lineHeight: theme.tokens.typography.lineHeight.md
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
//#endregion
|
|
1085
|
+
//#region src/components/Dropdown/Group/DropdownGroup.styles.ts
|
|
1086
|
+
const createStyles$19 = (theme) => StyleSheet.create({ groupLabel: {
|
|
1087
|
+
paddingHorizontal: theme.tokens.spacing[4],
|
|
1088
|
+
paddingVertical: theme.tokens.spacing[2],
|
|
1089
|
+
color: theme.components.dropdown.groupLabel.fg,
|
|
1090
|
+
fontFamily: theme.tokens.typography.family.medium,
|
|
1091
|
+
fontSize: theme.tokens.typography.size.xs,
|
|
1092
|
+
lineHeight: theme.tokens.typography.lineHeight.sm,
|
|
1093
|
+
textTransform: "uppercase"
|
|
1094
|
+
} });
|
|
1095
|
+
//#endregion
|
|
1096
|
+
//#region src/components/Dropdown/Group/DropdownGroup.tsx
|
|
1097
|
+
function DropdownGroup({ label }) {
|
|
1098
|
+
return /* @__PURE__ */ jsx(Text, {
|
|
1099
|
+
accessibilityRole: "header",
|
|
1100
|
+
style: useThemeStyles(createStyles$19).groupLabel,
|
|
1101
|
+
children: label
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
DropdownGroup.displayName = "DropdownGroup";
|
|
1105
|
+
//#endregion
|
|
1106
|
+
//#region src/components/Dropdown/internal/DropdownUtils.ts
|
|
1107
|
+
function createDropdownSelectEvent() {
|
|
1108
|
+
let defaultPrevented = false;
|
|
1109
|
+
return {
|
|
1110
|
+
preventDefault: () => {
|
|
1111
|
+
defaultPrevented = true;
|
|
1112
|
+
},
|
|
1113
|
+
get defaultPrevented() {
|
|
1114
|
+
return defaultPrevented;
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
function filterDropdownEntries(parsed, searchValue) {
|
|
1119
|
+
const normalizedSearch = searchValue.trim().toLocaleLowerCase();
|
|
1120
|
+
const matchedItems = new Set(parsed.items.filter((item) => item.label.toLocaleLowerCase().includes(normalizedSearch)).map((item) => item.id));
|
|
1121
|
+
return {
|
|
1122
|
+
...parsed,
|
|
1123
|
+
items: parsed.items.filter((item) => matchedItems.has(item.id)),
|
|
1124
|
+
entries: parsed.entries.filter((entry) => entry.type !== "item" || matchedItems.has(entry.id))
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
//#endregion
|
|
1014
1128
|
//#region src/components/Dropdown/Item/DropdownItem.styles.ts
|
|
1015
|
-
const createStyles$
|
|
1129
|
+
const createStyles$18 = (theme) => StyleSheet.create({
|
|
1016
1130
|
item: {
|
|
1017
1131
|
minWidth: 0,
|
|
1018
1132
|
minHeight: 44,
|
|
@@ -1037,7 +1151,7 @@ const createStyles$19 = (theme) => StyleSheet.create({
|
|
|
1037
1151
|
//#region src/components/Dropdown/Item/DropdownItem.tsx
|
|
1038
1152
|
function DropdownItem({ label, value, rootColor = "primary", color = "default", icon, disabled = false, textWrap = "truncate", itemStyle, textStyle, onSelect }) {
|
|
1039
1153
|
const { theme } = useTheme();
|
|
1040
|
-
const styles = useThemeStyles(createStyles$
|
|
1154
|
+
const styles = useThemeStyles(createStyles$18);
|
|
1041
1155
|
const rootColorPalette = theme.components.dropdown[rootColor];
|
|
1042
1156
|
const itemColorPalette = color === "default" ? void 0 : theme.components.dropdown[color];
|
|
1043
1157
|
const renderColoredNode = (node, color) => {
|
|
@@ -1091,19 +1205,19 @@ function DropdownItem({ label, value, rootColor = "primary", color = "default",
|
|
|
1091
1205
|
DropdownItem.displayName = "DropdownItem";
|
|
1092
1206
|
//#endregion
|
|
1093
1207
|
//#region src/components/Dropdown/Separator/DropdownSeparator.styles.ts
|
|
1094
|
-
const createStyles$
|
|
1208
|
+
const createStyles$17 = (theme) => StyleSheet.create({ separator: {
|
|
1095
1209
|
height: 1,
|
|
1096
1210
|
backgroundColor: theme.components.dropdown.separator.bg
|
|
1097
1211
|
} });
|
|
1098
1212
|
//#endregion
|
|
1099
1213
|
//#region src/components/Dropdown/Separator/DropdownSeparator.tsx
|
|
1100
1214
|
function DropdownSeparator() {
|
|
1101
|
-
return /* @__PURE__ */ jsx(View, { style: useThemeStyles(createStyles$
|
|
1215
|
+
return /* @__PURE__ */ jsx(View, { style: useThemeStyles(createStyles$17).separator });
|
|
1102
1216
|
}
|
|
1103
1217
|
DropdownSeparator.displayName = "DropdownSeparator";
|
|
1104
1218
|
//#endregion
|
|
1105
1219
|
//#region src/components/Dropdown/Trigger/DropdownTrigger.styles.ts
|
|
1106
|
-
const createStyles$
|
|
1220
|
+
const createStyles$16 = (theme) => StyleSheet.create({
|
|
1107
1221
|
trigger: {
|
|
1108
1222
|
alignItems: "center",
|
|
1109
1223
|
justifyContent: "center",
|
|
@@ -1191,7 +1305,7 @@ const createStyles$17 = (theme) => StyleSheet.create({
|
|
|
1191
1305
|
//#region src/components/Dropdown/Trigger/DropdownTrigger.tsx
|
|
1192
1306
|
function DropdownTrigger({ asChild = false, label, trigger, children, icon, arrowIcon, showArrow = true, color = "primary", size = "md", disabled = false, isOpen, triggerStyle, triggerRef, accessibilityLabel, accessibilityHint, onPress }) {
|
|
1193
1307
|
const { theme } = useTheme();
|
|
1194
|
-
const styles = useThemeStyles(createStyles$
|
|
1308
|
+
const styles = useThemeStyles(createStyles$16);
|
|
1195
1309
|
const colorPalette = theme.components.dropdown[color];
|
|
1196
1310
|
const textSizeStyle = {
|
|
1197
1311
|
sm: styles.textSm,
|
|
@@ -1311,32 +1425,27 @@ function DropdownTrigger({ asChild = false, label, trigger, children, icon, arro
|
|
|
1311
1425
|
}
|
|
1312
1426
|
DropdownTrigger.displayName = "DropdownTrigger";
|
|
1313
1427
|
//#endregion
|
|
1314
|
-
//#region src/components/Dropdown/
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
emptyText: {
|
|
1318
|
-
minHeight: 44,
|
|
1319
|
-
paddingHorizontal: theme.tokens.spacing[3],
|
|
1320
|
-
paddingVertical: theme.tokens.spacing[3],
|
|
1321
|
-
color: theme.components.dropdown.item.disabled.fg,
|
|
1322
|
-
fontFamily: theme.tokens.typography.family.regular,
|
|
1323
|
-
fontSize: theme.tokens.typography.size.md,
|
|
1324
|
-
lineHeight: theme.tokens.typography.lineHeight.md
|
|
1325
|
-
}
|
|
1326
|
-
});
|
|
1327
|
-
//#endregion
|
|
1328
|
-
//#region src/components/Dropdown/Dropdown.tsx
|
|
1329
|
-
function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, showArrow = true, open, defaultOpen = false, onOpenChange, presentation = "auto", 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 }) {
|
|
1330
|
-
const styles = useThemeStyles(createStyles$16);
|
|
1428
|
+
//#region src/components/Dropdown/Root/DropdownRoot.tsx
|
|
1429
|
+
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 }) {
|
|
1430
|
+
const styles = useThemeStyles(createStyles$20);
|
|
1331
1431
|
const overlayId = useId();
|
|
1332
1432
|
const [uncontrolledSearchValue, setUncontrolledSearchValue] = useState(defaultSearchValue);
|
|
1333
1433
|
const resolvedSearchValue = searchValue ?? uncontrolledSearchValue;
|
|
1334
|
-
const { width } = useWindowDimensions();
|
|
1335
1434
|
const triggerRef = useRef(null);
|
|
1435
|
+
const setTriggerRef = useCallback((node) => {
|
|
1436
|
+
if (node && typeof node === "object" && "measureInWindow" in node && typeof node.measureInWindow === "function") {
|
|
1437
|
+
triggerRef.current = node;
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
triggerRef.current = null;
|
|
1441
|
+
}, []);
|
|
1336
1442
|
const parsed = useMemo(() => parseDropdownChildren(children), [children]);
|
|
1337
1443
|
const contentCommand = parsed.contentProps?.command ?? false;
|
|
1338
1444
|
const isSearchable = searchable || command || contentCommand || !!parsed.searchProps;
|
|
1339
|
-
const resolvedPresentation = presentation
|
|
1445
|
+
const resolvedPresentation = useOverlayPresentation(presentation);
|
|
1446
|
+
const contentStyleFromSlot = parsed.contentProps?.style;
|
|
1447
|
+
const contentPresentation = (parsed.contentProps?.presentation === "auto" ? void 0 : parsed.contentProps?.presentation) ?? resolvedPresentation;
|
|
1448
|
+
const { position, updatePosition, onFloatingLayout } = useNativeFloatingPosition(placement, offset);
|
|
1340
1449
|
const menuAccessibilityLabel = useMemo(() => {
|
|
1341
1450
|
if (accessibilityLabel) return accessibilityLabel;
|
|
1342
1451
|
return typeof label === "string" ? label : "Menu";
|
|
@@ -1363,24 +1472,23 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
|
|
|
1363
1472
|
getItemValue: (item) => item.value,
|
|
1364
1473
|
getItemText: (item) => typeof item.label === "string" ? item.label : item.value
|
|
1365
1474
|
});
|
|
1475
|
+
useEffect(() => {
|
|
1476
|
+
if (!isOpen || contentPresentation !== "popover") return;
|
|
1477
|
+
updatePosition(triggerRef);
|
|
1478
|
+
}, [
|
|
1479
|
+
isOpen,
|
|
1480
|
+
contentPresentation,
|
|
1481
|
+
updatePosition
|
|
1482
|
+
]);
|
|
1366
1483
|
useEffect(() => {
|
|
1367
1484
|
if (!isOpen) return;
|
|
1368
1485
|
AccessibilityInfo.announceForAccessibility(`${menuAccessibilityLabel} opened`);
|
|
1369
1486
|
}, [isOpen, menuAccessibilityLabel]);
|
|
1370
|
-
const
|
|
1371
|
-
if (Platform.OS === "web") {
|
|
1372
|
-
const triggerNode = triggerRef.current;
|
|
1373
|
-
if (triggerNode && typeof triggerNode === "object" && "focus" in triggerNode && typeof triggerNode.focus === "function") triggerNode.focus();
|
|
1374
|
-
return;
|
|
1375
|
-
}
|
|
1376
|
-
if (typeof findNodeHandle !== "function") return;
|
|
1377
|
-
const handle = findNodeHandle(triggerRef.current);
|
|
1378
|
-
if (handle && AccessibilityInfo.setAccessibilityFocus) AccessibilityInfo.setAccessibilityFocus(handle);
|
|
1379
|
-
}, []);
|
|
1487
|
+
const { restoreFocusAfterClose } = useOverlayFocusRestore({ triggerRef });
|
|
1380
1488
|
const closeAndFocusTrigger = useCallback(() => {
|
|
1381
1489
|
closeDropdown();
|
|
1382
|
-
|
|
1383
|
-
}, [closeDropdown,
|
|
1490
|
+
restoreFocusAfterClose();
|
|
1491
|
+
}, [closeDropdown, restoreFocusAfterClose]);
|
|
1384
1492
|
const dismiss = useOverlayDismiss({
|
|
1385
1493
|
id: overlayId,
|
|
1386
1494
|
active: isOpen,
|
|
@@ -1443,8 +1551,6 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
|
|
|
1443
1551
|
id: "empty",
|
|
1444
1552
|
props: { children: empty ?? "No actions found" }
|
|
1445
1553
|
}] : filteredParsed.entries;
|
|
1446
|
-
const contentStyleFromSlot = parsed.contentProps?.style;
|
|
1447
|
-
const presentationFromSlot = parsed.contentProps?.presentation === "auto" ? void 0 : parsed.contentProps?.presentation;
|
|
1448
1554
|
return /* @__PURE__ */ jsxs(View, {
|
|
1449
1555
|
style: [styles.root, style],
|
|
1450
1556
|
children: [/* @__PURE__ */ jsx(DropdownTrigger, {
|
|
@@ -1458,9 +1564,7 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
|
|
|
1458
1564
|
disabled: disabled || parsed.triggerProps?.disabled,
|
|
1459
1565
|
isOpen,
|
|
1460
1566
|
size,
|
|
1461
|
-
triggerRef:
|
|
1462
|
-
triggerRef.current = node;
|
|
1463
|
-
},
|
|
1567
|
+
triggerRef: setTriggerRef,
|
|
1464
1568
|
triggerStyle,
|
|
1465
1569
|
accessibilityLabel,
|
|
1466
1570
|
accessibilityHint,
|
|
@@ -1471,7 +1575,9 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
|
|
|
1471
1575
|
color,
|
|
1472
1576
|
contentStyle: [contentStyle, contentStyleFromSlot],
|
|
1473
1577
|
accessibilityLabel: menuAccessibilityLabel,
|
|
1474
|
-
presentation:
|
|
1578
|
+
presentation: contentPresentation,
|
|
1579
|
+
position,
|
|
1580
|
+
onFloatingLayout,
|
|
1475
1581
|
searchable: isSearchable,
|
|
1476
1582
|
searchValue: resolvedSearchValue,
|
|
1477
1583
|
searchPlaceholder: parsed.searchProps?.placeholder ?? searchPlaceholder ?? (command || contentCommand ? "Type a command..." : "Search actions..."),
|
|
@@ -1488,26 +1594,8 @@ function DropdownRoot({ children, label = "Menu", trigger, icon, arrowIcon, show
|
|
|
1488
1594
|
});
|
|
1489
1595
|
}
|
|
1490
1596
|
DropdownRoot.displayName = "Dropdown";
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
return {
|
|
1494
|
-
preventDefault: () => {
|
|
1495
|
-
defaultPrevented = true;
|
|
1496
|
-
},
|
|
1497
|
-
get defaultPrevented() {
|
|
1498
|
-
return defaultPrevented;
|
|
1499
|
-
}
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
1502
|
-
function filterDropdownEntries(parsed, searchValue) {
|
|
1503
|
-
const normalizedSearch = searchValue.trim().toLocaleLowerCase();
|
|
1504
|
-
const matchedItems = new Set(parsed.items.filter((item) => item.label.toLocaleLowerCase().includes(normalizedSearch)).map((item) => item.id));
|
|
1505
|
-
return {
|
|
1506
|
-
...parsed,
|
|
1507
|
-
items: parsed.items.filter((item) => matchedItems.has(item.id)),
|
|
1508
|
-
entries: parsed.entries.filter((entry) => entry.type !== "item" || matchedItems.has(entry.id))
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1597
|
+
//#endregion
|
|
1598
|
+
//#region src/components/Dropdown/Dropdown.tsx
|
|
1511
1599
|
const DropdownTriggerSlot = createDropdownSlot("trigger", "Dropdown.Trigger");
|
|
1512
1600
|
const DropdownContentSlot = createDropdownSlot("content", "Dropdown.Content");
|
|
1513
1601
|
const DropdownItemSlot = createDropdownSlot("item", "Dropdown.Item");
|
|
@@ -3199,9 +3287,6 @@ const createPresentationStyles = (theme) => StyleSheet.create({
|
|
|
3199
3287
|
justifyContent: "center",
|
|
3200
3288
|
padding: theme.tokens.spacing[4]
|
|
3201
3289
|
},
|
|
3202
|
-
popoverRoot: { padding: theme.tokens.spacing[4] },
|
|
3203
|
-
popoverRootTop: { justifyContent: "flex-start" },
|
|
3204
|
-
popoverRootBottom: { justifyContent: "flex-end" },
|
|
3205
3290
|
backdrop: {
|
|
3206
3291
|
...StyleSheet.absoluteFill,
|
|
3207
3292
|
backgroundColor: theme.semantic.overlay.backdrop
|
|
@@ -3228,17 +3313,8 @@ const createPresentationStyles = (theme) => StyleSheet.create({
|
|
|
3228
3313
|
width: "100%",
|
|
3229
3314
|
maxWidth: 420,
|
|
3230
3315
|
maxHeight: "60%",
|
|
3231
|
-
alignSelf: "center",
|
|
3232
3316
|
borderRadius: theme.tokens.radius.lg
|
|
3233
3317
|
},
|
|
3234
|
-
popoverTop: {
|
|
3235
|
-
marginBottom: theme.tokens.spacing[8],
|
|
3236
|
-
alignSelf: "center"
|
|
3237
|
-
},
|
|
3238
|
-
popoverBottom: {
|
|
3239
|
-
marginTop: theme.tokens.spacing[8],
|
|
3240
|
-
alignSelf: "center"
|
|
3241
|
-
},
|
|
3242
3318
|
handleWrap: {
|
|
3243
3319
|
alignItems: "center",
|
|
3244
3320
|
paddingTop: theme.tokens.spacing[3]
|
|
@@ -3302,7 +3378,7 @@ const SelectModal = ({ visible, onClose, dismissOnBackdropPress, contentStyle, c
|
|
|
3302
3378
|
SelectModal.displayName = "Select.Modal";
|
|
3303
3379
|
//#endregion
|
|
3304
3380
|
//#region src/components/Select/Presentation/SelectPopover.tsx
|
|
3305
|
-
const SelectPopover = ({ visible, onClose, dismissOnBackdropPress,
|
|
3381
|
+
const SelectPopover = ({ visible, onClose, dismissOnBackdropPress, position, onFloatingLayout, matchTriggerWidth, triggerWidth, contentStyle, children }) => {
|
|
3306
3382
|
const styles = useThemeStyles(createPresentationStyles);
|
|
3307
3383
|
return /* @__PURE__ */ jsx(Modal$1, {
|
|
3308
3384
|
transparent: true,
|
|
@@ -3310,20 +3386,21 @@ const SelectPopover = ({ visible, onClose, dismissOnBackdropPress, placement, ma
|
|
|
3310
3386
|
animationType: "fade",
|
|
3311
3387
|
onRequestClose: onClose,
|
|
3312
3388
|
children: /* @__PURE__ */ jsxs(View, {
|
|
3313
|
-
style:
|
|
3314
|
-
styles.modalRoot,
|
|
3315
|
-
styles.popoverRoot,
|
|
3316
|
-
placement === "top" ? styles.popoverRootTop : styles.popoverRootBottom
|
|
3317
|
-
],
|
|
3389
|
+
style: styles.modalRoot,
|
|
3318
3390
|
testID: "select-content-root",
|
|
3319
3391
|
children: [/* @__PURE__ */ jsx(SelectBackdrop, {
|
|
3320
3392
|
onClose,
|
|
3321
3393
|
dismissOnBackdropPress
|
|
3322
3394
|
}), /* @__PURE__ */ jsx(View, {
|
|
3395
|
+
onLayout: onFloatingLayout,
|
|
3323
3396
|
style: [
|
|
3324
3397
|
styles.content,
|
|
3325
3398
|
styles.popover,
|
|
3326
|
-
|
|
3399
|
+
{
|
|
3400
|
+
position: "absolute",
|
|
3401
|
+
top: position.top,
|
|
3402
|
+
left: position.left
|
|
3403
|
+
},
|
|
3327
3404
|
matchTriggerWidth && triggerWidth ? { width: triggerWidth } : null,
|
|
3328
3405
|
contentStyle
|
|
3329
3406
|
],
|
|
@@ -3582,7 +3659,7 @@ const SelectContentSurface = () => {
|
|
|
3582
3659
|
const wasOpenRef = useRef(false);
|
|
3583
3660
|
const [openCycle, setOpenCycle] = useState(0);
|
|
3584
3661
|
const context = useSelectContext();
|
|
3585
|
-
const { isOpen, resolvedPresentation,
|
|
3662
|
+
const { isOpen, resolvedPresentation, position, onFloatingLayout, dismissOnBackdropPress, contentStyle, matchTriggerWidth, triggerWidth, resolvedLabel, closeContent, searchable, loading, filteredRows, selectedValues, selectedOptions, maxSelected, optionStyle, selectOption, selectGroup, itemHeight, selectedRowIndex, query } = context;
|
|
3586
3663
|
const initialScrollIndex = Boolean(context.virtual) && selectedRowIndex > 0 && query === "" ? selectedRowIndex : void 0;
|
|
3587
3664
|
useEffect(() => {
|
|
3588
3665
|
if (isOpen && !wasOpenRef.current) setOpenCycle((cycle) => cycle + 1);
|
|
@@ -3680,7 +3757,8 @@ const SelectContentSurface = () => {
|
|
|
3680
3757
|
visible: isOpen,
|
|
3681
3758
|
onClose: closeContent,
|
|
3682
3759
|
dismissOnBackdropPress,
|
|
3683
|
-
|
|
3760
|
+
position,
|
|
3761
|
+
onFloatingLayout,
|
|
3684
3762
|
matchTriggerWidth,
|
|
3685
3763
|
triggerWidth,
|
|
3686
3764
|
contentStyle,
|
|
@@ -3730,13 +3808,6 @@ const useSelectCollection = (children, optionsProp) => {
|
|
|
3730
3808
|
};
|
|
3731
3809
|
};
|
|
3732
3810
|
//#endregion
|
|
3733
|
-
//#region src/components/Select/internal/useSelectPresentation.ts
|
|
3734
|
-
const useSelectPresentation = (presentation = "auto") => {
|
|
3735
|
-
const { width } = useWindowDimensions();
|
|
3736
|
-
if (presentation === "auto") return width >= 768 ? "popover" : "sheet";
|
|
3737
|
-
return presentation;
|
|
3738
|
-
};
|
|
3739
|
-
//#endregion
|
|
3740
3811
|
//#region src/components/Select/internal/useSelectSearch.ts
|
|
3741
3812
|
const useSelectSearch = ({ rows, isOpen, searchable, searchableFromChildren, onSearch, filterOptions, filter = defaultSelectFilter }) => {
|
|
3742
3813
|
const [query, setQuery] = useState("");
|
|
@@ -4055,14 +4126,16 @@ const SelectValue = createSelectSlot("value", "Select.Value");
|
|
|
4055
4126
|
//#endregion
|
|
4056
4127
|
//#region src/components/Select/Root/SelectRoot.tsx
|
|
4057
4128
|
function SelectRoot(props) {
|
|
4058
|
-
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;
|
|
4129
|
+
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;
|
|
4059
4130
|
const field = useFormFieldContext();
|
|
4060
4131
|
const overlayId = useId();
|
|
4061
4132
|
const hasOwnField = Boolean(label || description || error);
|
|
4062
4133
|
const [triggerWidth, setTriggerWidth] = useState();
|
|
4134
|
+
const triggerRef = useRef(null);
|
|
4135
|
+
const { position, placement: resolvedPlacement, updatePosition, onFloatingLayout } = useNativeFloatingPosition(placement, offset);
|
|
4063
4136
|
const searchInputRef = useRef(null);
|
|
4064
4137
|
const selectedFocusValueRef = useRef(void 0);
|
|
4065
|
-
const resolvedPresentation =
|
|
4138
|
+
const resolvedPresentation = useOverlayPresentation(presentation);
|
|
4066
4139
|
const { options, rows, searchableFromChildren, searchPlaceholderFromChildren, emptyFromChildren, loadingFromChildren } = useSelectCollection(children, optionsProp);
|
|
4067
4140
|
const resolvedSize = size ?? field?.size ?? "md";
|
|
4068
4141
|
const isInvalid = invalid || Boolean(error) || !hasOwnField && Boolean(field?.invalid);
|
|
@@ -4175,6 +4248,10 @@ function SelectRoot(props) {
|
|
|
4175
4248
|
announce("Group selected");
|
|
4176
4249
|
if (closeOnSelect) closeDropdown();
|
|
4177
4250
|
};
|
|
4251
|
+
const openContent = () => {
|
|
4252
|
+
updatePosition(triggerRef);
|
|
4253
|
+
openDropdown();
|
|
4254
|
+
};
|
|
4178
4255
|
const contextValue = {
|
|
4179
4256
|
label,
|
|
4180
4257
|
description,
|
|
@@ -4194,7 +4271,9 @@ function SelectRoot(props) {
|
|
|
4194
4271
|
resolvedLabel,
|
|
4195
4272
|
resolvedHint,
|
|
4196
4273
|
resolvedPresentation,
|
|
4197
|
-
placement,
|
|
4274
|
+
placement: resolvedPlacement,
|
|
4275
|
+
position,
|
|
4276
|
+
onFloatingLayout,
|
|
4198
4277
|
dismissOnBackdropPress,
|
|
4199
4278
|
matchTriggerWidth,
|
|
4200
4279
|
triggerWidth,
|
|
@@ -4211,7 +4290,7 @@ function SelectRoot(props) {
|
|
|
4211
4290
|
empty: empty ?? emptyFromChildren ?? "Nothing found",
|
|
4212
4291
|
loadingContent: loadingFromChildren ?? loadingText,
|
|
4213
4292
|
closeContent: dismiss.requestClose,
|
|
4214
|
-
openContent
|
|
4293
|
+
openContent,
|
|
4215
4294
|
clearValue,
|
|
4216
4295
|
selectOption,
|
|
4217
4296
|
selectGroup,
|
|
@@ -4234,6 +4313,7 @@ function SelectRoot(props) {
|
|
|
4234
4313
|
const control = /* @__PURE__ */ jsx(SelectContext.Provider, {
|
|
4235
4314
|
value: contextValue,
|
|
4236
4315
|
children: /* @__PURE__ */ jsxs(View, {
|
|
4316
|
+
ref: triggerRef,
|
|
4237
4317
|
testID,
|
|
4238
4318
|
onLayout: (event) => setTriggerWidth(event.nativeEvent.layout.width),
|
|
4239
4319
|
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|