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