@tamagui/select 1.0.0-beta.17 → 1.0.1-beta.102
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/cjs/BubbleSelect.js +51 -0
- package/dist/cjs/BubbleSelect.js.map +7 -0
- package/dist/cjs/Select.js +697 -270
- package/dist/cjs/Select.js.map +2 -2
- package/dist/esm/BubbleSelect.js +37 -0
- package/dist/esm/BubbleSelect.js.map +7 -0
- package/dist/esm/Select.js +764 -288
- package/dist/esm/Select.js.map +2 -2
- package/dist/jsx/BubbleSelect.js +22 -0
- package/dist/jsx/BubbleSelect.js.map +7 -0
- package/dist/jsx/Select.js +619 -205
- package/dist/jsx/Select.js.map +7 -0
- package/dist/jsx/index.js +1 -0
- package/dist/jsx/index.js.map +7 -0
- package/package.json +18 -13
- package/src/BubbleSelect.tsx +46 -0
- package/src/Select.tsx +1320 -0
- package/src/index.tsx +1 -0
- package/types/BubbleSelect.d.ts +2 -0
- package/types/BubbleSelect.d.ts.map +1 -0
- package/types/Select.d.ts +806 -0
- package/types/Select.d.ts.map +1 -0
- package/types/index.d.ts +0 -0
- package/types/index.d.ts.map +0 -0
package/dist/jsx/Select.js
CHANGED
|
@@ -1,21 +1,47 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
1
|
import {
|
|
2
|
+
FloatingFocusManager,
|
|
3
|
+
FloatingOverlay,
|
|
4
|
+
FloatingPortal,
|
|
5
|
+
autoUpdate,
|
|
6
|
+
detectOverflow,
|
|
7
|
+
flip,
|
|
4
8
|
offset,
|
|
5
|
-
|
|
9
|
+
size,
|
|
10
|
+
useClick,
|
|
11
|
+
useDismiss,
|
|
12
|
+
useFloating,
|
|
13
|
+
useInteractions,
|
|
14
|
+
useListNavigation,
|
|
15
|
+
useRole,
|
|
16
|
+
useTypeahead
|
|
6
17
|
} from "@floating-ui/react-dom-interactions";
|
|
7
18
|
import { usePrevious } from "@radix-ui/react-use-previous";
|
|
8
19
|
import { useComposedRefs } from "@tamagui/compose-refs";
|
|
9
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
Theme,
|
|
22
|
+
styled,
|
|
23
|
+
useIsomorphicLayoutEffect,
|
|
24
|
+
useThemeName,
|
|
25
|
+
withStaticProperties
|
|
26
|
+
} from "@tamagui/core";
|
|
10
27
|
import { useId } from "@tamagui/core";
|
|
11
28
|
import { createContextScope } from "@tamagui/create-context";
|
|
29
|
+
import { ListItem } from "@tamagui/list-item";
|
|
12
30
|
import { Separator } from "@tamagui/separator";
|
|
13
|
-
import { XStack } from "@tamagui/stacks";
|
|
31
|
+
import { ThemeableStack, XStack, YStack } from "@tamagui/stacks";
|
|
32
|
+
import { Paragraph } from "@tamagui/text";
|
|
14
33
|
import { useControllableState } from "@tamagui/use-controllable-state";
|
|
15
34
|
import * as React from "react";
|
|
16
35
|
import * as ReactDOM from "react-dom";
|
|
17
|
-
const
|
|
18
|
-
const
|
|
36
|
+
const userAgent = typeof navigator !== "undefined" && navigator.userAgent || "";
|
|
37
|
+
const isFirefox = userAgent.toLowerCase().includes("firefox");
|
|
38
|
+
if (isFirefox) {
|
|
39
|
+
document.body.classList.add("firefox");
|
|
40
|
+
}
|
|
41
|
+
function getVisualOffsetTop() {
|
|
42
|
+
var _a;
|
|
43
|
+
return !/^((?!chrome|android).)*safari/i.test(userAgent) ? (_a = visualViewport == null ? void 0 : visualViewport.offsetTop) != null ? _a : 0 : 0;
|
|
44
|
+
}
|
|
19
45
|
const SELECT_NAME = "Select";
|
|
20
46
|
const WINDOW_PADDING = 8;
|
|
21
47
|
const SCROLL_ARROW_VELOCITY = 8;
|
|
@@ -24,58 +50,6 @@ const MIN_HEIGHT = 80;
|
|
|
24
50
|
const FALLBACK_THRESHOLD = 16;
|
|
25
51
|
const [createSelectContext, createSelectScope] = createContextScope(SELECT_NAME);
|
|
26
52
|
const [SelectProvider, useSelectContext] = createSelectContext(SELECT_NAME);
|
|
27
|
-
const Select = /* @__PURE__ */ __name((props) => {
|
|
28
|
-
const {
|
|
29
|
-
__scopeSelect,
|
|
30
|
-
children,
|
|
31
|
-
open: openProp,
|
|
32
|
-
defaultOpen,
|
|
33
|
-
onOpenChange,
|
|
34
|
-
value: valueProp,
|
|
35
|
-
defaultValue,
|
|
36
|
-
onValueChange,
|
|
37
|
-
dir: dir2,
|
|
38
|
-
name,
|
|
39
|
-
autoComplete
|
|
40
|
-
} = props;
|
|
41
|
-
const [open, setOpen] = useControllableState({
|
|
42
|
-
prop: openProp,
|
|
43
|
-
defaultProp: defaultOpen || false,
|
|
44
|
-
onChange: onOpenChange
|
|
45
|
-
});
|
|
46
|
-
const [value, setValue] = useControllableState({
|
|
47
|
-
prop: valueProp,
|
|
48
|
-
defaultProp: defaultValue || "",
|
|
49
|
-
onChange: onValueChange
|
|
50
|
-
});
|
|
51
|
-
const selectedIndexRef = React.useRef(null);
|
|
52
|
-
const activeIndexRef = React.useRef(null);
|
|
53
|
-
const prevActiveIndex = usePrevious(activeIndex);
|
|
54
|
-
const [showArrows, setShowArrows] = React.useState(false);
|
|
55
|
-
const [scrollTop, setScrollTop] = React.useState(0);
|
|
56
|
-
const [activeIndex, setActiveIndex] = React.useState(null);
|
|
57
|
-
const [selectedIndex, setSelectedIndex] = React.useState(Math.max(0, listContentRef.current.indexOf(value)));
|
|
58
|
-
useIsomorphicLayoutEffect(() => {
|
|
59
|
-
selectedIndexRef.current = selectedIndex;
|
|
60
|
-
activeIndexRef.current = activeIndex;
|
|
61
|
-
});
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
const frame = requestAnimationFrame(() => {
|
|
64
|
-
setShowArrows(open);
|
|
65
|
-
if (!open) {
|
|
66
|
-
setScrollTop(0);
|
|
67
|
-
setMiddlewareType("align");
|
|
68
|
-
setActiveIndex(null);
|
|
69
|
-
setControlledScrolling(false);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
return () => cancelAnimationFrame(frame);
|
|
73
|
-
}, [open]);
|
|
74
|
-
const showUpArrow = showArrows && scrollTop > SCROLL_ARROW_THRESHOLD;
|
|
75
|
-
const showDownArrow = showArrows && floatingRef.current && scrollTop < floatingRef.current.scrollHeight - floatingRef.current.clientHeight - SCROLL_ARROW_THRESHOLD;
|
|
76
|
-
return <SelectProvider scope={__scopeSelect} open={open}>{children}</SelectProvider>;
|
|
77
|
-
}, "Select");
|
|
78
|
-
Select.displayName = SELECT_NAME;
|
|
79
53
|
const TRIGGER_NAME = "SelectTrigger";
|
|
80
54
|
const SelectTrigger = React.forwardRef((props, forwardedRef) => {
|
|
81
55
|
const {
|
|
@@ -85,217 +59,657 @@ const SelectTrigger = React.forwardRef((props, forwardedRef) => {
|
|
|
85
59
|
...triggerProps
|
|
86
60
|
} = props;
|
|
87
61
|
const context = useSelectContext(TRIGGER_NAME, __scopeSelect);
|
|
88
|
-
|
|
89
|
-
const getItems = useCollection(__scopeSelect);
|
|
90
|
-
const handleOpen = /* @__PURE__ */ __name(() => {
|
|
91
|
-
if (!disabled) {
|
|
92
|
-
context.onOpenChange(true);
|
|
93
|
-
resetTypeahead();
|
|
94
|
-
}
|
|
95
|
-
}, "handleOpen");
|
|
96
|
-
return <Primitive.button type="button" role="combobox" aria-controls={context.contentId} aria-expanded={context.open} aria-autocomplete="none" dir={context.dir} disabled={disabled} data-disabled={disabled ? "" : void 0} {...triggerProps} ref={composedRefs} onPointerDown={composeEventHandlers(triggerProps.onPointerDown, (event) => {
|
|
97
|
-
;
|
|
98
|
-
event.target.releasePointerCapture(event.pointerId);
|
|
99
|
-
if (event.button === 0 && event.ctrlKey === false) {
|
|
100
|
-
handleOpen();
|
|
101
|
-
context.triggerPointerDownPosRef.current = {
|
|
102
|
-
x: Math.round(event.pageX),
|
|
103
|
-
y: Math.round(event.pageY)
|
|
104
|
-
};
|
|
105
|
-
event.preventDefault();
|
|
106
|
-
}
|
|
107
|
-
})} onKeyDown={composeEventHandlers(triggerProps.onKeyDown, (event) => {
|
|
108
|
-
const isTypingAhead = searchRef.current !== "";
|
|
109
|
-
const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
|
|
110
|
-
if (!isModifierKey && event.key.length === 1)
|
|
111
|
-
handleTypeaheadSearch(event.key);
|
|
112
|
-
if (isTypingAhead && event.key === " ")
|
|
113
|
-
return;
|
|
114
|
-
if (OPEN_KEYS.includes(event.key)) {
|
|
115
|
-
handleOpen();
|
|
116
|
-
event.preventDefault();
|
|
117
|
-
}
|
|
118
|
-
})} />;
|
|
62
|
+
return <ListItem backgrounded radiused hoverTheme pressTheme focusTheme focusable={false} borderWidth={1} componentName={TRIGGER_NAME} size={context.size} aria-expanded={context.open} aria-autocomplete="none" disabled={disabled} data-disabled={disabled ? "" : void 0} {...triggerProps} ref={forwardedRef} {...context.interactions.getReferenceProps()} />;
|
|
119
63
|
});
|
|
120
64
|
SelectTrigger.displayName = TRIGGER_NAME;
|
|
121
65
|
const VALUE_NAME = "SelectValue";
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
66
|
+
const SelectValueFrame = styled(Paragraph, {
|
|
67
|
+
name: VALUE_NAME,
|
|
68
|
+
selectable: false
|
|
125
69
|
});
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
const {
|
|
129
|
-
|
|
70
|
+
const SelectValue = SelectValueFrame.extractable(React.forwardRef(({ __scopeSelect, children, placeholder }, forwardedRef) => {
|
|
71
|
+
const context = useSelectContext(VALUE_NAME, __scopeSelect);
|
|
72
|
+
const { onValueNodeHasChildrenChange } = context;
|
|
73
|
+
const hasChildren = children !== void 0;
|
|
74
|
+
const composedRefs = useComposedRefs(forwardedRef, context.onValueNodeChange);
|
|
75
|
+
React.useLayoutEffect(() => {
|
|
76
|
+
onValueNodeHasChildrenChange(hasChildren);
|
|
77
|
+
}, [onValueNodeHasChildrenChange, hasChildren]);
|
|
78
|
+
return <SelectValueFrame size={context.size} ref={composedRefs} pointerEvents="none">{context.value === void 0 && placeholder !== void 0 ? placeholder : children}</SelectValueFrame>;
|
|
79
|
+
}));
|
|
80
|
+
SelectValue.displayName = VALUE_NAME;
|
|
81
|
+
const SelectIcon = styled(XStack, {
|
|
82
|
+
name: "SelectIcon",
|
|
83
|
+
"aria-hidden": true,
|
|
84
|
+
children: <Paragraph>{"\u25BC"}</Paragraph>
|
|
130
85
|
});
|
|
131
|
-
SelectIcon.displayName = ICON_NAME;
|
|
132
86
|
const CONTENT_NAME = "SelectContent";
|
|
133
|
-
const SelectContent =
|
|
134
|
-
const context = useSelectContext(CONTENT_NAME,
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
87
|
+
const SelectContent = ({ children, __scopeSelect }) => {
|
|
88
|
+
const context = useSelectContext(CONTENT_NAME, __scopeSelect);
|
|
89
|
+
const themeName = useThemeName();
|
|
90
|
+
const contents = <Theme name={themeName}>{children}</Theme>;
|
|
91
|
+
return <FloatingPortal>{context.open ? <FloatingOverlay lockScroll>{contents}</FloatingOverlay> : <div style={{ display: "none" }}>{contents}</div>}</FloatingPortal>;
|
|
92
|
+
};
|
|
93
|
+
const VIEWPORT_NAME = "SelectViewport";
|
|
94
|
+
const SelectViewportFrame = styled(ThemeableStack, {
|
|
95
|
+
name: VIEWPORT_NAME,
|
|
96
|
+
backgroundColor: "$background",
|
|
97
|
+
elevate: true,
|
|
98
|
+
bordered: true,
|
|
99
|
+
overflow: "scroll",
|
|
100
|
+
userSelect: "none",
|
|
101
|
+
variants: {
|
|
102
|
+
size: {
|
|
103
|
+
"...size": (val, { tokens }) => {
|
|
104
|
+
var _a;
|
|
105
|
+
return {
|
|
106
|
+
borderRadius: (_a = tokens.radius[val]) != null ? _a : val
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
defaultVariants: {
|
|
112
|
+
size: "$2"
|
|
113
|
+
}
|
|
143
114
|
});
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const {
|
|
148
|
-
|
|
149
|
-
|
|
115
|
+
const SelectViewport = React.forwardRef((props, forwardedRef) => {
|
|
116
|
+
const { __scopeSelect, children, ...viewportProps } = props;
|
|
117
|
+
const context = useSelectContext(VIEWPORT_NAME, __scopeSelect);
|
|
118
|
+
const { style, ...floatingProps } = context.interactions.getFloatingProps();
|
|
119
|
+
delete style["scrollbarWidth"];
|
|
120
|
+
delete style["listStyleType"];
|
|
121
|
+
return <>
|
|
122
|
+
<style dangerouslySetInnerHTML={{
|
|
123
|
+
__html: `[data-tamagui-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-tamagui-select-viewport]::-webkit-scrollbar{display:none}`
|
|
124
|
+
}} />
|
|
125
|
+
<FloatingFocusManager context={context.floatingContext} preventTabbing><SelectViewportFrame size={context.size} data-tamagui-select-viewport="" role="presentation" {...viewportProps} ref={forwardedRef} {...floatingProps} {...style}>{children}</SelectViewportFrame></FloatingFocusManager>
|
|
126
|
+
</>;
|
|
150
127
|
});
|
|
151
|
-
|
|
128
|
+
SelectViewport.displayName = VIEWPORT_NAME;
|
|
152
129
|
const ITEM_NAME = "SelectItem";
|
|
153
130
|
const [SelectItemContextProvider, useSelectItemContext] = createSelectContext(ITEM_NAME);
|
|
154
131
|
const SelectItem = React.forwardRef((props, forwardedRef) => {
|
|
155
|
-
const {
|
|
132
|
+
const {
|
|
133
|
+
__scopeSelect,
|
|
134
|
+
value,
|
|
135
|
+
disabled = false,
|
|
136
|
+
textValue: textValueProp,
|
|
137
|
+
index,
|
|
138
|
+
...itemProps
|
|
139
|
+
} = props;
|
|
156
140
|
const context = useSelectContext(ITEM_NAME, __scopeSelect);
|
|
157
|
-
const contentContext = useSelectContentContext(ITEM_NAME, __scopeSelect);
|
|
158
141
|
const isSelected = context.value === value;
|
|
159
142
|
const [textValue, setTextValue] = React.useState(textValueProp != null ? textValueProp : "");
|
|
160
143
|
const [isFocused, setIsFocused] = React.useState(false);
|
|
161
|
-
const composedRefs = useComposedRefs(forwardedRef, isSelected ? contentContext.onSelectedItemChange : void 0);
|
|
162
144
|
const textId = useId();
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
145
|
+
const {
|
|
146
|
+
selectedIndex,
|
|
147
|
+
setSelectedIndex,
|
|
148
|
+
listRef,
|
|
149
|
+
open,
|
|
150
|
+
setOpen,
|
|
151
|
+
onChange,
|
|
152
|
+
activeIndex,
|
|
153
|
+
setActiveIndex,
|
|
154
|
+
setValueAtIndex,
|
|
155
|
+
dataRef
|
|
156
|
+
} = context;
|
|
157
|
+
const composedRefs = useComposedRefs(forwardedRef, (node) => {
|
|
158
|
+
if (node instanceof HTMLElement) {
|
|
159
|
+
listRef.current[index] = node;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
React.useEffect(() => {
|
|
163
|
+
setValueAtIndex(index, value);
|
|
164
|
+
}, [index]);
|
|
165
|
+
const timeoutRef = React.useRef();
|
|
166
|
+
const [allowMouseUp, setAllowMouseUp] = React.useState(false);
|
|
167
|
+
function handleSelect() {
|
|
168
|
+
setSelectedIndex(index);
|
|
169
|
+
onChange(value);
|
|
170
|
+
setOpen(false);
|
|
171
|
+
}
|
|
172
|
+
React.useLayoutEffect(() => {
|
|
173
|
+
clearTimeout(timeoutRef.current);
|
|
174
|
+
if (open) {
|
|
175
|
+
if (selectedIndex !== index) {
|
|
176
|
+
setAllowMouseUp(true);
|
|
177
|
+
} else {
|
|
178
|
+
timeoutRef.current = setTimeout(() => {
|
|
179
|
+
setAllowMouseUp(true);
|
|
180
|
+
}, 300);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
setAllowMouseUp(false);
|
|
167
184
|
}
|
|
168
|
-
},
|
|
169
|
-
|
|
185
|
+
}, [open, index, setActiveIndex, selectedIndex]);
|
|
186
|
+
function handleKeyDown(event) {
|
|
187
|
+
if (event.key === "Enter" || event.key === " " && !dataRef.current.typing) {
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
handleSelect();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const selectItemProps = context.interactions.getItemProps({
|
|
193
|
+
onClick: allowMouseUp ? handleSelect : void 0,
|
|
194
|
+
onMouseUp: allowMouseUp ? handleSelect : void 0,
|
|
195
|
+
onKeyDown: handleKeyDown
|
|
196
|
+
});
|
|
197
|
+
return <SelectItemContextProvider scope={__scopeSelect} value={value} textId={textId || ""} isSelected={isSelected} onItemTextChange={React.useCallback((node) => {
|
|
170
198
|
setTextValue((prevTextValue) => {
|
|
171
199
|
var _a;
|
|
172
200
|
return prevTextValue || ((_a = node == null ? void 0 : node.textContent) != null ? _a : "").trim();
|
|
173
201
|
});
|
|
174
|
-
}, [])}><
|
|
175
|
-
if (disabled) {
|
|
176
|
-
contentContext.onItemLeave();
|
|
177
|
-
} else {
|
|
178
|
-
event.currentTarget.focus({ preventScroll: true });
|
|
179
|
-
}
|
|
180
|
-
})} onPointerLeave={composeEventHandlers(itemProps.onPointerLeave, (event) => {
|
|
181
|
-
if (event.currentTarget === document.activeElement) {
|
|
182
|
-
contentContext.onItemLeave();
|
|
183
|
-
}
|
|
184
|
-
})} onKeyDown={composeEventHandlers(itemProps.onKeyDown, (event) => {
|
|
185
|
-
const isTypingAhead = contentContext.searchRef.current !== "";
|
|
186
|
-
if (isTypingAhead && event.key === " ")
|
|
187
|
-
return;
|
|
188
|
-
if (SELECTION_KEYS.includes(event.key))
|
|
189
|
-
handleSelect();
|
|
190
|
-
if (event.key === " ")
|
|
191
|
-
event.preventDefault();
|
|
192
|
-
})} /></Collection.ItemSlot></SelectItemContextProvider>;
|
|
202
|
+
}, [])}><ListItem backgrounded pressTheme focusTheme componentName={ITEM_NAME} ref={composedRefs} aria-labelledby={textId} aria-selected={isSelected && isFocused} data-state={isSelected ? "active" : "inactive"} aria-disabled={disabled || void 0} data-disabled={disabled ? "" : void 0} tabIndex={disabled ? void 0 : -1} size={context.size} {...itemProps} {...selectItemProps} /></SelectItemContextProvider>;
|
|
193
203
|
});
|
|
194
204
|
SelectItem.displayName = ITEM_NAME;
|
|
195
205
|
const ITEM_TEXT_NAME = "SelectItemText";
|
|
206
|
+
const SelectItemTextFrame = styled(Paragraph, {
|
|
207
|
+
name: ITEM_TEXT_NAME,
|
|
208
|
+
selectable: false
|
|
209
|
+
});
|
|
196
210
|
const SelectItemText = React.forwardRef((props, forwardedRef) => {
|
|
197
|
-
var _a;
|
|
198
211
|
const { __scopeSelect, className, style, ...itemTextProps } = props;
|
|
199
212
|
const context = useSelectContext(ITEM_TEXT_NAME, __scopeSelect);
|
|
200
|
-
const contentContext = useSelectContentContext(ITEM_TEXT_NAME, __scopeSelect);
|
|
201
213
|
const itemContext = useSelectItemContext(ITEM_TEXT_NAME, __scopeSelect);
|
|
202
214
|
const ref = React.useRef(null);
|
|
203
|
-
const composedRefs = useComposedRefs(forwardedRef, ref, itemContext.onItemTextChange
|
|
215
|
+
const composedRefs = useComposedRefs(forwardedRef, ref, itemContext.onItemTextChange);
|
|
204
216
|
return <>
|
|
205
|
-
<
|
|
217
|
+
<SelectItemTextFrame size={context.size} id={itemContext.textId} {...itemTextProps} ref={composedRefs} />
|
|
206
218
|
{itemContext.isSelected && context.valueNode && !context.valueNodeHasChildren ? ReactDOM.createPortal(itemTextProps.children, context.valueNode) : null}
|
|
207
|
-
{context.bubbleSelect ? ReactDOM.createPortal(<option value={itemContext.value}>{(_a = ref.current) == null ? void 0 : _a.textContent}</option>, context.bubbleSelect) : null}
|
|
208
219
|
</>;
|
|
209
220
|
});
|
|
210
221
|
SelectItemText.displayName = ITEM_TEXT_NAME;
|
|
211
222
|
const ITEM_INDICATOR_NAME = "SelectItemIndicator";
|
|
223
|
+
const SelectItemIndicatorFrame = styled(XStack, {
|
|
224
|
+
name: ITEM_TEXT_NAME
|
|
225
|
+
});
|
|
212
226
|
const SelectItemIndicator = React.forwardRef((props, forwardedRef) => {
|
|
213
227
|
const { __scopeSelect, ...itemIndicatorProps } = props;
|
|
214
228
|
const itemContext = useSelectItemContext(ITEM_INDICATOR_NAME, __scopeSelect);
|
|
215
|
-
return itemContext.isSelected ? <
|
|
229
|
+
return itemContext.isSelected ? <SelectItemIndicatorFrame aria-hidden {...itemIndicatorProps} ref={forwardedRef} /> : null;
|
|
216
230
|
});
|
|
217
231
|
SelectItemIndicator.displayName = ITEM_INDICATOR_NAME;
|
|
232
|
+
const GROUP_NAME = "SelectGroup";
|
|
233
|
+
const [SelectGroupContextProvider, useSelectGroupContext] = createSelectContext(GROUP_NAME);
|
|
234
|
+
const SelectGroupFrame = styled(YStack, {
|
|
235
|
+
name: GROUP_NAME,
|
|
236
|
+
width: "100%"
|
|
237
|
+
});
|
|
238
|
+
const SelectGroup = React.forwardRef((props, forwardedRef) => {
|
|
239
|
+
const { __scopeSelect, ...groupProps } = props;
|
|
240
|
+
const groupId = useId();
|
|
241
|
+
return <SelectGroupContextProvider scope={__scopeSelect} id={groupId || ""}><SelectGroupFrame role="group" aria-labelledby={groupId} {...groupProps} ref={forwardedRef} /></SelectGroupContextProvider>;
|
|
242
|
+
});
|
|
243
|
+
SelectGroup.displayName = GROUP_NAME;
|
|
244
|
+
const LABEL_NAME = "SelectLabel";
|
|
245
|
+
const SelectLabel = React.forwardRef((props, forwardedRef) => {
|
|
246
|
+
const { __scopeSelect, ...labelProps } = props;
|
|
247
|
+
const context = useSelectContext(LABEL_NAME, __scopeSelect);
|
|
248
|
+
const groupContext = useSelectGroupContext(LABEL_NAME, __scopeSelect);
|
|
249
|
+
return <ListItem componentName={LABEL_NAME} fontWeight="800" id={groupContext.id} size={context.size} {...labelProps} ref={forwardedRef} />;
|
|
250
|
+
});
|
|
251
|
+
SelectLabel.displayName = LABEL_NAME;
|
|
218
252
|
const SCROLL_UP_BUTTON_NAME = "SelectScrollUpButton";
|
|
219
253
|
const SelectScrollUpButton = React.forwardRef((props, forwardedRef) => {
|
|
220
|
-
|
|
221
|
-
const [canScrollUp, setCanScrollUp] = React.useState(false);
|
|
222
|
-
const composedRefs = useComposedRefs(forwardedRef, contentContext.onScrollButtonChange);
|
|
223
|
-
return canScrollUp ? <SelectScrollButtonImpl {...props} ref={composedRefs} onAutoScroll={() => {
|
|
224
|
-
const { viewport, selectedItem } = contentContext;
|
|
225
|
-
if (viewport && selectedItem) {
|
|
226
|
-
viewport.scrollTop = viewport.scrollTop - selectedItem.offsetHeight;
|
|
227
|
-
}
|
|
228
|
-
}} /> : null;
|
|
254
|
+
return <SelectScrollButtonImpl componentName={SCROLL_UP_BUTTON_NAME} {...props} dir="up" ref={forwardedRef} />;
|
|
229
255
|
});
|
|
230
256
|
SelectScrollUpButton.displayName = SCROLL_UP_BUTTON_NAME;
|
|
231
257
|
const SCROLL_DOWN_BUTTON_NAME = "SelectScrollDownButton";
|
|
232
258
|
const SelectScrollDownButton = React.forwardRef((props, forwardedRef) => {
|
|
233
|
-
|
|
234
|
-
const [canScrollDown, setCanScrollDown] = React.useState(false);
|
|
235
|
-
const composedRefs = useComposedRefs(forwardedRef, contentContext.onScrollButtonChange);
|
|
236
|
-
return canScrollDown ? <SelectScrollButtonImpl {...props} ref={composedRefs} onAutoScroll={() => {
|
|
237
|
-
const { viewport, selectedItem } = contentContext;
|
|
238
|
-
if (viewport && selectedItem) {
|
|
239
|
-
viewport.scrollTop = viewport.scrollTop + selectedItem.offsetHeight;
|
|
240
|
-
}
|
|
241
|
-
}} /> : null;
|
|
259
|
+
return <SelectScrollButtonImpl componentName={SCROLL_DOWN_BUTTON_NAME} {...props} dir="down" ref={forwardedRef} />;
|
|
242
260
|
});
|
|
243
261
|
SelectScrollDownButton.displayName = SCROLL_DOWN_BUTTON_NAME;
|
|
244
262
|
const SelectScrollButtonImpl = React.forwardRef((props, forwardedRef) => {
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
const
|
|
263
|
+
var _a, _b;
|
|
264
|
+
const { __scopeSelect, dir, componentName, ...scrollIndicatorProps } = props;
|
|
265
|
+
const { floatingRef, increaseHeight, forceUpdate, ...context } = useSelectContext(componentName, __scopeSelect);
|
|
248
266
|
const intervalRef = React.useRef();
|
|
249
267
|
const loopingRef = React.useRef(false);
|
|
268
|
+
const isVisible = context[dir === "down" ? "canScrollDown" : "canScrollUp"];
|
|
250
269
|
const { x, y, reference, floating, strategy, update, refs } = useFloating({
|
|
251
270
|
strategy: "fixed",
|
|
252
271
|
placement: dir === "up" ? "top" : "bottom",
|
|
253
|
-
middleware: [offset(({
|
|
272
|
+
middleware: [offset(({ rects }) => -rects.floating.height)]
|
|
254
273
|
});
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
274
|
+
const composedRefs = useComposedRefs(forwardedRef, floating);
|
|
275
|
+
React.useLayoutEffect(() => {
|
|
276
|
+
reference(floatingRef.current);
|
|
277
|
+
}, [reference, floatingRef.current]);
|
|
278
|
+
React.useEffect(() => {
|
|
279
|
+
if (!refs.reference.current || !refs.floating.current || !isVisible) {
|
|
280
|
+
return;
|
|
259
281
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
282
|
+
const cleanup = autoUpdate(refs.reference.current, refs.floating.current, update, {
|
|
283
|
+
animationFrame: true
|
|
284
|
+
});
|
|
285
|
+
return () => {
|
|
286
|
+
clearInterval(intervalRef.current);
|
|
287
|
+
loopingRef.current = false;
|
|
288
|
+
cleanup();
|
|
289
|
+
};
|
|
290
|
+
}, [isVisible, update, refs.floating, refs.reference]);
|
|
291
|
+
if (!isVisible) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const handleScrollArrowChange = () => {
|
|
295
|
+
const floating2 = floatingRef.current;
|
|
296
|
+
const isUp = dir === "up";
|
|
297
|
+
if (floating2) {
|
|
298
|
+
const value = isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY;
|
|
299
|
+
const multi = isUp && floating2.scrollTop <= SCROLL_ARROW_THRESHOLD * 2 || !isUp && floating2.scrollTop >= floating2.scrollHeight - floating2.clientHeight - SCROLL_ARROW_THRESHOLD * 2 ? 2 : 1;
|
|
300
|
+
floating2.scrollTop += multi * (isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY);
|
|
301
|
+
increaseHeight(floating2, multi === 2 ? value * 2 : value);
|
|
302
|
+
forceUpdate();
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
return <YStack ref={composedRefs} componentName={componentName} aria-hidden {...scrollIndicatorProps} zIndex={1e3} position={strategy} left={x || 0} top={y || 0} width={`calc(${((_b = (_a = floatingRef.current) == null ? void 0 : _a.offsetWidth) != null ? _b : 0) - 2}px)`} onPointerMove={() => {
|
|
306
|
+
if (!loopingRef.current) {
|
|
307
|
+
intervalRef.current = setInterval(handleScrollArrowChange, 1e3 / 60);
|
|
308
|
+
loopingRef.current = true;
|
|
309
|
+
}
|
|
310
|
+
}} onPointerLeave={() => {
|
|
311
|
+
loopingRef.current = false;
|
|
312
|
+
clearInterval(intervalRef.current);
|
|
313
|
+
}} />;
|
|
263
314
|
});
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
const {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
315
|
+
const SelectSeparator = styled(Separator, {
|
|
316
|
+
name: "SelectSeparator"
|
|
317
|
+
});
|
|
318
|
+
const Select = withStaticProperties((props) => {
|
|
319
|
+
const {
|
|
320
|
+
id,
|
|
321
|
+
__scopeSelect,
|
|
322
|
+
children,
|
|
323
|
+
open: openProp,
|
|
324
|
+
defaultOpen,
|
|
325
|
+
onOpenChange,
|
|
326
|
+
value: valueProp,
|
|
327
|
+
defaultValue,
|
|
328
|
+
onValueChange,
|
|
329
|
+
size: sizeProp,
|
|
330
|
+
dir,
|
|
331
|
+
name,
|
|
332
|
+
autoComplete
|
|
333
|
+
} = props;
|
|
334
|
+
const [open, setOpen] = useControllableState({
|
|
335
|
+
prop: openProp,
|
|
336
|
+
defaultProp: defaultOpen || false,
|
|
337
|
+
onChange: onOpenChange
|
|
338
|
+
});
|
|
339
|
+
const [value, setValue] = useControllableState({
|
|
340
|
+
prop: valueProp,
|
|
341
|
+
defaultProp: defaultValue || "",
|
|
342
|
+
onChange: onValueChange
|
|
343
|
+
});
|
|
344
|
+
const [activeIndex, setActiveIndex] = React.useState(null);
|
|
345
|
+
const selectedIndexRef = React.useRef(null);
|
|
346
|
+
const activeIndexRef = React.useRef(null);
|
|
347
|
+
const prevActiveIndex = usePrevious(activeIndex);
|
|
348
|
+
const [showArrows, setShowArrows] = React.useState(false);
|
|
349
|
+
const [scrollTop, setScrollTop] = React.useState(0);
|
|
350
|
+
const listItemsRef = React.useRef([]);
|
|
351
|
+
const listContentRef = React.useRef([]);
|
|
352
|
+
const [selectedIndex, setSelectedIndex] = React.useState(Math.max(0, listContentRef.current.indexOf(value)));
|
|
353
|
+
const [controlledScrolling, setControlledScrolling] = React.useState(false);
|
|
354
|
+
const [middlewareType, setMiddlewareType] = React.useState("align");
|
|
355
|
+
useIsomorphicLayoutEffect(() => {
|
|
356
|
+
selectedIndexRef.current = selectedIndex;
|
|
357
|
+
activeIndexRef.current = activeIndex;
|
|
358
|
+
});
|
|
272
359
|
React.useEffect(() => {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
360
|
+
const frame = requestAnimationFrame(() => {
|
|
361
|
+
setShowArrows(open);
|
|
362
|
+
if (!open) {
|
|
363
|
+
setScrollTop(0);
|
|
364
|
+
setMiddlewareType("align");
|
|
365
|
+
setActiveIndex(null);
|
|
366
|
+
setControlledScrolling(false);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
return () => {
|
|
370
|
+
cancelAnimationFrame(frame);
|
|
371
|
+
};
|
|
372
|
+
}, [open]);
|
|
373
|
+
function getFloatingPadding(floating2) {
|
|
374
|
+
var _a;
|
|
375
|
+
if (!floating2) {
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
return Number((_a = getComputedStyle(floating2).paddingLeft) == null ? void 0 : _a.replace("px", ""));
|
|
379
|
+
}
|
|
380
|
+
const { x, y, reference, floating, strategy, context, refs, middlewareData, update } = useFloating({
|
|
381
|
+
open,
|
|
382
|
+
onOpenChange: setOpen,
|
|
383
|
+
placement: "bottom",
|
|
384
|
+
middleware: middlewareType === "align" ? [
|
|
385
|
+
offset(({ rects }) => {
|
|
386
|
+
var _a;
|
|
387
|
+
const index = (_a = activeIndexRef.current) != null ? _a : selectedIndexRef.current;
|
|
388
|
+
if (index == null) {
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
const item = listItemsRef.current[index];
|
|
392
|
+
if (item == null) {
|
|
393
|
+
return 0;
|
|
394
|
+
}
|
|
395
|
+
const offsetTop = item.offsetTop;
|
|
396
|
+
const itemHeight = item.offsetHeight;
|
|
397
|
+
const height = rects.reference.height;
|
|
398
|
+
return -offsetTop - height - (itemHeight - height) / 2;
|
|
399
|
+
}),
|
|
400
|
+
{
|
|
401
|
+
name: "size",
|
|
402
|
+
async fn(args) {
|
|
403
|
+
var _a;
|
|
404
|
+
const {
|
|
405
|
+
elements: { floating: floating2 },
|
|
406
|
+
rects: { reference: reference2 },
|
|
407
|
+
middlewareData: middlewareData2
|
|
408
|
+
} = args;
|
|
409
|
+
const overflow = await detectOverflow(args, {
|
|
410
|
+
padding: WINDOW_PADDING
|
|
411
|
+
});
|
|
412
|
+
const top = Math.max(0, overflow.top);
|
|
413
|
+
const bottom = Math.max(0, overflow.bottom);
|
|
414
|
+
const nextY = args.y + top;
|
|
415
|
+
if ((_a = middlewareData2.size) == null ? void 0 : _a.skip) {
|
|
416
|
+
return {
|
|
417
|
+
y: nextY,
|
|
418
|
+
data: {
|
|
419
|
+
y: middlewareData2.size.y
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
Object.assign(floating2.style, {
|
|
424
|
+
maxHeight: `${floating2.scrollHeight - Math.abs(top + bottom)}px`,
|
|
425
|
+
minWidth: `${reference2.width + getFloatingPadding(floating2) * 2}px`
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
y: nextY,
|
|
429
|
+
data: {
|
|
430
|
+
y: top,
|
|
431
|
+
skip: true
|
|
432
|
+
},
|
|
433
|
+
reset: {
|
|
434
|
+
rects: true
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
] : [
|
|
440
|
+
offset(5),
|
|
441
|
+
flip(),
|
|
442
|
+
size({
|
|
443
|
+
apply({ rects, availableHeight, elements }) {
|
|
444
|
+
Object.assign(elements.floating.style, {
|
|
445
|
+
width: `${rects.reference.width}px`,
|
|
446
|
+
maxHeight: `${availableHeight}px`
|
|
447
|
+
});
|
|
448
|
+
},
|
|
449
|
+
padding: WINDOW_PADDING
|
|
450
|
+
})
|
|
451
|
+
]
|
|
452
|
+
});
|
|
453
|
+
const floatingRef = refs.floating;
|
|
454
|
+
const forceUpdate = React.useReducer(() => ({}), {})[1];
|
|
455
|
+
const showUpArrow = showArrows && scrollTop > SCROLL_ARROW_THRESHOLD;
|
|
456
|
+
const showDownArrow = showArrows && floatingRef.current && scrollTop < floatingRef.current.scrollHeight - floatingRef.current.clientHeight - SCROLL_ARROW_THRESHOLD;
|
|
457
|
+
const interactions = useInteractions([
|
|
458
|
+
useClick(context, { pointerDown: true }),
|
|
459
|
+
useRole(context, { role: "listbox" }),
|
|
460
|
+
useDismiss(context),
|
|
461
|
+
useListNavigation(context, {
|
|
462
|
+
listRef: listItemsRef,
|
|
463
|
+
activeIndex,
|
|
464
|
+
selectedIndex,
|
|
465
|
+
onNavigate: setActiveIndex
|
|
466
|
+
}),
|
|
467
|
+
useTypeahead(context, {
|
|
468
|
+
listRef: listContentRef,
|
|
469
|
+
onMatch: open ? setActiveIndex : setSelectedIndex,
|
|
470
|
+
selectedIndex,
|
|
471
|
+
activeIndex
|
|
472
|
+
})
|
|
473
|
+
]);
|
|
474
|
+
const increaseHeight = React.useCallback((floating2, amount = 0) => {
|
|
475
|
+
var _a;
|
|
476
|
+
if (middlewareType === "fallback") {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const currentMaxHeight = Number(floating2.style.maxHeight.replace("px", ""));
|
|
480
|
+
const currentTop = Number(floating2.style.top.replace("px", ""));
|
|
481
|
+
const rect = floating2.getBoundingClientRect();
|
|
482
|
+
const rectTop = rect.top;
|
|
483
|
+
const rectBottom = rect.bottom;
|
|
484
|
+
const viewportHeight = (_a = visualViewport == null ? void 0 : visualViewport.height) != null ? _a : 0;
|
|
485
|
+
const visualMaxHeight = viewportHeight - WINDOW_PADDING * 2;
|
|
486
|
+
if (amount < 0 && selectedIndexRef.current != null && Math.round(rectBottom) < Math.round(viewportHeight + getVisualOffsetTop() - WINDOW_PADDING)) {
|
|
487
|
+
floating2.style.maxHeight = `${Math.min(visualMaxHeight, currentMaxHeight - amount)}px`;
|
|
488
|
+
}
|
|
489
|
+
if (amount > 0 && Math.round(rectTop) > Math.round(WINDOW_PADDING - getVisualOffsetTop()) && floating2.scrollHeight > floating2.offsetHeight) {
|
|
490
|
+
const nextTop = Math.max(WINDOW_PADDING + getVisualOffsetTop(), currentTop - amount);
|
|
491
|
+
const nextMaxHeight = Math.min(visualMaxHeight, currentMaxHeight + amount);
|
|
492
|
+
Object.assign(floating2.style, {
|
|
493
|
+
maxHeight: `${nextMaxHeight}px`,
|
|
494
|
+
top: `${nextTop}px`
|
|
495
|
+
});
|
|
496
|
+
if (nextTop - WINDOW_PADDING > getVisualOffsetTop()) {
|
|
497
|
+
floating2.scrollTop -= nextMaxHeight - currentMaxHeight + getFloatingPadding(floating2);
|
|
498
|
+
}
|
|
499
|
+
return currentTop - nextTop;
|
|
500
|
+
}
|
|
501
|
+
}, [middlewareType]);
|
|
502
|
+
const touchPageYRef = React.useRef(null);
|
|
503
|
+
const handleWheel = React.useCallback((event) => {
|
|
504
|
+
var _a, _b, _c;
|
|
505
|
+
const pinching = event.ctrlKey;
|
|
506
|
+
const currentTarget = event.currentTarget;
|
|
507
|
+
function isWheelEvent(event2) {
|
|
508
|
+
return typeof event2.deltaY === "number";
|
|
509
|
+
}
|
|
510
|
+
function isTouchEvent(event2) {
|
|
511
|
+
return event2.touches != null;
|
|
512
|
+
}
|
|
513
|
+
if (Math.abs(((_a = currentTarget == null ? void 0 : currentTarget.offsetHeight) != null ? _a : 0) - (((_b = visualViewport == null ? void 0 : visualViewport.height) != null ? _b : 0) - WINDOW_PADDING * 2)) > 1 && !pinching) {
|
|
514
|
+
event.preventDefault();
|
|
515
|
+
} else if (isWheelEvent(event) && isFirefox) {
|
|
516
|
+
currentTarget.scrollTop += event.deltaY;
|
|
517
|
+
}
|
|
518
|
+
if (!pinching) {
|
|
519
|
+
let delta = 5;
|
|
520
|
+
if (isTouchEvent(event)) {
|
|
521
|
+
const currentPageY = touchPageYRef.current;
|
|
522
|
+
const pageY = (_c = event.touches[0]) == null ? void 0 : _c.pageY;
|
|
523
|
+
if (pageY != null) {
|
|
524
|
+
touchPageYRef.current = pageY;
|
|
525
|
+
if (currentPageY != null) {
|
|
526
|
+
delta = currentPageY - pageY;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
increaseHeight(currentTarget, isWheelEvent(event) ? event.deltaY : delta);
|
|
531
|
+
setScrollTop(currentTarget.scrollTop);
|
|
532
|
+
forceUpdate();
|
|
281
533
|
}
|
|
282
|
-
}, [
|
|
283
|
-
|
|
284
|
-
|
|
534
|
+
}, [increaseHeight, forceUpdate]);
|
|
535
|
+
React.useEffect(() => {
|
|
536
|
+
function onTouchEnd() {
|
|
537
|
+
touchPageYRef.current = null;
|
|
538
|
+
}
|
|
539
|
+
const floating2 = floatingRef.current;
|
|
540
|
+
if (open && floating2 && middlewareType === "align") {
|
|
541
|
+
floating2.addEventListener("wheel", handleWheel);
|
|
542
|
+
floating2.addEventListener("touchmove", handleWheel);
|
|
543
|
+
floating2.addEventListener("touchend", onTouchEnd, { passive: true });
|
|
544
|
+
return () => {
|
|
545
|
+
floating2.removeEventListener("wheel", handleWheel);
|
|
546
|
+
floating2.removeEventListener("touchmove", handleWheel);
|
|
547
|
+
floating2.removeEventListener("touchend", onTouchEnd);
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
}, [open, floatingRef, handleWheel, middlewareType]);
|
|
551
|
+
React.useEffect(() => {
|
|
552
|
+
window.addEventListener("resize", update);
|
|
553
|
+
return () => {
|
|
554
|
+
window.removeEventListener("resize", update);
|
|
555
|
+
};
|
|
556
|
+
}, [update]);
|
|
557
|
+
React.useLayoutEffect(() => {
|
|
558
|
+
var _a, _b, _c;
|
|
559
|
+
const floating2 = floatingRef.current;
|
|
560
|
+
if (open && controlledScrolling && floating2) {
|
|
561
|
+
const item = activeIndex != null ? listItemsRef.current[activeIndex] : selectedIndex != null ? listItemsRef.current[selectedIndex] : null;
|
|
562
|
+
if (item && prevActiveIndex != null) {
|
|
563
|
+
const itemHeight = (_b = (_a = listItemsRef.current[prevActiveIndex]) == null ? void 0 : _a.offsetHeight) != null ? _b : 0;
|
|
564
|
+
const floatingHeight = floating2.offsetHeight;
|
|
565
|
+
const top = item.offsetTop;
|
|
566
|
+
const bottom = top + itemHeight;
|
|
567
|
+
if (top < floating2.scrollTop + 20) {
|
|
568
|
+
const diff = floating2.scrollTop - top + 20;
|
|
569
|
+
floating2.scrollTop -= diff;
|
|
570
|
+
if (activeIndex != selectedIndex && activeIndex != null) {
|
|
571
|
+
increaseHeight(floating2, -diff);
|
|
572
|
+
}
|
|
573
|
+
} else if (bottom > floatingHeight + floating2.scrollTop - 20) {
|
|
574
|
+
const diff = bottom - floatingHeight - floating2.scrollTop + 20;
|
|
575
|
+
floating2.scrollTop += diff;
|
|
576
|
+
if (activeIndex != selectedIndex && activeIndex != null) {
|
|
577
|
+
floating2.scrollTop -= (_c = increaseHeight(floating2, diff)) != null ? _c : 0;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}, [
|
|
583
|
+
open,
|
|
584
|
+
controlledScrolling,
|
|
585
|
+
prevActiveIndex,
|
|
586
|
+
activeIndex,
|
|
587
|
+
selectedIndex,
|
|
588
|
+
floatingRef,
|
|
589
|
+
increaseHeight
|
|
590
|
+
]);
|
|
591
|
+
React.useLayoutEffect(() => {
|
|
592
|
+
var _a, _b;
|
|
593
|
+
const floating2 = refs.floating.current;
|
|
594
|
+
const reference2 = refs.reference.current;
|
|
595
|
+
if (open && floating2 && reference2 && floating2.offsetHeight < floating2.scrollHeight) {
|
|
596
|
+
const referenceRect = reference2.getBoundingClientRect();
|
|
597
|
+
if (middlewareType === "fallback") {
|
|
598
|
+
const item = listItemsRef.current[selectedIndex];
|
|
599
|
+
if (item) {
|
|
600
|
+
floating2.scrollTop = item.offsetTop - floating2.clientHeight + referenceRect.height;
|
|
601
|
+
}
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
floating2.scrollTop = (_a = middlewareData.size) == null ? void 0 : _a.y;
|
|
605
|
+
const closeToBottom = ((_b = visualViewport == null ? void 0 : visualViewport.height) != null ? _b : 0) + getVisualOffsetTop() - referenceRect.bottom < FALLBACK_THRESHOLD;
|
|
606
|
+
const closeToTop = referenceRect.top < FALLBACK_THRESHOLD;
|
|
607
|
+
if (floating2.offsetHeight < MIN_HEIGHT || closeToTop || closeToBottom) {
|
|
608
|
+
setMiddlewareType("fallback");
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}, [
|
|
612
|
+
open,
|
|
613
|
+
increaseHeight,
|
|
614
|
+
selectedIndex,
|
|
615
|
+
middlewareType,
|
|
616
|
+
refs.floating,
|
|
617
|
+
refs.reference,
|
|
618
|
+
middlewareData
|
|
619
|
+
]);
|
|
620
|
+
React.useLayoutEffect(() => {
|
|
621
|
+
if (open && selectedIndex != null) {
|
|
622
|
+
requestAnimationFrame(() => {
|
|
623
|
+
var _a;
|
|
624
|
+
(_a = listItemsRef.current[selectedIndex]) == null ? void 0 : _a.focus({ preventScroll: true });
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
}, [listItemsRef, selectedIndex, open]);
|
|
628
|
+
React.useEffect(() => {
|
|
629
|
+
const frame = requestAnimationFrame(() => {
|
|
630
|
+
setShowArrows(open);
|
|
631
|
+
if (!open) {
|
|
632
|
+
setScrollTop(0);
|
|
633
|
+
setMiddlewareType("align");
|
|
634
|
+
setActiveIndex(null);
|
|
635
|
+
setControlledScrolling(false);
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
return () => cancelAnimationFrame(frame);
|
|
639
|
+
}, [open]);
|
|
640
|
+
const [valueNode, setValueNode] = React.useState(null);
|
|
641
|
+
const [valueNodeHasChildren, setValueNodeHasChildren] = React.useState(false);
|
|
642
|
+
return <SelectProvider size={sizeProp} scope={__scopeSelect} increaseHeight={increaseHeight} forceUpdate={forceUpdate} floatingRef={floatingRef} valueNode={valueNode} onValueNodeChange={setValueNode} valueNodeHasChildren={valueNodeHasChildren} onValueNodeHasChildrenChange={setValueNodeHasChildren} setValueAtIndex={(index, value2) => {
|
|
643
|
+
listContentRef.current[index] = value2;
|
|
644
|
+
}} interactions={{
|
|
645
|
+
...interactions,
|
|
646
|
+
getReferenceProps() {
|
|
647
|
+
return interactions.getReferenceProps({
|
|
648
|
+
ref: reference,
|
|
649
|
+
className: "SelectTrigger",
|
|
650
|
+
onKeyDown(event) {
|
|
651
|
+
if (event.key === "Enter" || event.key === " " && !context.dataRef.current.typing) {
|
|
652
|
+
event.preventDefault();
|
|
653
|
+
setOpen(true);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
},
|
|
658
|
+
getFloatingProps(props2) {
|
|
659
|
+
return interactions.getFloatingProps({
|
|
660
|
+
ref: floating,
|
|
661
|
+
className: "Select",
|
|
662
|
+
...props2,
|
|
663
|
+
style: {
|
|
664
|
+
position: strategy,
|
|
665
|
+
top: y != null ? y : "",
|
|
666
|
+
left: x != null ? x : "",
|
|
667
|
+
outline: 0,
|
|
668
|
+
listStyleType: "none",
|
|
669
|
+
scrollbarWidth: "none",
|
|
670
|
+
userSelect: "none",
|
|
671
|
+
...props2 == null ? void 0 : props2.style
|
|
672
|
+
},
|
|
673
|
+
onPointerEnter() {
|
|
674
|
+
setControlledScrolling(false);
|
|
675
|
+
},
|
|
676
|
+
onPointerMove() {
|
|
677
|
+
setControlledScrolling(false);
|
|
678
|
+
},
|
|
679
|
+
onKeyDown() {
|
|
680
|
+
setControlledScrolling(true);
|
|
681
|
+
},
|
|
682
|
+
onScroll(event) {
|
|
683
|
+
setScrollTop(event.currentTarget.scrollTop);
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}} floatingContext={context} activeIndex={activeIndex} canScrollDown={!!showDownArrow} canScrollUp={!!showUpArrow} controlledScrolling dataRef={context.dataRef} listRef={listItemsRef} onChange={setValue} selectedIndex={selectedIndex} setActiveIndex={setActiveIndex} setOpen={setOpen} setSelectedIndex={setSelectedIndex} value={value} open={open}>{children}</SelectProvider>;
|
|
688
|
+
}, {
|
|
689
|
+
Content: SelectContent,
|
|
690
|
+
Group: SelectGroup,
|
|
691
|
+
Icon: SelectIcon,
|
|
692
|
+
Item: SelectItem,
|
|
693
|
+
ItemIndicator: SelectItemIndicator,
|
|
694
|
+
ItemText: SelectItemText,
|
|
695
|
+
Label: SelectLabel,
|
|
696
|
+
ScrollDownButton: SelectScrollDownButton,
|
|
697
|
+
ScrollUpButton: SelectScrollUpButton,
|
|
698
|
+
Trigger: SelectTrigger,
|
|
699
|
+
Value: SelectValue,
|
|
700
|
+
Viewport: SelectViewport
|
|
285
701
|
});
|
|
702
|
+
Select.displayName = SELECT_NAME;
|
|
286
703
|
export {
|
|
287
704
|
Select,
|
|
288
|
-
|
|
289
|
-
SelectGroup,
|
|
705
|
+
SelectGroupFrame,
|
|
290
706
|
SelectIcon,
|
|
291
707
|
SelectItem,
|
|
292
|
-
|
|
293
|
-
SelectItemText,
|
|
294
|
-
SelectLabel,
|
|
295
|
-
SelectScrollDownButton,
|
|
296
|
-
SelectScrollUpButton,
|
|
708
|
+
SelectItemTextFrame,
|
|
297
709
|
SelectSeparator,
|
|
298
710
|
SelectTrigger,
|
|
299
|
-
|
|
711
|
+
SelectViewport,
|
|
712
|
+
SelectViewportFrame,
|
|
300
713
|
createSelectScope
|
|
301
714
|
};
|
|
715
|
+
//# sourceMappingURL=Select.js.map
|