@tamagui/select 1.0.1-beta.103 → 1.0.1-beta.106
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/Select.js +185 -81
- package/dist/cjs/Select.js.map +2 -2
- package/dist/esm/Select.js +174 -70
- package/dist/esm/Select.js.map +2 -2
- package/dist/jsx/Select.js +137 -62
- package/dist/jsx/Select.js.map +2 -2
- package/package.json +12 -11
- package/src/Select.tsx +717 -585
- package/types/Select.d.ts +89 -20
- package/types/Select.d.ts.map +1 -1
package/src/Select.tsx
CHANGED
|
@@ -20,11 +20,11 @@ import {
|
|
|
20
20
|
} from '@floating-ui/react-dom-interactions'
|
|
21
21
|
import { usePrevious } from '@radix-ui/react-use-previous'
|
|
22
22
|
import { useComposedRefs } from '@tamagui/compose-refs'
|
|
23
|
+
import { GetProps, MediaPropKeys, SizeTokens, TamaguiElement, isWeb, useMedia } from '@tamagui/core'
|
|
23
24
|
import {
|
|
24
|
-
GetProps,
|
|
25
|
-
SizeTokens,
|
|
26
25
|
Theme,
|
|
27
26
|
styled,
|
|
27
|
+
useGet,
|
|
28
28
|
useIsomorphicLayoutEffect,
|
|
29
29
|
useThemeName,
|
|
30
30
|
withStaticProperties,
|
|
@@ -33,29 +33,14 @@ import { useId } from '@tamagui/core'
|
|
|
33
33
|
import { createContextScope } from '@tamagui/create-context'
|
|
34
34
|
import type { Scope } from '@tamagui/create-context'
|
|
35
35
|
import { ListItem, ListItemProps } from '@tamagui/list-item'
|
|
36
|
+
import { PortalHost, PortalItem, PortalItemProps } from '@tamagui/portal'
|
|
36
37
|
import { Separator } from '@tamagui/separator'
|
|
38
|
+
import { Sheet, SheetController } from '@tamagui/sheet'
|
|
37
39
|
import { ThemeableStack, XStack, YStack, YStackProps } from '@tamagui/stacks'
|
|
38
40
|
import { Paragraph } from '@tamagui/text'
|
|
39
41
|
import { useControllableState } from '@tamagui/use-controllable-state'
|
|
40
42
|
import * as React from 'react'
|
|
41
43
|
import * as ReactDOM from 'react-dom'
|
|
42
|
-
import { View } from 'react-native'
|
|
43
|
-
|
|
44
|
-
type TamaguiElement = HTMLElement | View
|
|
45
|
-
|
|
46
|
-
// Cross browser fixes for pinch-zooming/backdrop-filter 🙄
|
|
47
|
-
const userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || ''
|
|
48
|
-
const isFirefox = userAgent.toLowerCase().includes('firefox')
|
|
49
|
-
if (isFirefox) {
|
|
50
|
-
document.body.classList.add('firefox')
|
|
51
|
-
}
|
|
52
|
-
function getVisualOffsetTop() {
|
|
53
|
-
return !/^((?!chrome|android).)*safari/i.test(userAgent) ? visualViewport?.offsetTop ?? 0 : 0
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/* -------------------------------------------------------------------------------------------------
|
|
57
|
-
* SelectContext
|
|
58
|
-
* -----------------------------------------------------------------------------------------------*/
|
|
59
44
|
|
|
60
45
|
const SELECT_NAME = 'Select'
|
|
61
46
|
const WINDOW_PADDING = 8
|
|
@@ -64,7 +49,28 @@ const SCROLL_ARROW_THRESHOLD = 8
|
|
|
64
49
|
const MIN_HEIGHT = 80
|
|
65
50
|
const FALLBACK_THRESHOLD = 16
|
|
66
51
|
|
|
52
|
+
export interface SelectProps {
|
|
53
|
+
id?: string
|
|
54
|
+
children?: React.ReactNode
|
|
55
|
+
value?: string
|
|
56
|
+
defaultValue?: string
|
|
57
|
+
onValueChange?(value: string): void
|
|
58
|
+
open?: boolean
|
|
59
|
+
defaultOpen?: boolean
|
|
60
|
+
onOpenChange?(open: boolean): void
|
|
61
|
+
dir?: Direction
|
|
62
|
+
name?: string
|
|
63
|
+
autoComplete?: string
|
|
64
|
+
size?: SizeTokens
|
|
65
|
+
sheetBreakpoint?: MediaPropKeys | false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type NonNull<A> = Exclude<A, void | null>
|
|
69
|
+
|
|
67
70
|
interface SelectContextValue {
|
|
71
|
+
dir?: Direction
|
|
72
|
+
scopeKey: string
|
|
73
|
+
sheetBreakpoint: NonNull<SelectProps['sheetBreakpoint']>
|
|
68
74
|
size?: SizeTokens
|
|
69
75
|
value: any
|
|
70
76
|
selectedIndex: number
|
|
@@ -72,29 +78,41 @@ interface SelectContextValue {
|
|
|
72
78
|
activeIndex: number | null
|
|
73
79
|
setActiveIndex: (index: number | null) => void
|
|
74
80
|
setValueAtIndex: (index: number, value: string) => void
|
|
75
|
-
listRef: React.MutableRefObject<Array<HTMLElement | null>>
|
|
76
|
-
floatingRef: React.MutableRefObject<HTMLElement | null>
|
|
77
81
|
open: boolean
|
|
78
82
|
setOpen: (open: boolean) => void
|
|
79
83
|
onChange: (value: string) => void
|
|
80
|
-
dataRef: React.MutableRefObject<ContextData>
|
|
81
|
-
controlledScrolling: boolean
|
|
82
84
|
valueNode: Element | null
|
|
83
85
|
onValueNodeChange(node: HTMLElement): void
|
|
84
86
|
valueNodeHasChildren: boolean
|
|
85
87
|
onValueNodeHasChildrenChange(hasChildren: boolean): void
|
|
86
|
-
canScrollUp: boolean
|
|
87
|
-
canScrollDown: boolean
|
|
88
|
-
floatingContext: FloatingContext<ReferenceType>
|
|
89
|
-
increaseHeight: (floating: HTMLElement, amount?: any) => number | undefined
|
|
90
88
|
forceUpdate: React.DispatchWithoutAction
|
|
91
|
-
|
|
89
|
+
|
|
90
|
+
// InlineImpl only:
|
|
91
|
+
dataRef?: React.MutableRefObject<ContextData>
|
|
92
|
+
controlledScrolling?: boolean
|
|
93
|
+
listRef?: React.MutableRefObject<Array<HTMLElement | null>>
|
|
94
|
+
floatingRef?: React.MutableRefObject<HTMLElement | null>
|
|
95
|
+
canScrollUp?: boolean
|
|
96
|
+
canScrollDown?: boolean
|
|
97
|
+
floatingContext?: FloatingContext<ReferenceType>
|
|
98
|
+
increaseHeight?: (floating: HTMLElement, amount?: any) => number | undefined
|
|
99
|
+
interactions?: {
|
|
92
100
|
getReferenceProps: (userProps?: React.HTMLProps<Element> | undefined) => any
|
|
93
101
|
getFloatingProps: (userProps?: React.HTMLProps<HTMLElement> | undefined) => any
|
|
94
102
|
getItemProps: (userProps?: React.HTMLProps<HTMLElement> | undefined) => any
|
|
95
103
|
}
|
|
96
104
|
}
|
|
97
105
|
|
|
106
|
+
// Cross browser fixes for pinch-zooming/backdrop-filter 🙄
|
|
107
|
+
const userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || ''
|
|
108
|
+
const isFirefox = userAgent.toLowerCase().includes('firefox')
|
|
109
|
+
if (isFirefox) {
|
|
110
|
+
document.body.classList.add('firefox')
|
|
111
|
+
}
|
|
112
|
+
function getVisualOffsetTop() {
|
|
113
|
+
return !/^((?!chrome|android).)*safari/i.test(userAgent) ? visualViewport?.offsetTop ?? 0 : 0
|
|
114
|
+
}
|
|
115
|
+
|
|
98
116
|
type ScopedProps<P> = P & { __scopeSelect?: Scope }
|
|
99
117
|
|
|
100
118
|
const [createSelectContext, createSelectScope] = createContextScope(SELECT_NAME)
|
|
@@ -103,8 +121,6 @@ const [SelectProvider, useSelectContext] = createSelectContext<SelectContextValu
|
|
|
103
121
|
// const [SelectContentContextProvider, useSelectContentContext] =
|
|
104
122
|
// createSelectContext<SelectContentContextValue>(CONTENT_NAME);
|
|
105
123
|
|
|
106
|
-
type GenericElement = HTMLElement | View
|
|
107
|
-
|
|
108
124
|
type Direction = 'ltr' | 'rtl'
|
|
109
125
|
|
|
110
126
|
/* -------------------------------------------------------------------------------------------------
|
|
@@ -115,7 +131,7 @@ const TRIGGER_NAME = 'SelectTrigger'
|
|
|
115
131
|
|
|
116
132
|
export type SelectTriggerProps = ListItemProps
|
|
117
133
|
|
|
118
|
-
export const SelectTrigger = React.forwardRef<
|
|
134
|
+
export const SelectTrigger = React.forwardRef<TamaguiElement, SelectTriggerProps>(
|
|
119
135
|
(props: ScopedProps<SelectTriggerProps>, forwardedRef) => {
|
|
120
136
|
const {
|
|
121
137
|
__scopeSelect,
|
|
@@ -128,7 +144,7 @@ export const SelectTrigger = React.forwardRef<GenericElement, SelectTriggerProps
|
|
|
128
144
|
// const composedRefs = useComposedRefs(forwardedRef, context.onTriggerChange)
|
|
129
145
|
// const getItems = useCollection(__scopeSelect)
|
|
130
146
|
// const labelId = useLabelContext(context.trigger)
|
|
131
|
-
|
|
147
|
+
const labelledBy = ariaLabelledby // || labelId
|
|
132
148
|
|
|
133
149
|
return (
|
|
134
150
|
<ListItem
|
|
@@ -144,13 +160,19 @@ export const SelectTrigger = React.forwardRef<GenericElement, SelectTriggerProps
|
|
|
144
160
|
// aria-controls={context.contentId}
|
|
145
161
|
aria-expanded={context.open}
|
|
146
162
|
aria-autocomplete="none"
|
|
147
|
-
|
|
148
|
-
|
|
163
|
+
aria-labelledby={labelledBy}
|
|
164
|
+
dir={context.dir}
|
|
149
165
|
disabled={disabled}
|
|
150
166
|
data-disabled={disabled ? '' : undefined}
|
|
151
167
|
{...triggerProps}
|
|
152
168
|
ref={forwardedRef}
|
|
153
|
-
{...context.interactions
|
|
169
|
+
{...(context.interactions
|
|
170
|
+
? context.interactions.getReferenceProps()
|
|
171
|
+
: {
|
|
172
|
+
onPress() {
|
|
173
|
+
context.setOpen(!context.open)
|
|
174
|
+
},
|
|
175
|
+
})}
|
|
154
176
|
/>
|
|
155
177
|
)
|
|
156
178
|
}
|
|
@@ -225,9 +247,13 @@ export type SelectContentProps = { children?: React.ReactNode }
|
|
|
225
247
|
const SelectContent = ({ children, __scopeSelect }: ScopedProps<SelectContentProps>) => {
|
|
226
248
|
const context = useSelectContext(CONTENT_NAME, __scopeSelect)
|
|
227
249
|
const themeName = useThemeName()
|
|
228
|
-
|
|
250
|
+
const showSheet = useShowSelectSheet(context)
|
|
229
251
|
const contents = <Theme name={themeName}>{children}</Theme>
|
|
230
252
|
|
|
253
|
+
if (showSheet && context.open) {
|
|
254
|
+
return contents
|
|
255
|
+
}
|
|
256
|
+
|
|
231
257
|
return (
|
|
232
258
|
<FloatingPortal>
|
|
233
259
|
{context.open ? (
|
|
@@ -250,6 +276,7 @@ export const SelectViewportFrame = styled(ThemeableStack, {
|
|
|
250
276
|
backgroundColor: '$background',
|
|
251
277
|
elevate: true,
|
|
252
278
|
bordered: true,
|
|
279
|
+
// width: '100%',
|
|
253
280
|
overflow: 'scroll',
|
|
254
281
|
userSelect: 'none',
|
|
255
282
|
|
|
@@ -274,9 +301,21 @@ export const SelectViewport = React.forwardRef<TamaguiElement, SelectViewportPro
|
|
|
274
301
|
(props: ScopedProps<SelectViewportProps>, forwardedRef) => {
|
|
275
302
|
const { __scopeSelect, children, ...viewportProps } = props
|
|
276
303
|
const context = useSelectContext(VIEWPORT_NAME, __scopeSelect)
|
|
277
|
-
const { style, ...floatingProps } = context.interactions
|
|
304
|
+
const { style, ...floatingProps } = context.interactions?.getFloatingProps() ?? {}
|
|
305
|
+
const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint)
|
|
306
|
+
|
|
307
|
+
if (breakpointActive || !isWeb) {
|
|
308
|
+
return <PortalItem hostName={`${context.scopeKey}SheetContents`}>{children}</PortalItem>
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!context.floatingContext) {
|
|
312
|
+
return null
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// TODO
|
|
278
316
|
delete style['scrollbarWidth']
|
|
279
317
|
delete style['listStyleType']
|
|
318
|
+
|
|
280
319
|
return (
|
|
281
320
|
<>
|
|
282
321
|
{/* Hide scrollbars cross-browser and enable momentum scroll for touch devices */}
|
|
@@ -364,7 +403,9 @@ export const SelectItem = React.forwardRef<TamaguiElement, SelectItemProps>(
|
|
|
364
403
|
forwardedRef,
|
|
365
404
|
(node) => {
|
|
366
405
|
if (node instanceof HTMLElement) {
|
|
367
|
-
listRef
|
|
406
|
+
if (listRef) {
|
|
407
|
+
listRef.current[index] = node
|
|
408
|
+
}
|
|
368
409
|
}
|
|
369
410
|
}
|
|
370
411
|
// isSelected ? context.onSelectedItemChange : undefined
|
|
@@ -372,7 +413,7 @@ export const SelectItem = React.forwardRef<TamaguiElement, SelectItemProps>(
|
|
|
372
413
|
|
|
373
414
|
React.useEffect(() => {
|
|
374
415
|
setValueAtIndex(index, value)
|
|
375
|
-
}, [index])
|
|
416
|
+
}, [index, setValueAtIndex, value])
|
|
376
417
|
|
|
377
418
|
const timeoutRef = React.useRef<any>()
|
|
378
419
|
const [allowMouseUp, setAllowMouseUp] = React.useState(false)
|
|
@@ -399,17 +440,21 @@ export const SelectItem = React.forwardRef<TamaguiElement, SelectItemProps>(
|
|
|
399
440
|
}, [open, index, setActiveIndex, selectedIndex])
|
|
400
441
|
|
|
401
442
|
function handleKeyDown(event: React.KeyboardEvent) {
|
|
402
|
-
if (event.key === 'Enter' || (event.key === ' ' && !dataRef
|
|
443
|
+
if (event.key === 'Enter' || (event.key === ' ' && !dataRef?.current.typing)) {
|
|
403
444
|
event.preventDefault()
|
|
404
445
|
handleSelect()
|
|
405
446
|
}
|
|
406
447
|
}
|
|
407
448
|
|
|
408
|
-
const selectItemProps = context.interactions
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
449
|
+
const selectItemProps = context.interactions
|
|
450
|
+
? context.interactions.getItemProps({
|
|
451
|
+
onClick: allowMouseUp ? handleSelect : undefined,
|
|
452
|
+
onMouseUp: allowMouseUp ? handleSelect : undefined,
|
|
453
|
+
onKeyDown: handleKeyDown,
|
|
454
|
+
})
|
|
455
|
+
: {
|
|
456
|
+
onPress: handleSelect,
|
|
457
|
+
}
|
|
413
458
|
|
|
414
459
|
return (
|
|
415
460
|
<SelectItemContextProvider
|
|
@@ -461,8 +506,7 @@ type SelectItemTextProps = GetProps<typeof SelectItemTextFrame>
|
|
|
461
506
|
|
|
462
507
|
const SelectItemText = React.forwardRef<TamaguiElement, SelectItemTextProps>(
|
|
463
508
|
(props: ScopedProps<SelectItemTextProps>, forwardedRef) => {
|
|
464
|
-
|
|
465
|
-
const { __scopeSelect, className, style, ...itemTextProps } = props
|
|
509
|
+
const { __scopeSelect, className, ...itemTextProps } = props
|
|
466
510
|
const context = useSelectContext(ITEM_TEXT_NAME, __scopeSelect)
|
|
467
511
|
const itemContext = useSelectItemContext(ITEM_TEXT_NAME, __scopeSelect)
|
|
468
512
|
const ref = React.useRef<TamaguiElement | null>(null)
|
|
@@ -471,6 +515,7 @@ const SelectItemText = React.forwardRef<TamaguiElement, SelectItemTextProps>(
|
|
|
471
515
|
return (
|
|
472
516
|
<>
|
|
473
517
|
<SelectItemTextFrame
|
|
518
|
+
className={className}
|
|
474
519
|
size={context.size}
|
|
475
520
|
id={itemContext.textId}
|
|
476
521
|
{...itemTextProps}
|
|
@@ -639,99 +684,102 @@ interface SelectScrollButtonImplProps extends YStackProps {
|
|
|
639
684
|
componentName: string
|
|
640
685
|
}
|
|
641
686
|
|
|
642
|
-
const SelectScrollButtonImpl = React.
|
|
643
|
-
SelectScrollButtonImplElement,
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
687
|
+
const SelectScrollButtonImpl = React.memo(
|
|
688
|
+
React.forwardRef<SelectScrollButtonImplElement, SelectScrollButtonImplProps>(
|
|
689
|
+
(props: ScopedProps<SelectScrollButtonImplProps>, forwardedRef) => {
|
|
690
|
+
const { __scopeSelect, dir, componentName, ...scrollIndicatorProps } = props
|
|
691
|
+
const { floatingRef, increaseHeight, forceUpdate, ...context } = useSelectContext(
|
|
692
|
+
componentName,
|
|
693
|
+
__scopeSelect
|
|
694
|
+
)
|
|
695
|
+
const intervalRef = React.useRef<any>()
|
|
696
|
+
const loopingRef = React.useRef(false)
|
|
697
|
+
const isVisible = context[dir === 'down' ? 'canScrollDown' : 'canScrollUp']
|
|
698
|
+
|
|
699
|
+
const { x, y, reference, floating, strategy, update, refs } = useFloating({
|
|
700
|
+
strategy: 'fixed',
|
|
701
|
+
placement: dir === 'up' ? 'top' : 'bottom',
|
|
702
|
+
middleware: [offset(({ rects }) => -rects.floating.height)],
|
|
703
|
+
})
|
|
654
704
|
|
|
655
|
-
|
|
656
|
-
strategy: 'fixed',
|
|
657
|
-
placement: dir === 'up' ? 'top' : 'bottom',
|
|
658
|
-
middleware: [offset(({ rects }) => -rects.floating.height)],
|
|
659
|
-
})
|
|
705
|
+
const composedRefs = useComposedRefs(forwardedRef, floating)
|
|
660
706
|
|
|
661
|
-
|
|
707
|
+
React.useLayoutEffect(() => {
|
|
708
|
+
if (!floatingRef) return
|
|
709
|
+
reference(floatingRef.current)
|
|
710
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
711
|
+
}, [reference, floatingRef?.current])
|
|
662
712
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
713
|
+
React.useEffect(() => {
|
|
714
|
+
if (!refs.reference.current || !refs.floating.current || !isVisible) {
|
|
715
|
+
return
|
|
716
|
+
}
|
|
667
717
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
}
|
|
718
|
+
const cleanup = autoUpdate(refs.reference.current, refs.floating.current, update, {
|
|
719
|
+
animationFrame: true,
|
|
720
|
+
})
|
|
672
721
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
722
|
+
return () => {
|
|
723
|
+
clearInterval(intervalRef.current)
|
|
724
|
+
loopingRef.current = false
|
|
725
|
+
cleanup()
|
|
726
|
+
}
|
|
727
|
+
}, [isVisible, update, refs.floating, refs.reference])
|
|
676
728
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
cleanup()
|
|
681
|
-
}
|
|
682
|
-
}, [isVisible, update, refs.floating, refs.reference])
|
|
729
|
+
if (!isVisible) {
|
|
730
|
+
return null
|
|
731
|
+
}
|
|
683
732
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
733
|
+
const handleScrollArrowChange = () => {
|
|
734
|
+
if (!floatingRef) return
|
|
735
|
+
const floating = floatingRef.current
|
|
736
|
+
const isUp = dir === 'up'
|
|
687
737
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
floating.scrollTop >=
|
|
698
|
-
floating.scrollHeight - floating.clientHeight - SCROLL_ARROW_THRESHOLD * 2)
|
|
699
|
-
? 2
|
|
700
|
-
: 1
|
|
701
|
-
|
|
702
|
-
floating.scrollTop += multi * (isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY)
|
|
703
|
-
|
|
704
|
-
increaseHeight(floating, multi === 2 ? value * 2 : value)
|
|
705
|
-
// Ensure derived data (scroll arrows) is fresh
|
|
706
|
-
forceUpdate()
|
|
707
|
-
}
|
|
708
|
-
}
|
|
738
|
+
if (floating) {
|
|
739
|
+
const value = isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY
|
|
740
|
+
const multi =
|
|
741
|
+
(isUp && floating.scrollTop <= SCROLL_ARROW_THRESHOLD * 2) ||
|
|
742
|
+
(!isUp &&
|
|
743
|
+
floating.scrollTop >=
|
|
744
|
+
floating.scrollHeight - floating.clientHeight - SCROLL_ARROW_THRESHOLD * 2)
|
|
745
|
+
? 2
|
|
746
|
+
: 1
|
|
709
747
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
{...scrollIndicatorProps}
|
|
716
|
-
zIndex={1000}
|
|
717
|
-
// @ts-expect-error
|
|
718
|
-
position={strategy}
|
|
719
|
-
left={x || 0}
|
|
720
|
-
top={y || 0}
|
|
721
|
-
width={`calc(${(floatingRef.current?.offsetWidth ?? 0) - 2}px)`}
|
|
722
|
-
onPointerMove={() => {
|
|
723
|
-
if (!loopingRef.current) {
|
|
724
|
-
intervalRef.current = setInterval(handleScrollArrowChange, 1000 / 60)
|
|
725
|
-
loopingRef.current = true
|
|
748
|
+
floating.scrollTop += multi * (isUp ? -SCROLL_ARROW_VELOCITY : SCROLL_ARROW_VELOCITY)
|
|
749
|
+
|
|
750
|
+
increaseHeight?.(floating, multi === 2 ? value * 2 : value)
|
|
751
|
+
// Ensure derived data (scroll arrows) is fresh
|
|
752
|
+
forceUpdate()
|
|
726
753
|
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return (
|
|
757
|
+
<YStack
|
|
758
|
+
ref={composedRefs}
|
|
759
|
+
componentName={componentName}
|
|
760
|
+
aria-hidden
|
|
761
|
+
{...scrollIndicatorProps}
|
|
762
|
+
zIndex={1000}
|
|
763
|
+
// @ts-expect-error
|
|
764
|
+
position={strategy}
|
|
765
|
+
left={x || 0}
|
|
766
|
+
top={y || 0}
|
|
767
|
+
width={`calc(${(floatingRef?.current?.offsetWidth ?? 0) - 2}px)`}
|
|
768
|
+
onPointerMove={() => {
|
|
769
|
+
if (!loopingRef.current) {
|
|
770
|
+
intervalRef.current = setInterval(handleScrollArrowChange, 1000 / 60)
|
|
771
|
+
loopingRef.current = true
|
|
772
|
+
}
|
|
773
|
+
}}
|
|
774
|
+
onPointerLeave={() => {
|
|
775
|
+
loopingRef.current = false
|
|
776
|
+
clearInterval(intervalRef.current)
|
|
777
|
+
}}
|
|
778
|
+
/>
|
|
779
|
+
)
|
|
780
|
+
}
|
|
733
781
|
)
|
|
734
|
-
|
|
782
|
+
)
|
|
735
783
|
|
|
736
784
|
/* -------------------------------------------------------------------------------------------------
|
|
737
785
|
* SelectSeparator
|
|
@@ -741,539 +789,622 @@ export const SelectSeparator = styled(Separator, {
|
|
|
741
789
|
name: 'SelectSeparator',
|
|
742
790
|
})
|
|
743
791
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
792
|
+
const useSelectBreakpointActive = (sheetBreakpoint: SelectContextValue['sheetBreakpoint']) => {
|
|
793
|
+
const media = useMedia()
|
|
794
|
+
return sheetBreakpoint ? media[sheetBreakpoint] : false
|
|
795
|
+
}
|
|
747
796
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
value?: string
|
|
752
|
-
defaultValue?: string
|
|
753
|
-
onValueChange?(value: string): void
|
|
754
|
-
open?: boolean
|
|
755
|
-
defaultOpen?: boolean
|
|
756
|
-
onOpenChange?(open: boolean): void
|
|
757
|
-
dir?: Direction
|
|
758
|
-
name?: string
|
|
759
|
-
autoComplete?: string
|
|
760
|
-
size?: SizeTokens
|
|
797
|
+
const useShowSelectSheet = (context: SelectContextValue) => {
|
|
798
|
+
const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint)
|
|
799
|
+
return context.open === false ? false : breakpointActive
|
|
761
800
|
}
|
|
762
801
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
value: valueProp,
|
|
774
|
-
defaultValue,
|
|
775
|
-
onValueChange,
|
|
776
|
-
size: sizeProp,
|
|
777
|
-
dir,
|
|
778
|
-
name,
|
|
779
|
-
autoComplete,
|
|
780
|
-
} = props
|
|
802
|
+
const SelectSheetController = (
|
|
803
|
+
props: ScopedProps<{}> & {
|
|
804
|
+
children: React.ReactNode
|
|
805
|
+
onChangeOpen: React.Dispatch<React.SetStateAction<boolean>>
|
|
806
|
+
}
|
|
807
|
+
) => {
|
|
808
|
+
const context = useSelectContext('SelectSheetController', props.__scopeSelect)
|
|
809
|
+
const showSheet = useShowSelectSheet(context)
|
|
810
|
+
const breakpointActive = useSelectBreakpointActive(context.sheetBreakpoint)
|
|
811
|
+
const getShowSheet = useGet(showSheet)
|
|
781
812
|
|
|
782
|
-
|
|
813
|
+
return (
|
|
814
|
+
<SheetController
|
|
815
|
+
onChangeOpen={(val) => {
|
|
816
|
+
if (getShowSheet()) {
|
|
817
|
+
props.onChangeOpen(val)
|
|
818
|
+
}
|
|
819
|
+
}}
|
|
820
|
+
open={context.open}
|
|
821
|
+
hidden={breakpointActive === false}
|
|
822
|
+
>
|
|
823
|
+
{props.children}
|
|
824
|
+
</SheetController>
|
|
825
|
+
)
|
|
826
|
+
}
|
|
783
827
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
onChange: onOpenChange,
|
|
788
|
-
})
|
|
828
|
+
/* -------------------------------------------------------------------------------------------------
|
|
829
|
+
* SelectSheetContents
|
|
830
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
789
831
|
|
|
790
|
-
|
|
791
|
-
prop: valueProp,
|
|
792
|
-
defaultProp: defaultValue || '',
|
|
793
|
-
onChange: onValueChange,
|
|
794
|
-
})
|
|
832
|
+
const SHEET_CONTENTS_NAME = 'SelectSheetContents'
|
|
795
833
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
834
|
+
export const SelectSheetContents = ({ __scopeSelect }: ScopedProps<{}>) => {
|
|
835
|
+
const context = useSelectContext(SHEET_CONTENTS_NAME, __scopeSelect)
|
|
836
|
+
return <PortalHost name={`${context.scopeKey}SheetContents`} />
|
|
837
|
+
}
|
|
800
838
|
|
|
801
|
-
|
|
802
|
-
const [scrollTop, setScrollTop] = React.useState(0)
|
|
803
|
-
const listItemsRef = React.useRef<Array<HTMLElement | null>>([])
|
|
804
|
-
const listContentRef = React.useRef<string[]>([])
|
|
839
|
+
SelectSheetContents.displayName = SHEET_CONTENTS_NAME
|
|
805
840
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
841
|
+
/* -------------------------------------------------------------------------------------------------
|
|
842
|
+
* Select
|
|
843
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
809
844
|
|
|
810
|
-
|
|
811
|
-
|
|
845
|
+
type SelectImplProps = ScopedProps<SelectProps> & {
|
|
846
|
+
activeIndexRef: any
|
|
847
|
+
selectedIndexRef: any
|
|
848
|
+
listContentRef: any
|
|
849
|
+
}
|
|
812
850
|
|
|
813
|
-
|
|
814
|
-
selectedIndexRef.current = selectedIndex
|
|
815
|
-
activeIndexRef.current = activeIndex
|
|
816
|
-
})
|
|
851
|
+
// TODO use id for focusing from label
|
|
817
852
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
853
|
+
const SelectInlineImpl = (props: SelectImplProps) => {
|
|
854
|
+
const {
|
|
855
|
+
__scopeSelect,
|
|
856
|
+
children,
|
|
857
|
+
open = false,
|
|
858
|
+
activeIndexRef,
|
|
859
|
+
selectedIndexRef,
|
|
860
|
+
listContentRef,
|
|
861
|
+
} = props
|
|
862
|
+
|
|
863
|
+
const selectContext = useSelectContext('SelectSheetImpl', __scopeSelect)
|
|
864
|
+
const { setActiveIndex, setOpen, setSelectedIndex, selectedIndex, activeIndex, forceUpdate } =
|
|
865
|
+
selectContext
|
|
866
|
+
const [showArrows, setShowArrows] = React.useState(false)
|
|
867
|
+
const [scrollTop, setScrollTop] = React.useState(0)
|
|
868
|
+
const prevActiveIndex = usePrevious<number | null>(activeIndex)
|
|
869
|
+
|
|
870
|
+
const listItemsRef = React.useRef<Array<HTMLElement | null>>([])
|
|
835
871
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
872
|
+
const [controlledScrolling, setControlledScrolling] = React.useState(false)
|
|
873
|
+
const [middlewareType, setMiddlewareType] = React.useState<'align' | 'fallback'>('align')
|
|
874
|
+
|
|
875
|
+
// Wait for scroll position to settle before showing arrows to prevent
|
|
876
|
+
// interference with pointer events.
|
|
877
|
+
React.useEffect(() => {
|
|
878
|
+
const frame = requestAnimationFrame(() => {
|
|
879
|
+
setShowArrows(open)
|
|
880
|
+
|
|
881
|
+
if (!open) {
|
|
882
|
+
setScrollTop(0)
|
|
883
|
+
setMiddlewareType('align')
|
|
884
|
+
setActiveIndex(null)
|
|
885
|
+
setControlledScrolling(false)
|
|
839
886
|
}
|
|
840
|
-
|
|
887
|
+
})
|
|
888
|
+
return () => {
|
|
889
|
+
cancelAnimationFrame(frame)
|
|
841
890
|
}
|
|
891
|
+
}, [open, setActiveIndex])
|
|
842
892
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
middlewareType === 'align'
|
|
850
|
-
? [
|
|
851
|
-
offset(({ rects }) => {
|
|
852
|
-
const index = activeIndexRef.current ?? selectedIndexRef.current
|
|
853
|
-
|
|
854
|
-
if (index == null) {
|
|
855
|
-
return 0
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
const item = listItemsRef.current[index]
|
|
893
|
+
function getFloatingPadding(floating: HTMLElement | null) {
|
|
894
|
+
if (!floating) {
|
|
895
|
+
return 0
|
|
896
|
+
}
|
|
897
|
+
return Number(getComputedStyle(floating).paddingLeft?.replace('px', ''))
|
|
898
|
+
}
|
|
859
899
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
900
|
+
const { x, y, reference, floating, strategy, context, refs, middlewareData, update } =
|
|
901
|
+
useFloating({
|
|
902
|
+
open,
|
|
903
|
+
onOpenChange: setOpen,
|
|
904
|
+
placement: 'bottom',
|
|
905
|
+
middleware:
|
|
906
|
+
middlewareType === 'align'
|
|
907
|
+
? [
|
|
908
|
+
offset(({ rects }) => {
|
|
909
|
+
const index = activeIndexRef.current ?? selectedIndexRef.current
|
|
910
|
+
|
|
911
|
+
if (index == null) {
|
|
912
|
+
return 0
|
|
913
|
+
}
|
|
863
914
|
|
|
864
|
-
|
|
865
|
-
const itemHeight = item.offsetHeight
|
|
866
|
-
const height = rects.reference.height
|
|
867
|
-
|
|
868
|
-
return -offsetTop - height - (itemHeight - height) / 2
|
|
869
|
-
}),
|
|
870
|
-
// Custom `size` that can handle the opposite direction of the placement
|
|
871
|
-
{
|
|
872
|
-
name: 'size',
|
|
873
|
-
async fn(args) {
|
|
874
|
-
const {
|
|
875
|
-
elements: { floating },
|
|
876
|
-
rects: { reference },
|
|
877
|
-
middlewareData,
|
|
878
|
-
} = args
|
|
879
|
-
|
|
880
|
-
const overflow = await detectOverflow(args, {
|
|
881
|
-
padding: WINDOW_PADDING,
|
|
882
|
-
})
|
|
883
|
-
|
|
884
|
-
const top = Math.max(0, overflow.top)
|
|
885
|
-
const bottom = Math.max(0, overflow.bottom)
|
|
886
|
-
const nextY = args.y + top
|
|
887
|
-
|
|
888
|
-
if (middlewareData.size?.skip) {
|
|
889
|
-
return {
|
|
890
|
-
y: nextY,
|
|
891
|
-
data: {
|
|
892
|
-
y: middlewareData.size.y,
|
|
893
|
-
},
|
|
894
|
-
}
|
|
895
|
-
}
|
|
915
|
+
const item = listItemsRef.current[index]
|
|
896
916
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
})
|
|
917
|
+
if (item == null) {
|
|
918
|
+
return 0
|
|
919
|
+
}
|
|
901
920
|
|
|
921
|
+
const offsetTop = item.offsetTop
|
|
922
|
+
const itemHeight = item.offsetHeight
|
|
923
|
+
const height = rects.reference.height
|
|
924
|
+
|
|
925
|
+
return -offsetTop - height - (itemHeight - height) / 2
|
|
926
|
+
}),
|
|
927
|
+
// Custom `size` that can handle the opposite direction of the placement
|
|
928
|
+
{
|
|
929
|
+
name: 'size',
|
|
930
|
+
async fn(args) {
|
|
931
|
+
const {
|
|
932
|
+
elements: { floating },
|
|
933
|
+
rects: { reference },
|
|
934
|
+
middlewareData,
|
|
935
|
+
} = args
|
|
936
|
+
|
|
937
|
+
const overflow = await detectOverflow(args, {
|
|
938
|
+
padding: WINDOW_PADDING,
|
|
939
|
+
})
|
|
940
|
+
|
|
941
|
+
const top = Math.max(0, overflow.top)
|
|
942
|
+
const bottom = Math.max(0, overflow.bottom)
|
|
943
|
+
const nextY = args.y + top
|
|
944
|
+
|
|
945
|
+
if (middlewareData.size?.skip) {
|
|
902
946
|
return {
|
|
903
947
|
y: nextY,
|
|
904
948
|
data: {
|
|
905
|
-
y:
|
|
906
|
-
skip: true,
|
|
907
|
-
},
|
|
908
|
-
reset: {
|
|
909
|
-
rects: true,
|
|
949
|
+
y: middlewareData.size.y,
|
|
910
950
|
},
|
|
911
951
|
}
|
|
912
|
-
}
|
|
913
|
-
},
|
|
914
|
-
]
|
|
915
|
-
: [
|
|
916
|
-
offset(5),
|
|
917
|
-
flip(),
|
|
918
|
-
size({
|
|
919
|
-
apply({ rects, availableHeight, elements }) {
|
|
920
|
-
Object.assign(elements.floating.style, {
|
|
921
|
-
width: `${rects.reference.width}px`,
|
|
922
|
-
maxHeight: `${availableHeight}px`,
|
|
923
|
-
})
|
|
924
|
-
},
|
|
925
|
-
padding: WINDOW_PADDING,
|
|
926
|
-
}),
|
|
927
|
-
],
|
|
928
|
-
})
|
|
952
|
+
}
|
|
929
953
|
|
|
930
|
-
|
|
931
|
-
|
|
954
|
+
Object.assign(floating.style, {
|
|
955
|
+
maxHeight: `${floating.scrollHeight - Math.abs(top + bottom)}px`,
|
|
956
|
+
minWidth: `${reference.width + getFloatingPadding(floating) * 2}px`,
|
|
957
|
+
})
|
|
958
|
+
|
|
959
|
+
return {
|
|
960
|
+
y: nextY,
|
|
961
|
+
data: {
|
|
962
|
+
y: top,
|
|
963
|
+
skip: true,
|
|
964
|
+
},
|
|
965
|
+
reset: {
|
|
966
|
+
rects: true,
|
|
967
|
+
},
|
|
968
|
+
}
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
]
|
|
972
|
+
: [
|
|
973
|
+
offset(5),
|
|
974
|
+
flip(),
|
|
975
|
+
size({
|
|
976
|
+
apply({ rects, availableHeight, elements }) {
|
|
977
|
+
Object.assign(elements.floating.style, {
|
|
978
|
+
width: `${rects.reference.width}px`,
|
|
979
|
+
maxHeight: `${availableHeight}px`,
|
|
980
|
+
})
|
|
981
|
+
},
|
|
982
|
+
padding: WINDOW_PADDING,
|
|
983
|
+
}),
|
|
984
|
+
],
|
|
985
|
+
})
|
|
932
986
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
(floating: HTMLElement, amount = 0) => {
|
|
960
|
-
if (middlewareType === 'fallback') {
|
|
961
|
-
return
|
|
962
|
-
}
|
|
987
|
+
const floatingRef = refs.floating
|
|
988
|
+
|
|
989
|
+
const showUpArrow = showArrows && scrollTop > SCROLL_ARROW_THRESHOLD
|
|
990
|
+
const showDownArrow =
|
|
991
|
+
showArrows &&
|
|
992
|
+
floatingRef.current &&
|
|
993
|
+
scrollTop <
|
|
994
|
+
floatingRef.current.scrollHeight - floatingRef.current.clientHeight - SCROLL_ARROW_THRESHOLD
|
|
995
|
+
|
|
996
|
+
const interactions = useInteractions([
|
|
997
|
+
useClick(context, { pointerDown: true }),
|
|
998
|
+
useRole(context, { role: 'listbox' }),
|
|
999
|
+
useDismiss(context),
|
|
1000
|
+
useListNavigation(context, {
|
|
1001
|
+
listRef: listItemsRef,
|
|
1002
|
+
activeIndex,
|
|
1003
|
+
selectedIndex,
|
|
1004
|
+
onNavigate: setActiveIndex,
|
|
1005
|
+
}),
|
|
1006
|
+
useTypeahead(context, {
|
|
1007
|
+
listRef: listContentRef,
|
|
1008
|
+
onMatch: open ? setActiveIndex : setSelectedIndex,
|
|
1009
|
+
selectedIndex,
|
|
1010
|
+
activeIndex,
|
|
1011
|
+
}),
|
|
1012
|
+
])
|
|
963
1013
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
const viewportHeight = visualViewport?.height ?? 0
|
|
970
|
-
const visualMaxHeight = viewportHeight - WINDOW_PADDING * 2
|
|
971
|
-
|
|
972
|
-
if (
|
|
973
|
-
amount < 0 &&
|
|
974
|
-
selectedIndexRef.current != null &&
|
|
975
|
-
Math.round(rectBottom) <
|
|
976
|
-
Math.round(viewportHeight + getVisualOffsetTop() - WINDOW_PADDING)
|
|
977
|
-
) {
|
|
978
|
-
floating.style.maxHeight = `${Math.min(visualMaxHeight, currentMaxHeight - amount)}px`
|
|
979
|
-
}
|
|
1014
|
+
const increaseHeight = React.useCallback(
|
|
1015
|
+
(floating: HTMLElement, amount = 0) => {
|
|
1016
|
+
if (middlewareType === 'fallback') {
|
|
1017
|
+
return
|
|
1018
|
+
}
|
|
980
1019
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1020
|
+
const currentMaxHeight = Number(floating.style.maxHeight.replace('px', ''))
|
|
1021
|
+
const currentTop = Number(floating.style.top.replace('px', ''))
|
|
1022
|
+
const rect = floating.getBoundingClientRect()
|
|
1023
|
+
const rectTop = rect.top
|
|
1024
|
+
const rectBottom = rect.bottom
|
|
1025
|
+
const viewportHeight = visualViewport?.height ?? 0
|
|
1026
|
+
const visualMaxHeight = viewportHeight - WINDOW_PADDING * 2
|
|
1027
|
+
|
|
1028
|
+
if (
|
|
1029
|
+
amount < 0 &&
|
|
1030
|
+
selectedIndexRef.current != null &&
|
|
1031
|
+
Math.round(rectBottom) < Math.round(viewportHeight + getVisualOffsetTop() - WINDOW_PADDING)
|
|
1032
|
+
) {
|
|
1033
|
+
floating.style.maxHeight = `${Math.min(visualMaxHeight, currentMaxHeight - amount)}px`
|
|
1034
|
+
}
|
|
987
1035
|
|
|
988
|
-
|
|
1036
|
+
if (
|
|
1037
|
+
amount > 0 &&
|
|
1038
|
+
Math.round(rectTop) > Math.round(WINDOW_PADDING - getVisualOffsetTop()) &&
|
|
1039
|
+
floating.scrollHeight > floating.offsetHeight
|
|
1040
|
+
) {
|
|
1041
|
+
const nextTop = Math.max(WINDOW_PADDING + getVisualOffsetTop(), currentTop - amount)
|
|
989
1042
|
|
|
990
|
-
|
|
991
|
-
maxHeight: `${nextMaxHeight}px`,
|
|
992
|
-
top: `${nextTop}px`,
|
|
993
|
-
})
|
|
1043
|
+
const nextMaxHeight = Math.min(visualMaxHeight, currentMaxHeight + amount)
|
|
994
1044
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
}
|
|
1045
|
+
Object.assign(floating.style, {
|
|
1046
|
+
maxHeight: `${nextMaxHeight}px`,
|
|
1047
|
+
top: `${nextTop}px`,
|
|
1048
|
+
})
|
|
998
1049
|
|
|
999
|
-
|
|
1050
|
+
if (nextTop - WINDOW_PADDING > getVisualOffsetTop()) {
|
|
1051
|
+
floating.scrollTop -= nextMaxHeight - currentMaxHeight + getFloatingPadding(floating)
|
|
1000
1052
|
}
|
|
1001
|
-
},
|
|
1002
|
-
[middlewareType]
|
|
1003
|
-
)
|
|
1004
1053
|
|
|
1005
|
-
|
|
1054
|
+
return currentTop - nextTop
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
[middlewareType, selectedIndexRef]
|
|
1058
|
+
)
|
|
1006
1059
|
|
|
1007
|
-
|
|
1008
|
-
(event: WheelEvent | TouchEvent) => {
|
|
1009
|
-
const pinching = event.ctrlKey
|
|
1060
|
+
const touchPageYRef = React.useRef<number | null>(null)
|
|
1010
1061
|
|
|
1011
|
-
|
|
1062
|
+
const handleWheel = React.useCallback(
|
|
1063
|
+
(event: WheelEvent | TouchEvent) => {
|
|
1064
|
+
const pinching = event.ctrlKey
|
|
1012
1065
|
|
|
1013
|
-
|
|
1014
|
-
return typeof event.deltaY === 'number'
|
|
1015
|
-
}
|
|
1066
|
+
const currentTarget = event.currentTarget as HTMLElement
|
|
1016
1067
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1068
|
+
function isWheelEvent(event: any): event is WheelEvent {
|
|
1069
|
+
return typeof event.deltaY === 'number'
|
|
1070
|
+
}
|
|
1020
1071
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
((visualViewport?.height ?? 0) - WINDOW_PADDING * 2)
|
|
1025
|
-
) > 1 &&
|
|
1026
|
-
!pinching
|
|
1027
|
-
) {
|
|
1028
|
-
event.preventDefault()
|
|
1029
|
-
} else if (isWheelEvent(event) && isFirefox) {
|
|
1030
|
-
// Firefox needs this to propagate scrolling
|
|
1031
|
-
// during momentum scrolling phase if the
|
|
1032
|
-
// height reached its maximum (at boundaries)
|
|
1033
|
-
currentTarget.scrollTop += event.deltaY
|
|
1034
|
-
}
|
|
1072
|
+
function isTouchEvent(event: any): event is TouchEvent {
|
|
1073
|
+
return event.touches != null
|
|
1074
|
+
}
|
|
1035
1075
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1076
|
+
if (
|
|
1077
|
+
Math.abs(
|
|
1078
|
+
(currentTarget?.offsetHeight ?? 0) - ((visualViewport?.height ?? 0) - WINDOW_PADDING * 2)
|
|
1079
|
+
) > 1 &&
|
|
1080
|
+
!pinching
|
|
1081
|
+
) {
|
|
1082
|
+
event.preventDefault()
|
|
1083
|
+
} else if (isWheelEvent(event) && isFirefox) {
|
|
1084
|
+
// Firefox needs this to propagate scrolling
|
|
1085
|
+
// during momentum scrolling phase if the
|
|
1086
|
+
// height reached its maximum (at boundaries)
|
|
1087
|
+
currentTarget.scrollTop += event.deltaY
|
|
1088
|
+
}
|
|
1038
1089
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
const pageY = event.touches[0]?.pageY
|
|
1090
|
+
if (!pinching) {
|
|
1091
|
+
let delta = 5
|
|
1042
1092
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1093
|
+
if (isTouchEvent(event)) {
|
|
1094
|
+
const currentPageY = touchPageYRef.current
|
|
1095
|
+
const pageY = event.touches[0]?.pageY
|
|
1045
1096
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1097
|
+
if (pageY != null) {
|
|
1098
|
+
touchPageYRef.current = pageY
|
|
1099
|
+
|
|
1100
|
+
if (currentPageY != null) {
|
|
1101
|
+
delta = currentPageY - pageY
|
|
1049
1102
|
}
|
|
1050
1103
|
}
|
|
1051
|
-
|
|
1052
|
-
increaseHeight(currentTarget, isWheelEvent(event) ? event.deltaY : delta)
|
|
1053
|
-
setScrollTop(currentTarget.scrollTop)
|
|
1054
|
-
// Ensure derived data (scroll arrows) is fresh
|
|
1055
|
-
forceUpdate()
|
|
1056
1104
|
}
|
|
1057
|
-
},
|
|
1058
|
-
[increaseHeight, forceUpdate]
|
|
1059
|
-
)
|
|
1060
1105
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
touchPageYRef.current = null
|
|
1106
|
+
increaseHeight(currentTarget, isWheelEvent(event) ? event.deltaY : delta)
|
|
1107
|
+
setScrollTop(currentTarget.scrollTop)
|
|
1108
|
+
// Ensure derived data (scroll arrows) is fresh
|
|
1109
|
+
forceUpdate()
|
|
1066
1110
|
}
|
|
1111
|
+
},
|
|
1112
|
+
[forceUpdate, increaseHeight]
|
|
1113
|
+
)
|
|
1067
1114
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
floating.removeEventListener('wheel', handleWheel)
|
|
1075
|
-
floating.removeEventListener('touchmove', handleWheel)
|
|
1076
|
-
floating.removeEventListener('touchend', onTouchEnd)
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
}, [open, floatingRef, handleWheel, middlewareType])
|
|
1115
|
+
// Handle `onWheel` event in an effect to remove the `passive` option so we
|
|
1116
|
+
// can .preventDefault() it
|
|
1117
|
+
React.useEffect(() => {
|
|
1118
|
+
function onTouchEnd() {
|
|
1119
|
+
touchPageYRef.current = null
|
|
1120
|
+
}
|
|
1080
1121
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1122
|
+
const floating = floatingRef.current
|
|
1123
|
+
if (open && floating && middlewareType === 'align') {
|
|
1124
|
+
floating.addEventListener('wheel', handleWheel)
|
|
1125
|
+
floating.addEventListener('touchmove', handleWheel)
|
|
1126
|
+
floating.addEventListener('touchend', onTouchEnd, { passive: true })
|
|
1084
1127
|
return () => {
|
|
1085
|
-
|
|
1128
|
+
floating.removeEventListener('wheel', handleWheel)
|
|
1129
|
+
floating.removeEventListener('touchmove', handleWheel)
|
|
1130
|
+
floating.removeEventListener('touchend', onTouchEnd)
|
|
1086
1131
|
}
|
|
1087
|
-
}
|
|
1132
|
+
}
|
|
1133
|
+
}, [open, floatingRef, handleWheel, middlewareType])
|
|
1088
1134
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1135
|
+
// Ensure the menu remains attached to the reference element when resizing.
|
|
1136
|
+
React.useEffect(() => {
|
|
1137
|
+
window.addEventListener('resize', update)
|
|
1138
|
+
return () => {
|
|
1139
|
+
window.removeEventListener('resize', update)
|
|
1140
|
+
}
|
|
1141
|
+
}, [update])
|
|
1093
1142
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
: selectedIndex != null
|
|
1099
|
-
? listItemsRef.current[selectedIndex]
|
|
1100
|
-
: null
|
|
1143
|
+
// Scroll the active or selected item into view when in `controlledScrolling`
|
|
1144
|
+
// mode (i.e. arrow key nav).
|
|
1145
|
+
React.useLayoutEffect(() => {
|
|
1146
|
+
const floating = floatingRef.current
|
|
1101
1147
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1148
|
+
if (open && controlledScrolling && floating) {
|
|
1149
|
+
const item =
|
|
1150
|
+
activeIndex != null
|
|
1151
|
+
? listItemsRef.current[activeIndex]
|
|
1152
|
+
: selectedIndex != null
|
|
1153
|
+
? listItemsRef.current[selectedIndex]
|
|
1154
|
+
: null
|
|
1104
1155
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
const bottom = top + itemHeight
|
|
1156
|
+
if (item && prevActiveIndex != null) {
|
|
1157
|
+
const itemHeight = listItemsRef.current[prevActiveIndex]?.offsetHeight ?? 0
|
|
1108
1158
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1159
|
+
const floatingHeight = floating.offsetHeight
|
|
1160
|
+
const top = item.offsetTop
|
|
1161
|
+
const bottom = top + itemHeight
|
|
1112
1162
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
} else if (bottom > floatingHeight + floating.scrollTop - 20) {
|
|
1117
|
-
const diff = bottom - floatingHeight - floating.scrollTop + 20
|
|
1163
|
+
if (top < floating.scrollTop + 20) {
|
|
1164
|
+
const diff = floating.scrollTop - top + 20
|
|
1165
|
+
floating.scrollTop -= diff
|
|
1118
1166
|
|
|
1119
|
-
|
|
1167
|
+
if (activeIndex != selectedIndex && activeIndex != null) {
|
|
1168
|
+
increaseHeight(floating, -diff)
|
|
1169
|
+
}
|
|
1170
|
+
} else if (bottom > floatingHeight + floating.scrollTop - 20) {
|
|
1171
|
+
const diff = bottom - floatingHeight - floating.scrollTop + 20
|
|
1120
1172
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1173
|
+
floating.scrollTop += diff
|
|
1174
|
+
|
|
1175
|
+
if (activeIndex != selectedIndex && activeIndex != null) {
|
|
1176
|
+
floating.scrollTop -= increaseHeight(floating, diff) ?? 0
|
|
1124
1177
|
}
|
|
1125
1178
|
}
|
|
1126
1179
|
}
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1180
|
+
}
|
|
1181
|
+
}, [
|
|
1182
|
+
open,
|
|
1183
|
+
controlledScrolling,
|
|
1184
|
+
prevActiveIndex,
|
|
1185
|
+
activeIndex,
|
|
1186
|
+
selectedIndex,
|
|
1187
|
+
floatingRef,
|
|
1188
|
+
increaseHeight,
|
|
1189
|
+
])
|
|
1190
|
+
|
|
1191
|
+
// Sync the height and the scrollTop values and device whether to use fallback
|
|
1192
|
+
// positioning.
|
|
1193
|
+
React.useLayoutEffect(() => {
|
|
1194
|
+
const floating = refs.floating.current
|
|
1195
|
+
const reference = refs.reference.current
|
|
1142
1196
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1197
|
+
if (open && floating && reference && floating.offsetHeight < floating.scrollHeight) {
|
|
1198
|
+
const referenceRect = reference.getBoundingClientRect()
|
|
1145
1199
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
}
|
|
1151
|
-
return
|
|
1200
|
+
if (middlewareType === 'fallback') {
|
|
1201
|
+
const item = listItemsRef.current[selectedIndex]
|
|
1202
|
+
if (item) {
|
|
1203
|
+
floating.scrollTop = item.offsetTop - floating.clientHeight + referenceRect.height
|
|
1152
1204
|
}
|
|
1205
|
+
return
|
|
1206
|
+
}
|
|
1153
1207
|
|
|
1154
|
-
|
|
1208
|
+
floating.scrollTop = middlewareData.size?.y
|
|
1155
1209
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1210
|
+
const closeToBottom =
|
|
1211
|
+
(visualViewport?.height ?? 0) + getVisualOffsetTop() - referenceRect.bottom <
|
|
1212
|
+
FALLBACK_THRESHOLD
|
|
1213
|
+
const closeToTop = referenceRect.top < FALLBACK_THRESHOLD
|
|
1160
1214
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
}
|
|
1215
|
+
if (floating.offsetHeight < MIN_HEIGHT || closeToTop || closeToBottom) {
|
|
1216
|
+
setMiddlewareType('fallback')
|
|
1164
1217
|
}
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1218
|
+
}
|
|
1219
|
+
}, [
|
|
1220
|
+
open,
|
|
1221
|
+
increaseHeight,
|
|
1222
|
+
selectedIndex,
|
|
1223
|
+
middlewareType,
|
|
1224
|
+
refs.floating,
|
|
1225
|
+
refs.reference,
|
|
1226
|
+
// Always re-run this effect when the position has been computed so the
|
|
1227
|
+
// .scrollTop change works with fresh sizing.
|
|
1228
|
+
middlewareData,
|
|
1229
|
+
])
|
|
1176
1230
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1231
|
+
React.useLayoutEffect(() => {
|
|
1232
|
+
if (open && selectedIndex != null) {
|
|
1233
|
+
requestAnimationFrame(() => {
|
|
1234
|
+
listItemsRef.current[selectedIndex]?.focus({ preventScroll: true })
|
|
1235
|
+
})
|
|
1236
|
+
}
|
|
1237
|
+
}, [listItemsRef, selectedIndex, open])
|
|
1238
|
+
|
|
1239
|
+
// Wait for scroll position to settle before showing arrows to prevent
|
|
1240
|
+
// interference with pointer events.
|
|
1241
|
+
React.useEffect(() => {
|
|
1242
|
+
const frame = requestAnimationFrame(() => {
|
|
1243
|
+
setShowArrows(open)
|
|
1244
|
+
|
|
1245
|
+
if (!open) {
|
|
1246
|
+
setScrollTop(0)
|
|
1247
|
+
setMiddlewareType('align')
|
|
1248
|
+
setActiveIndex(null)
|
|
1249
|
+
setControlledScrolling(false)
|
|
1182
1250
|
}
|
|
1183
|
-
}
|
|
1251
|
+
})
|
|
1252
|
+
return () => cancelAnimationFrame(frame)
|
|
1253
|
+
}, [open])
|
|
1184
1254
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
setShowArrows(open)
|
|
1190
|
-
|
|
1191
|
-
if (!open) {
|
|
1192
|
-
setScrollTop(0)
|
|
1193
|
-
setMiddlewareType('align')
|
|
1194
|
-
setActiveIndex(null)
|
|
1195
|
-
setControlledScrolling(false)
|
|
1196
|
-
}
|
|
1197
|
-
})
|
|
1198
|
-
return () => cancelAnimationFrame(frame)
|
|
1199
|
-
}, [open])
|
|
1255
|
+
// We set this to true by default so that events bubble to forms without JS (SSR)
|
|
1256
|
+
// const isFormControl = trigger ? Boolean(trigger.closest('form')) : true
|
|
1257
|
+
// const [bubbleSelect, setBubbleSelect] = React.useState<HTMLSelectElement | null>(null)
|
|
1258
|
+
// const triggerPointerDownPosRef = React.useRef<{ x: number; y: number } | null>(null)
|
|
1200
1259
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1260
|
+
return (
|
|
1261
|
+
<SelectProvider
|
|
1262
|
+
scope={__scopeSelect}
|
|
1263
|
+
{...(selectContext as Required<typeof selectContext>)}
|
|
1264
|
+
increaseHeight={increaseHeight}
|
|
1265
|
+
floatingRef={floatingRef}
|
|
1266
|
+
setValueAtIndex={(index, value) => {
|
|
1267
|
+
listContentRef.current[index] = value
|
|
1268
|
+
}}
|
|
1269
|
+
interactions={{
|
|
1270
|
+
...interactions,
|
|
1271
|
+
getReferenceProps() {
|
|
1272
|
+
return interactions.getReferenceProps({
|
|
1273
|
+
ref: reference,
|
|
1274
|
+
className: 'SelectTrigger',
|
|
1275
|
+
onKeyDown(event) {
|
|
1276
|
+
if (event.key === 'Enter' || (event.key === ' ' && !context.dataRef.current.typing)) {
|
|
1277
|
+
event.preventDefault()
|
|
1278
|
+
setOpen(true)
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
})
|
|
1282
|
+
},
|
|
1283
|
+
getFloatingProps(props) {
|
|
1284
|
+
return interactions.getFloatingProps({
|
|
1285
|
+
ref: floating,
|
|
1286
|
+
className: 'Select',
|
|
1287
|
+
...props,
|
|
1288
|
+
style: {
|
|
1289
|
+
position: strategy,
|
|
1290
|
+
top: y ?? '',
|
|
1291
|
+
left: x ?? '',
|
|
1292
|
+
outline: 0,
|
|
1293
|
+
listStyleType: 'none',
|
|
1294
|
+
scrollbarWidth: 'none',
|
|
1295
|
+
userSelect: 'none',
|
|
1296
|
+
...props?.style,
|
|
1297
|
+
},
|
|
1298
|
+
onPointerEnter() {
|
|
1299
|
+
setControlledScrolling(false)
|
|
1300
|
+
},
|
|
1301
|
+
onPointerMove() {
|
|
1302
|
+
setControlledScrolling(false)
|
|
1303
|
+
},
|
|
1304
|
+
onKeyDown() {
|
|
1305
|
+
setControlledScrolling(true)
|
|
1306
|
+
},
|
|
1307
|
+
onScroll(event) {
|
|
1308
|
+
setScrollTop(event.currentTarget.scrollTop)
|
|
1309
|
+
},
|
|
1310
|
+
})
|
|
1311
|
+
},
|
|
1312
|
+
}}
|
|
1313
|
+
floatingContext={context}
|
|
1314
|
+
activeIndex={activeIndex}
|
|
1315
|
+
canScrollDown={!!showDownArrow}
|
|
1316
|
+
canScrollUp={!!showUpArrow}
|
|
1317
|
+
controlledScrolling
|
|
1318
|
+
dataRef={context.dataRef}
|
|
1319
|
+
listRef={listItemsRef}
|
|
1320
|
+
>
|
|
1321
|
+
{children}
|
|
1322
|
+
{/* {isFormControl ? (
|
|
1323
|
+
<BubbleSelect
|
|
1324
|
+
ref={setBubbleSelect}
|
|
1325
|
+
aria-hidden
|
|
1326
|
+
tabIndex={-1}
|
|
1327
|
+
name={name}
|
|
1328
|
+
autoComplete={autoComplete}
|
|
1329
|
+
value={value}
|
|
1330
|
+
// enable form autofill
|
|
1331
|
+
onChange={(event) => setValue(event.target.value)}
|
|
1332
|
+
/>
|
|
1333
|
+
) : null} */}
|
|
1334
|
+
</SelectProvider>
|
|
1335
|
+
)
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
const SelectSheetImpl = (props: SelectImplProps) => {
|
|
1339
|
+
return <>{props.children}</>
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
export const Select = withStaticProperties(
|
|
1343
|
+
(props: ScopedProps<SelectProps>) => {
|
|
1344
|
+
const {
|
|
1345
|
+
__scopeSelect,
|
|
1346
|
+
children,
|
|
1347
|
+
open: openProp,
|
|
1348
|
+
defaultOpen,
|
|
1349
|
+
onOpenChange,
|
|
1350
|
+
value: valueProp,
|
|
1351
|
+
defaultValue,
|
|
1352
|
+
onValueChange,
|
|
1353
|
+
size: sizeProp,
|
|
1354
|
+
sheetBreakpoint = false,
|
|
1355
|
+
dir,
|
|
1356
|
+
} = props
|
|
1357
|
+
|
|
1358
|
+
const isSheet = useSelectBreakpointActive(sheetBreakpoint)
|
|
1359
|
+
const SelectImpl = isSheet ? SelectSheetImpl : SelectInlineImpl
|
|
1360
|
+
const forceUpdate = React.useReducer(() => ({}), {})[1]
|
|
1361
|
+
|
|
1362
|
+
const [open, setOpen] = useControllableState({
|
|
1363
|
+
prop: openProp,
|
|
1364
|
+
defaultProp: defaultOpen || false,
|
|
1365
|
+
onChange: onOpenChange,
|
|
1366
|
+
})
|
|
1367
|
+
|
|
1368
|
+
const [value, setValue] = useControllableState({
|
|
1369
|
+
prop: valueProp,
|
|
1370
|
+
defaultProp: defaultValue || '',
|
|
1371
|
+
onChange: onValueChange,
|
|
1372
|
+
})
|
|
1373
|
+
|
|
1374
|
+
const [activeIndex, setActiveIndex] = React.useState<number | null>(null)
|
|
1375
|
+
const selectedIndexRef = React.useRef<number | null>(null)
|
|
1376
|
+
const activeIndexRef = React.useRef<number | null>(null)
|
|
1377
|
+
|
|
1378
|
+
const listContentRef = React.useRef<string[]>([])
|
|
1379
|
+
|
|
1380
|
+
const [selectedIndex, setSelectedIndex] = React.useState(
|
|
1381
|
+
Math.max(0, listContentRef.current.indexOf(value))
|
|
1382
|
+
)
|
|
1205
1383
|
|
|
1206
1384
|
const [valueNode, setValueNode] = React.useState<HTMLElement | null>(null)
|
|
1207
1385
|
const [valueNodeHasChildren, setValueNodeHasChildren] = React.useState(false)
|
|
1208
1386
|
|
|
1387
|
+
useIsomorphicLayoutEffect(() => {
|
|
1388
|
+
selectedIndexRef.current = selectedIndex
|
|
1389
|
+
activeIndexRef.current = activeIndex
|
|
1390
|
+
})
|
|
1391
|
+
|
|
1209
1392
|
return (
|
|
1210
1393
|
<SelectProvider
|
|
1394
|
+
dir={dir}
|
|
1211
1395
|
size={sizeProp}
|
|
1212
|
-
scope={__scopeSelect}
|
|
1213
|
-
increaseHeight={increaseHeight}
|
|
1214
1396
|
forceUpdate={forceUpdate}
|
|
1215
|
-
floatingRef={floatingRef}
|
|
1216
1397
|
valueNode={valueNode}
|
|
1217
1398
|
onValueNodeChange={setValueNode}
|
|
1218
|
-
valueNodeHasChildren={valueNodeHasChildren}
|
|
1219
1399
|
onValueNodeHasChildrenChange={setValueNodeHasChildren}
|
|
1400
|
+
valueNodeHasChildren={valueNodeHasChildren}
|
|
1401
|
+
scopeKey={__scopeSelect ? Object.keys(__scopeSelect)[0] : ''}
|
|
1402
|
+
sheetBreakpoint={sheetBreakpoint}
|
|
1403
|
+
scope={__scopeSelect}
|
|
1220
1404
|
setValueAtIndex={(index, value) => {
|
|
1221
1405
|
listContentRef.current[index] = value
|
|
1222
1406
|
}}
|
|
1223
|
-
interactions={{
|
|
1224
|
-
...interactions,
|
|
1225
|
-
getReferenceProps() {
|
|
1226
|
-
return interactions.getReferenceProps({
|
|
1227
|
-
ref: reference,
|
|
1228
|
-
className: 'SelectTrigger',
|
|
1229
|
-
onKeyDown(event) {
|
|
1230
|
-
if (
|
|
1231
|
-
event.key === 'Enter' ||
|
|
1232
|
-
(event.key === ' ' && !context.dataRef.current.typing)
|
|
1233
|
-
) {
|
|
1234
|
-
event.preventDefault()
|
|
1235
|
-
setOpen(true)
|
|
1236
|
-
}
|
|
1237
|
-
},
|
|
1238
|
-
})
|
|
1239
|
-
},
|
|
1240
|
-
getFloatingProps(props) {
|
|
1241
|
-
return interactions.getFloatingProps({
|
|
1242
|
-
ref: floating,
|
|
1243
|
-
className: 'Select',
|
|
1244
|
-
...props,
|
|
1245
|
-
style: {
|
|
1246
|
-
position: strategy,
|
|
1247
|
-
top: y ?? '',
|
|
1248
|
-
left: x ?? '',
|
|
1249
|
-
outline: 0,
|
|
1250
|
-
listStyleType: 'none',
|
|
1251
|
-
scrollbarWidth: 'none',
|
|
1252
|
-
userSelect: 'none',
|
|
1253
|
-
...props?.style,
|
|
1254
|
-
},
|
|
1255
|
-
onPointerEnter() {
|
|
1256
|
-
setControlledScrolling(false)
|
|
1257
|
-
},
|
|
1258
|
-
onPointerMove() {
|
|
1259
|
-
setControlledScrolling(false)
|
|
1260
|
-
},
|
|
1261
|
-
onKeyDown() {
|
|
1262
|
-
setControlledScrolling(true)
|
|
1263
|
-
},
|
|
1264
|
-
onScroll(event) {
|
|
1265
|
-
setScrollTop(event.currentTarget.scrollTop)
|
|
1266
|
-
},
|
|
1267
|
-
})
|
|
1268
|
-
},
|
|
1269
|
-
}}
|
|
1270
|
-
floatingContext={context}
|
|
1271
1407
|
activeIndex={activeIndex}
|
|
1272
|
-
canScrollDown={!!showDownArrow}
|
|
1273
|
-
canScrollUp={!!showUpArrow}
|
|
1274
|
-
controlledScrolling
|
|
1275
|
-
dataRef={context.dataRef}
|
|
1276
|
-
listRef={listItemsRef}
|
|
1277
1408
|
onChange={setValue}
|
|
1278
1409
|
selectedIndex={selectedIndex}
|
|
1279
1410
|
setActiveIndex={setActiveIndex}
|
|
@@ -1282,19 +1413,18 @@ export const Select = withStaticProperties(
|
|
|
1282
1413
|
value={value}
|
|
1283
1414
|
open={open}
|
|
1284
1415
|
>
|
|
1285
|
-
{
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
) : null} */}
|
|
1416
|
+
<SelectSheetController onChangeOpen={setOpen} __scopeSelect={__scopeSelect}>
|
|
1417
|
+
<SelectImpl
|
|
1418
|
+
activeIndexRef={activeIndexRef}
|
|
1419
|
+
listContentRef={listContentRef}
|
|
1420
|
+
selectedIndexRef={selectedIndexRef}
|
|
1421
|
+
{...props}
|
|
1422
|
+
open={open}
|
|
1423
|
+
value={value}
|
|
1424
|
+
>
|
|
1425
|
+
{children}
|
|
1426
|
+
</SelectImpl>
|
|
1427
|
+
</SelectSheetController>
|
|
1298
1428
|
</SelectProvider>
|
|
1299
1429
|
)
|
|
1300
1430
|
},
|
|
@@ -1311,6 +1441,8 @@ export const Select = withStaticProperties(
|
|
|
1311
1441
|
Trigger: SelectTrigger,
|
|
1312
1442
|
Value: SelectValue,
|
|
1313
1443
|
Viewport: SelectViewport,
|
|
1444
|
+
SheetContents: SelectSheetContents,
|
|
1445
|
+
Sheet,
|
|
1314
1446
|
}
|
|
1315
1447
|
)
|
|
1316
1448
|
|