@tamagui/select 2.7.7 → 3.0.0-beta.1091.1
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.cjs +406 -467
- package/dist/cjs/Select.native.cjs +495 -0
- package/dist/cjs/Select.native.js +445 -492
- package/dist/cjs/Select.native.js.map +1 -1
- package/dist/cjs/SelectContent.cjs +79 -79
- package/dist/cjs/SelectContent.native.cjs +29 -0
- package/dist/cjs/SelectContent.native.js +17 -22
- package/dist/cjs/SelectContent.native.js.map +1 -1
- package/dist/cjs/SelectImpl.cjs +305 -320
- package/dist/cjs/SelectImpl.native.cjs +29 -0
- package/dist/cjs/SelectImpl.native.js +16 -24
- package/dist/cjs/SelectImpl.native.js.map +1 -1
- package/dist/cjs/SelectItem.cjs +256 -205
- package/dist/cjs/SelectItem.native.cjs +288 -0
- package/dist/cjs/SelectItem.native.js +264 -213
- package/dist/cjs/SelectItem.native.js.map +1 -1
- package/dist/cjs/SelectItemText.cjs +48 -79
- package/dist/cjs/SelectItemText.native.cjs +73 -0
- package/dist/cjs/SelectItemText.native.js +49 -79
- package/dist/cjs/SelectItemText.native.js.map +1 -1
- package/dist/cjs/SelectScrollButton.cjs +110 -130
- package/dist/cjs/SelectScrollButton.native.cjs +37 -0
- package/dist/cjs/SelectScrollButton.native.js +24 -22
- package/dist/cjs/SelectScrollButton.native.js.map +1 -1
- package/dist/cjs/SelectTrigger.cjs +68 -84
- package/dist/cjs/SelectTrigger.native.cjs +90 -0
- package/dist/cjs/SelectTrigger.native.js +70 -66
- package/dist/cjs/SelectTrigger.native.js.map +1 -1
- package/dist/cjs/SelectViewport.cjs +121 -142
- package/dist/cjs/SelectViewport.native.cjs +72 -0
- package/dist/cjs/SelectViewport.native.js +53 -50
- package/dist/cjs/SelectViewport.native.js.map +1 -1
- package/dist/cjs/constants.cjs +20 -22
- package/dist/cjs/constants.native.cjs +40 -0
- package/dist/cjs/constants.native.js +20 -21
- package/dist/cjs/constants.native.js.map +1 -1
- package/dist/cjs/context.cjs +37 -48
- package/dist/cjs/context.native.cjs +57 -0
- package/dist/cjs/context.native.js +39 -49
- package/dist/cjs/context.native.js.map +1 -1
- package/dist/cjs/index.cjs +14 -11
- package/dist/cjs/index.native.cjs +26 -0
- package/dist/cjs/index.native.js +14 -10
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/selectionController.cjs +343 -0
- package/dist/cjs/selectionController.native.cjs +407 -0
- package/dist/cjs/selectionController.native.js +409 -0
- package/dist/cjs/selectionController.native.js.map +1 -0
- package/dist/cjs/types.cjs +9 -11
- package/dist/cjs/types.native.cjs +18 -0
- package/dist/cjs/types.native.js +9 -10
- package/dist/cjs/types.native.js.map +1 -1
- package/dist/esm/Select.mjs +381 -443
- package/dist/esm/Select.mjs.map +1 -1
- package/dist/esm/Select.native.js +420 -469
- package/dist/esm/Select.native.js.map +1 -1
- package/dist/esm/SelectContent.mjs +67 -61
- package/dist/esm/SelectContent.mjs.map +1 -1
- package/dist/esm/SelectContent.native.js +4 -5
- package/dist/esm/SelectContent.native.js.map +1 -1
- package/dist/esm/SelectImpl.mjs +291 -295
- package/dist/esm/SelectImpl.mjs.map +1 -1
- package/dist/esm/SelectImpl.native.js +5 -8
- package/dist/esm/SelectImpl.native.js.map +1 -1
- package/dist/esm/SelectItem.mjs +240 -181
- package/dist/esm/SelectItem.mjs.map +1 -1
- package/dist/esm/SelectItem.native.js +249 -191
- package/dist/esm/SelectItem.native.js.map +1 -1
- package/dist/esm/SelectItemText.mjs +33 -55
- package/dist/esm/SelectItemText.mjs.map +1 -1
- package/dist/esm/SelectItemText.native.js +34 -56
- package/dist/esm/SelectItemText.native.js.map +1 -1
- package/dist/esm/SelectScrollButton.mjs +95 -107
- package/dist/esm/SelectScrollButton.mjs.map +1 -1
- package/dist/esm/SelectScrollButton.native.js +11 -7
- package/dist/esm/SelectScrollButton.native.js.map +1 -1
- package/dist/esm/SelectTrigger.mjs +56 -58
- package/dist/esm/SelectTrigger.mjs.map +1 -1
- package/dist/esm/SelectTrigger.native.js +54 -44
- package/dist/esm/SelectTrigger.native.js.map +1 -1
- package/dist/esm/SelectViewport.mjs +107 -119
- package/dist/esm/SelectViewport.mjs.map +1 -1
- package/dist/esm/SelectViewport.native.js +44 -39
- package/dist/esm/SelectViewport.native.js.map +1 -1
- package/dist/esm/constants.mjs +1 -0
- package/dist/esm/constants.mjs.map +1 -1
- package/dist/esm/constants.native.js +1 -0
- package/dist/esm/constants.native.js.map +1 -1
- package/dist/esm/context.mjs +20 -28
- package/dist/esm/context.mjs.map +1 -1
- package/dist/esm/context.native.js +23 -31
- package/dist/esm/context.native.js.map +1 -1
- package/dist/esm/index.js +13 -3
- package/dist/esm/index.mjs +13 -3
- package/dist/esm/index.native.js +13 -3
- package/dist/esm/selectionController.mjs +315 -0
- package/dist/esm/selectionController.mjs.map +1 -0
- package/dist/esm/selectionController.native.js +377 -0
- package/dist/esm/selectionController.native.js.map +1 -0
- package/dist/esm/types.mjs +0 -2
- package/dist/esm/types.native.js +0 -2
- package/dist/jsx/Select.mjs +381 -443
- package/dist/jsx/Select.mjs.map +1 -1
- package/dist/jsx/Select.native.cjs +495 -0
- package/dist/jsx/Select.native.js +445 -492
- package/dist/jsx/Select.native.js.map +1 -1
- package/dist/jsx/SelectContent.mjs +67 -61
- package/dist/jsx/SelectContent.mjs.map +1 -1
- package/dist/jsx/SelectContent.native.cjs +29 -0
- package/dist/jsx/SelectContent.native.js +17 -22
- package/dist/jsx/SelectContent.native.js.map +1 -1
- package/dist/jsx/SelectImpl.mjs +291 -295
- package/dist/jsx/SelectImpl.mjs.map +1 -1
- package/dist/jsx/SelectImpl.native.cjs +29 -0
- package/dist/jsx/SelectImpl.native.js +16 -24
- package/dist/jsx/SelectImpl.native.js.map +1 -1
- package/dist/jsx/SelectItem.mjs +240 -181
- package/dist/jsx/SelectItem.mjs.map +1 -1
- package/dist/jsx/SelectItem.native.cjs +288 -0
- package/dist/jsx/SelectItem.native.js +264 -213
- package/dist/jsx/SelectItem.native.js.map +1 -1
- package/dist/jsx/SelectItemText.mjs +33 -55
- package/dist/jsx/SelectItemText.mjs.map +1 -1
- package/dist/jsx/SelectItemText.native.cjs +73 -0
- package/dist/jsx/SelectItemText.native.js +49 -79
- package/dist/jsx/SelectItemText.native.js.map +1 -1
- package/dist/jsx/SelectScrollButton.mjs +95 -107
- package/dist/jsx/SelectScrollButton.mjs.map +1 -1
- package/dist/jsx/SelectScrollButton.native.cjs +37 -0
- package/dist/jsx/SelectScrollButton.native.js +24 -22
- package/dist/jsx/SelectScrollButton.native.js.map +1 -1
- package/dist/jsx/SelectTrigger.mjs +56 -58
- package/dist/jsx/SelectTrigger.mjs.map +1 -1
- package/dist/jsx/SelectTrigger.native.cjs +90 -0
- package/dist/jsx/SelectTrigger.native.js +70 -66
- package/dist/jsx/SelectTrigger.native.js.map +1 -1
- package/dist/jsx/SelectViewport.mjs +107 -119
- package/dist/jsx/SelectViewport.mjs.map +1 -1
- package/dist/jsx/SelectViewport.native.cjs +72 -0
- package/dist/jsx/SelectViewport.native.js +53 -50
- package/dist/jsx/SelectViewport.native.js.map +1 -1
- package/dist/jsx/constants.mjs +1 -0
- package/dist/jsx/constants.mjs.map +1 -1
- package/dist/jsx/constants.native.cjs +40 -0
- package/dist/jsx/constants.native.js +20 -21
- package/dist/jsx/constants.native.js.map +1 -1
- package/dist/jsx/context.mjs +20 -28
- package/dist/jsx/context.mjs.map +1 -1
- package/dist/jsx/context.native.cjs +57 -0
- package/dist/jsx/context.native.js +39 -49
- package/dist/jsx/context.native.js.map +1 -1
- package/dist/jsx/index.js +13 -3
- package/dist/jsx/index.mjs +13 -3
- package/dist/jsx/index.native.cjs +26 -0
- package/dist/jsx/index.native.js +14 -10
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/selectionController.mjs +315 -0
- package/dist/jsx/selectionController.mjs.map +1 -0
- package/dist/jsx/selectionController.native.cjs +407 -0
- package/dist/jsx/selectionController.native.js +409 -0
- package/dist/jsx/selectionController.native.js.map +1 -0
- package/dist/jsx/types.mjs +0 -2
- package/dist/jsx/types.native.cjs +18 -0
- package/dist/jsx/types.native.js +9 -10
- package/dist/jsx/types.native.js.map +1 -1
- package/package.json +31 -30
- package/src/Select.tsx +478 -378
- package/src/SelectContent.tsx +46 -16
- package/src/SelectImpl.native.tsx +0 -8
- package/src/SelectImpl.tsx +149 -160
- package/src/SelectItem.tsx +280 -159
- package/src/SelectItemText.tsx +16 -33
- package/src/SelectScrollButton.native.tsx +13 -4
- package/src/SelectScrollButton.tsx +57 -42
- package/src/SelectTrigger.tsx +77 -53
- package/src/SelectViewport.native.tsx +42 -25
- package/src/SelectViewport.tsx +60 -60
- package/src/context.tsx +6 -2
- package/src/index.tsx +5 -2
- package/src/selectionController.ts +404 -0
- package/src/types.tsx +105 -59
- package/types/Select.d.ts +139 -201
- package/types/Select.d.ts.map +1 -1
- package/types/SelectContent.d.ts +1 -1
- package/types/SelectContent.d.ts.map +1 -1
- package/types/SelectContent.native.d.ts.map +1 -1
- package/types/SelectImpl.d.ts +2 -1
- package/types/SelectImpl.d.ts.map +1 -1
- package/types/SelectImpl.native.d.ts +1 -5
- package/types/SelectImpl.native.d.ts.map +1 -1
- package/types/SelectItem.d.ts +13 -16
- package/types/SelectItem.d.ts.map +1 -1
- package/types/SelectItemText.d.ts +9 -13
- package/types/SelectItemText.d.ts.map +1 -1
- package/types/SelectScrollButton.d.ts +15 -2
- package/types/SelectScrollButton.d.ts.map +1 -1
- package/types/SelectScrollButton.native.d.ts +16 -3
- package/types/SelectScrollButton.native.d.ts.map +1 -1
- package/types/SelectTrigger.d.ts +11 -23
- package/types/SelectTrigger.d.ts.map +1 -1
- package/types/SelectViewport.d.ts +11 -38
- package/types/SelectViewport.d.ts.map +1 -1
- package/types/SelectViewport.native.d.ts +15 -4
- package/types/SelectViewport.native.d.ts.map +1 -1
- package/types/context.d.ts +2 -0
- package/types/context.d.ts.map +1 -1
- package/types/index.d.ts +5 -2
- package/types/index.d.ts.map +1 -1
- package/types/selectionController.d.ts +90 -0
- package/types/selectionController.d.ts.map +1 -0
- package/types/types.d.ts +66 -50
- package/types/types.d.ts.map +1 -1
- package/dist/cjs/BubbleSelect.cjs +0 -50
- package/dist/cjs/BubbleSelect.native.js +0 -30
- package/dist/cjs/BubbleSelect.native.js.map +0 -1
- package/dist/cjs/useSelectBreakpointActive.cjs +0 -32
- package/dist/cjs/useSelectBreakpointActive.native.js +0 -35
- package/dist/cjs/useSelectBreakpointActive.native.js.map +0 -1
- package/dist/esm/BubbleSelect.mjs +0 -27
- package/dist/esm/BubbleSelect.mjs.map +0 -1
- package/dist/esm/BubbleSelect.native.js +0 -4
- package/dist/esm/BubbleSelect.native.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/index.mjs.map +0 -1
- package/dist/esm/index.native.js.map +0 -1
- package/dist/esm/types.mjs.map +0 -1
- package/dist/esm/types.native.js.map +0 -1
- package/dist/esm/useSelectBreakpointActive.mjs +0 -7
- package/dist/esm/useSelectBreakpointActive.mjs.map +0 -1
- package/dist/esm/useSelectBreakpointActive.native.js +0 -7
- package/dist/esm/useSelectBreakpointActive.native.js.map +0 -1
- package/dist/jsx/BubbleSelect.mjs +0 -27
- package/dist/jsx/BubbleSelect.mjs.map +0 -1
- package/dist/jsx/BubbleSelect.native.js +0 -30
- package/dist/jsx/BubbleSelect.native.js.map +0 -1
- package/dist/jsx/index.js.map +0 -1
- package/dist/jsx/index.mjs.map +0 -1
- package/dist/jsx/types.mjs.map +0 -1
- package/dist/jsx/useSelectBreakpointActive.mjs +0 -7
- package/dist/jsx/useSelectBreakpointActive.mjs.map +0 -1
- package/dist/jsx/useSelectBreakpointActive.native.js +0 -35
- package/dist/jsx/useSelectBreakpointActive.native.js.map +0 -1
- package/src/BubbleSelect.tsx +0 -50
- package/src/useSelectBreakpointActive.tsx +0 -7
- package/types/BubbleSelect.d.ts +0 -2
- package/types/BubbleSelect.d.ts.map +0 -1
- package/types/useSelectBreakpointActive.d.ts +0 -3
- package/types/useSelectBreakpointActive.d.ts.map +0 -1
package/src/SelectItem.tsx
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { useComposedRefs } from '@tamagui/compose-refs'
|
|
2
|
+
import { useAdaptIsActive } from '@tamagui/adapt'
|
|
2
3
|
import { isWeb, useIsomorphicLayoutEffect } from '@tamagui/constants'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
createChangeEventDetails,
|
|
6
|
+
createStyledHOC,
|
|
7
|
+
createStyledContext,
|
|
8
|
+
styled,
|
|
9
|
+
View,
|
|
10
|
+
} from '@tamagui/core'
|
|
11
|
+
import type { GetProps } from '@tamagui/core'
|
|
12
|
+
import { composeEventHandlers } from '@tamagui/helpers'
|
|
6
13
|
import * as React from 'react'
|
|
7
14
|
import { useSelectItemParentContext } from './context'
|
|
8
|
-
import
|
|
15
|
+
import { getSelectOptionProps } from './selectionController'
|
|
16
|
+
import type {
|
|
17
|
+
SelectActiveChangeDetails,
|
|
18
|
+
SelectScopedProps,
|
|
19
|
+
SelectValueChangeDetails,
|
|
20
|
+
} from './types'
|
|
9
21
|
|
|
10
22
|
/* -------------------------------------------------------------------------------------------------
|
|
11
23
|
* SelectItem
|
|
@@ -16,6 +28,7 @@ const ITEM_NAME = 'SelectItem'
|
|
|
16
28
|
type SelectItemContextValue = {
|
|
17
29
|
value: string
|
|
18
30
|
textId: string
|
|
31
|
+
textValue?: string
|
|
19
32
|
isSelected: boolean
|
|
20
33
|
}
|
|
21
34
|
|
|
@@ -26,228 +39,336 @@ export const {
|
|
|
26
39
|
|
|
27
40
|
export interface SelectItemExtraProps {
|
|
28
41
|
value: string
|
|
29
|
-
index: number
|
|
30
42
|
disabled?: boolean
|
|
31
43
|
textValue?: string
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
export interface SelectItemProps
|
|
35
|
-
extends
|
|
47
|
+
extends
|
|
48
|
+
Omit<GetProps<typeof SelectItemFrame>, keyof SelectItemExtraProps>,
|
|
49
|
+
SelectItemExtraProps {}
|
|
50
|
+
|
|
51
|
+
export const SelectItemFrame = styled(View, {
|
|
52
|
+
displayName: ITEM_NAME,
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
flexDirection: 'row',
|
|
55
|
+
})
|
|
36
56
|
|
|
37
|
-
export const SelectItem =
|
|
57
|
+
export const SelectItem = createStyledHOC(
|
|
58
|
+
SelectItemFrame,
|
|
38
59
|
function SelectItem(props: SelectScopedProps<SelectItemProps>, forwardedRef) {
|
|
39
60
|
const {
|
|
40
61
|
scope,
|
|
41
62
|
value,
|
|
42
|
-
disabled
|
|
63
|
+
disabled: disabledProp,
|
|
64
|
+
'aria-disabled': ariaDisabled,
|
|
43
65
|
textValue: textValueProp,
|
|
44
|
-
index,
|
|
45
66
|
...restProps
|
|
46
67
|
} = props
|
|
68
|
+
const disabled = disabledProp ?? ariaDisabled === true
|
|
47
69
|
|
|
70
|
+
// items read only the item parent context: the select context carries
|
|
71
|
+
// viewport state (position, scroll arrows) that must not re-render the list
|
|
48
72
|
const context = useSelectItemParentContext(scope)
|
|
73
|
+
const isAdapted = useAdaptIsActive(context.adaptScope)
|
|
49
74
|
|
|
50
75
|
const {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
76
|
+
registry,
|
|
77
|
+
mode,
|
|
78
|
+
selectedValues,
|
|
79
|
+
selectValue,
|
|
55
80
|
activeIndexSubscribe,
|
|
56
81
|
activeIndexRef,
|
|
57
|
-
valueSubscribe,
|
|
58
82
|
allowMouseUpRef,
|
|
59
83
|
allowSelectRef,
|
|
60
|
-
setValueAtIndex,
|
|
61
84
|
selectTimeoutRef,
|
|
62
|
-
|
|
63
|
-
interactions,
|
|
85
|
+
getItemProps,
|
|
64
86
|
shouldRenderWebNative,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
setActiveIndex,
|
|
88
|
+
lastPointerRef,
|
|
89
|
+
moveActive,
|
|
90
|
+
search,
|
|
69
91
|
} = context
|
|
70
92
|
|
|
71
|
-
const
|
|
93
|
+
const isSelected = selectedValues.includes(value)
|
|
94
|
+
const pendingMouseUpSelectionRef = React.useRef(false)
|
|
95
|
+
const itemNodeRef = React.useRef<any>(null)
|
|
96
|
+
const registrationRef = React.useRef<ReturnType<typeof registry.registerItem> | null>(
|
|
97
|
+
null
|
|
98
|
+
)
|
|
99
|
+
const initialRegistration = React.useRef({
|
|
100
|
+
value,
|
|
101
|
+
disabled,
|
|
102
|
+
textValue: textValueProp,
|
|
103
|
+
})
|
|
72
104
|
|
|
73
|
-
//
|
|
105
|
+
// the item never subscribes to the registry: its index is read at event
|
|
106
|
+
// time, and the owner mirrors the registry's nodes into the list ref
|
|
74
107
|
useIsomorphicLayoutEffect(() => {
|
|
75
|
-
|
|
76
|
-
|
|
108
|
+
const registration = registry.registerItem(initialRegistration.current)
|
|
109
|
+
registrationRef.current = registration
|
|
110
|
+
registration.setNode(itemNodeRef.current)
|
|
111
|
+
return () => {
|
|
112
|
+
registrationRef.current = null
|
|
113
|
+
registration.unregister()
|
|
77
114
|
}
|
|
78
|
-
}, [])
|
|
115
|
+
}, [registry])
|
|
116
|
+
|
|
117
|
+
useIsomorphicLayoutEffect(() => {
|
|
118
|
+
registrationRef.current?.update({ value, disabled, textValue: textValueProp })
|
|
119
|
+
}, [disabled, textValueProp, value])
|
|
120
|
+
|
|
121
|
+
useIsomorphicLayoutEffect(() => {
|
|
122
|
+
registrationRef.current?.setNode(itemNodeRef.current)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// the only per-item state: this item is the active one. the active index
|
|
126
|
+
// itself is a ref plus an emitter, so a hover re-renders two items, not the list
|
|
127
|
+
const [isActive, setIsActive] = React.useState(false)
|
|
128
|
+
// every item hears every change; only the two whose state flips may set
|
|
129
|
+
// it, since a same-value set still renders once when updates are queued
|
|
130
|
+
const isActiveRef = React.useRef(false)
|
|
79
131
|
|
|
80
132
|
React.useEffect(() => {
|
|
81
|
-
const handleActiveIndex = (i: number) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
133
|
+
const handleActiveIndex = (i: number | null) => {
|
|
134
|
+
const next = i != null && registry.getIndex(value) === i
|
|
135
|
+
if (next !== isActiveRef.current) {
|
|
136
|
+
isActiveRef.current = next
|
|
137
|
+
setIsActive(next)
|
|
138
|
+
}
|
|
139
|
+
if (next && isWeb) {
|
|
140
|
+
// use rAF to focus after browser's click handling completes
|
|
141
|
+
// this prevents the trigger from stealing focus after we set it
|
|
142
|
+
requestAnimationFrame(() => {
|
|
143
|
+
itemNodeRef.current?.focus?.()
|
|
144
|
+
})
|
|
91
145
|
}
|
|
92
146
|
}
|
|
93
147
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
if (currentActiveIndex !== null && currentActiveIndex !== undefined) {
|
|
97
|
-
handleActiveIndex(currentActiveIndex)
|
|
98
|
-
}
|
|
148
|
+
// the parent effect may have set it before we subscribed
|
|
149
|
+
handleActiveIndex(activeIndexRef?.current ?? null)
|
|
99
150
|
|
|
100
151
|
return activeIndexSubscribe(handleActiveIndex)
|
|
101
|
-
}, [
|
|
102
|
-
|
|
103
|
-
React.useEffect(() => {
|
|
104
|
-
return valueSubscribe((val) => {
|
|
105
|
-
setSelected(val === value)
|
|
106
|
-
})
|
|
107
|
-
}, [value])
|
|
152
|
+
}, [activeIndexRef, activeIndexSubscribe, registry, value])
|
|
108
153
|
|
|
109
154
|
const textId = React.useId()
|
|
110
155
|
|
|
111
|
-
const refCallback = React.useCallback(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (listRef) {
|
|
116
|
-
listRef.current[index] = node
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
[index, listRef]
|
|
121
|
-
)
|
|
156
|
+
const refCallback = React.useCallback((node) => {
|
|
157
|
+
itemNodeRef.current = node
|
|
158
|
+
registrationRef.current?.setNode(node)
|
|
159
|
+
}, [])
|
|
122
160
|
|
|
123
161
|
const composedRefs = useComposedRefs(forwardedRef, refCallback)
|
|
124
162
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
163
|
+
const handleSelect = React.useCallback(
|
|
164
|
+
(event?: any, reason: 'item-press' | 'keyboard' = 'item-press') => {
|
|
165
|
+
if (disabled) return
|
|
166
|
+
const nativeEvent = event?.nativeEvent || event
|
|
167
|
+
selectValue(
|
|
168
|
+
value,
|
|
169
|
+
createChangeEventDetails(
|
|
170
|
+
reason,
|
|
171
|
+
nativeEvent,
|
|
172
|
+
event?.currentTarget
|
|
173
|
+
) as SelectValueChangeDetails
|
|
174
|
+
)
|
|
175
|
+
},
|
|
176
|
+
[disabled, selectValue, value]
|
|
177
|
+
)
|
|
128
178
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
179
|
+
const handleMouseMove = React.useCallback(
|
|
180
|
+
(event: any) => {
|
|
181
|
+
if (disabled) return
|
|
182
|
+
const index = registry.getIndex(value)
|
|
183
|
+
if (index < 0) return
|
|
184
|
+
const last = lastPointerRef.current
|
|
185
|
+
if (event.clientX === last.x && event.clientY === last.y) return
|
|
186
|
+
last.x = event.clientX
|
|
187
|
+
last.y = event.clientY
|
|
188
|
+
setActiveIndex(index, {
|
|
189
|
+
reason: 'item-hover',
|
|
190
|
+
event: event.nativeEvent || event,
|
|
191
|
+
trigger: event.currentTarget,
|
|
192
|
+
index,
|
|
193
|
+
} as SelectActiveChangeDetails)
|
|
194
|
+
},
|
|
195
|
+
[disabled, lastPointerRef, registry, setActiveIndex, value]
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
const handleKeyDown = React.useCallback(
|
|
199
|
+
(event: any) => {
|
|
200
|
+
if (disabled) return
|
|
201
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
202
|
+
event.preventDefault()
|
|
203
|
+
handleSelect(event, 'keyboard')
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
207
|
+
event.preventDefault()
|
|
208
|
+
event.stopPropagation()
|
|
209
|
+
moveActive(event.key === 'ArrowDown' ? 1 : -1, event.nativeEvent || event)
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
if (
|
|
213
|
+
!getItemProps &&
|
|
214
|
+
event.key?.length === 1 &&
|
|
215
|
+
!event.metaKey &&
|
|
216
|
+
!event.ctrlKey
|
|
217
|
+
) {
|
|
218
|
+
search(event.key, event.nativeEvent || event)
|
|
219
|
+
}
|
|
220
|
+
if (allowSelectRef) {
|
|
221
|
+
allowSelectRef.current = true
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
[allowSelectRef, disabled, handleSelect, getItemProps, moveActive, search]
|
|
225
|
+
)
|
|
134
226
|
|
|
135
227
|
const selectItemProps = React.useMemo(() => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
228
|
+
if (getItemProps) {
|
|
229
|
+
const {
|
|
230
|
+
onTouchMove,
|
|
231
|
+
onTouchEnd,
|
|
232
|
+
onKeyDown,
|
|
233
|
+
onClick,
|
|
234
|
+
onMouseUp,
|
|
235
|
+
onMouseMove,
|
|
236
|
+
onPress,
|
|
237
|
+
...itemProps
|
|
238
|
+
} = restProps
|
|
239
|
+
const interactionProps = getItemProps({
|
|
240
|
+
...itemProps,
|
|
241
|
+
onTouchMove() {
|
|
242
|
+
allowSelectRef!.current = true
|
|
243
|
+
allowMouseUpRef!.current = false
|
|
244
|
+
},
|
|
245
|
+
onTouchEnd() {
|
|
246
|
+
allowSelectRef!.current = false
|
|
247
|
+
allowMouseUpRef!.current = true
|
|
248
|
+
},
|
|
249
|
+
onMouseMove: handleMouseMove,
|
|
250
|
+
onKeyDown: handleKeyDown,
|
|
251
|
+
onClick(event) {
|
|
252
|
+
if (disabled) return
|
|
253
|
+
const shouldSelect =
|
|
254
|
+
pendingMouseUpSelectionRef.current || allowSelectRef!.current
|
|
255
|
+
pendingMouseUpSelectionRef.current = false
|
|
256
|
+
clearTimeout(selectTimeoutRef!.current)
|
|
257
|
+
allowSelectRef!.current = true
|
|
258
|
+
if (shouldSelect) {
|
|
259
|
+
handleSelect(event)
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
onMouseUp(event) {
|
|
263
|
+
if (disabled) return
|
|
264
|
+
if (!allowMouseUpRef!.current) {
|
|
265
|
+
// Re-enable mouseup and selection for subsequent interactions
|
|
144
266
|
allowMouseUpRef!.current = true
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
event.key === 'Enter' ||
|
|
149
|
-
(event.key === ' ' && !dataRef?.current.typing)
|
|
150
|
-
) {
|
|
151
|
-
event.preventDefault()
|
|
152
|
-
handleSelect()
|
|
153
|
-
} else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
154
|
-
// prevent default and stop propagation so floating-ui doesn't also handle
|
|
155
|
-
event.preventDefault()
|
|
156
|
-
event.stopPropagation()
|
|
157
|
-
const itemCount = listRef?.current.length ?? 0
|
|
158
|
-
if (itemCount === 0) return
|
|
267
|
+
allowSelectRef!.current = true
|
|
268
|
+
return
|
|
269
|
+
}
|
|
159
270
|
|
|
160
|
-
|
|
161
|
-
if (event.key === 'ArrowDown') {
|
|
162
|
-
nextIndex = index + 1 >= itemCount ? 0 : index + 1
|
|
163
|
-
} else {
|
|
164
|
-
nextIndex = index - 1 < 0 ? itemCount - 1 : index - 1
|
|
165
|
-
}
|
|
166
|
-
// use fast setter to avoid triggering state updates that reset activeIndex
|
|
167
|
-
setActiveIndexFast?.(nextIndex)
|
|
168
|
-
} else {
|
|
169
|
-
allowSelectRef!.current = true
|
|
170
|
-
}
|
|
171
|
-
},
|
|
271
|
+
pendingMouseUpSelectionRef.current = allowSelectRef!.current
|
|
172
272
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
273
|
+
// A normal click follows mouseup synchronously. Defer the drag-release
|
|
274
|
+
// fallback so the caller's onClick runs before selection closes the item.
|
|
275
|
+
clearTimeout(selectTimeoutRef!.current)
|
|
276
|
+
selectTimeoutRef!.current = setTimeout(() => {
|
|
277
|
+
allowSelectRef!.current = true
|
|
278
|
+
if (pendingMouseUpSelectionRef.current) {
|
|
279
|
+
pendingMouseUpSelectionRef.current = false
|
|
280
|
+
handleSelect(event)
|
|
176
281
|
}
|
|
177
|
-
}
|
|
282
|
+
})
|
|
283
|
+
},
|
|
284
|
+
} as any)
|
|
178
285
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
286
|
+
return {
|
|
287
|
+
...interactionProps,
|
|
288
|
+
onTouchMove: composeEventHandlers(
|
|
289
|
+
onTouchMove as any,
|
|
290
|
+
interactionProps.onTouchMove
|
|
291
|
+
),
|
|
292
|
+
onTouchEnd: composeEventHandlers(
|
|
293
|
+
onTouchEnd as any,
|
|
294
|
+
interactionProps.onTouchEnd
|
|
295
|
+
),
|
|
296
|
+
onKeyDown: composeEventHandlers(onKeyDown as any, interactionProps.onKeyDown),
|
|
297
|
+
onClick(event: any) {
|
|
298
|
+
onClick?.(event)
|
|
299
|
+
if (event.defaultPrevented) {
|
|
300
|
+
pendingMouseUpSelectionRef.current = false
|
|
301
|
+
clearTimeout(selectTimeoutRef!.current)
|
|
302
|
+
allowSelectRef!.current = true
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
interactionProps.onClick?.(event)
|
|
306
|
+
},
|
|
307
|
+
onMouseUp: composeEventHandlers(onMouseUp as any, interactionProps.onMouseUp),
|
|
308
|
+
onMouseMove: composeEventHandlers(
|
|
309
|
+
onMouseMove as any,
|
|
310
|
+
interactionProps.onMouseMove
|
|
311
|
+
),
|
|
312
|
+
onPress,
|
|
313
|
+
}
|
|
314
|
+
}
|
|
186
315
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
316
|
+
return {
|
|
317
|
+
...restProps,
|
|
318
|
+
onKeyDown: composeEventHandlers(restProps.onKeyDown as any, handleKeyDown),
|
|
319
|
+
onMouseMove: composeEventHandlers(restProps.onMouseMove as any, handleMouseMove),
|
|
320
|
+
onPress: composeEventHandlers(restProps.onPress as any, handleSelect),
|
|
321
|
+
}
|
|
322
|
+
}, [
|
|
323
|
+
allowMouseUpRef,
|
|
324
|
+
allowSelectRef,
|
|
325
|
+
disabled,
|
|
326
|
+
handleKeyDown,
|
|
327
|
+
handleMouseMove,
|
|
328
|
+
handleSelect,
|
|
329
|
+
getItemProps,
|
|
330
|
+
restProps,
|
|
331
|
+
selectTimeoutRef,
|
|
332
|
+
])
|
|
190
333
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
},
|
|
198
|
-
})
|
|
199
|
-
: {
|
|
200
|
-
onPress: handleSelect,
|
|
201
|
-
}
|
|
202
|
-
}, [handleSelect, index, listRef, setActiveIndexFast])
|
|
334
|
+
const accessibilityProps = getSelectOptionProps(
|
|
335
|
+
mode,
|
|
336
|
+
isSelected,
|
|
337
|
+
disabled,
|
|
338
|
+
isWeb ? 'web' : 'native'
|
|
339
|
+
)
|
|
203
340
|
|
|
204
341
|
return (
|
|
205
342
|
<SelectItemContextProvider
|
|
206
343
|
scope={scope}
|
|
207
344
|
value={value}
|
|
208
345
|
textId={textId || ''}
|
|
346
|
+
textValue={textValueProp}
|
|
209
347
|
isSelected={isSelected}
|
|
210
348
|
>
|
|
211
349
|
{shouldRenderWebNative ? (
|
|
212
|
-
<option value={value}
|
|
350
|
+
<option value={value} disabled={disabled}>
|
|
351
|
+
{props.children}
|
|
352
|
+
</option>
|
|
213
353
|
) : (
|
|
214
|
-
<
|
|
354
|
+
<SelectItemFrame
|
|
215
355
|
render="div"
|
|
216
|
-
componentName={ITEM_NAME}
|
|
217
356
|
ref={composedRefs}
|
|
218
|
-
role="option"
|
|
219
357
|
aria-labelledby={textId}
|
|
220
|
-
aria-selected={isSelected}
|
|
221
358
|
data-state={isSelected ? 'active' : 'inactive'}
|
|
222
|
-
aria-disabled={disabled || undefined}
|
|
223
359
|
data-disabled={disabled ? '' : undefined}
|
|
224
|
-
tabIndex={
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
pressStyle: {
|
|
236
|
-
backgroundColor: '$backgroundPress',
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
focusStyle: {
|
|
240
|
-
backgroundColor: '$backgroundFocus',
|
|
241
|
-
},
|
|
242
|
-
|
|
243
|
-
focusVisibleStyle: {
|
|
244
|
-
outlineColor: '$outlineColor',
|
|
245
|
-
outlineWidth: 1,
|
|
246
|
-
outlineStyle: 'solid',
|
|
247
|
-
},
|
|
248
|
-
})}
|
|
249
|
-
{...restProps}
|
|
360
|
+
tabIndex={
|
|
361
|
+
disabled
|
|
362
|
+
? undefined
|
|
363
|
+
: isWeb &&
|
|
364
|
+
isAdapted &&
|
|
365
|
+
(isActive || (activeIndexRef?.current == null && isSelected))
|
|
366
|
+
? 0
|
|
367
|
+
: -1
|
|
368
|
+
}
|
|
369
|
+
zIndex={100}
|
|
250
370
|
{...selectItemProps}
|
|
371
|
+
{...accessibilityProps}
|
|
251
372
|
/>
|
|
252
373
|
)}
|
|
253
374
|
</SelectItemContextProvider>
|
package/src/SelectItemText.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useComposedRefs } from '@tamagui/compose-refs'
|
|
2
2
|
import type { GetProps, TamaguiTextElement } from '@tamagui/core'
|
|
3
|
-
import { styled, useIsomorphicLayoutEffect } from '@tamagui/core'
|
|
4
|
-
import { SizableText } from '@tamagui/text'
|
|
3
|
+
import { createStyledHOC, styled, Text, useIsomorphicLayoutEffect } from '@tamagui/core'
|
|
5
4
|
import * as React from 'react'
|
|
6
5
|
|
|
7
6
|
import { useSelectItemParentContext } from './context'
|
|
7
|
+
import { getSelectLabelText } from './selectionController'
|
|
8
8
|
import { useSelectItemContext } from './SelectItem'
|
|
9
9
|
import type { SelectScopedProps } from './types'
|
|
10
10
|
|
|
@@ -14,30 +14,17 @@ import type { SelectScopedProps } from './types'
|
|
|
14
14
|
|
|
15
15
|
export const ITEM_TEXT_NAME = 'SelectItemText'
|
|
16
16
|
|
|
17
|
-
export const SelectItemTextFrame = styled(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
variants: {
|
|
21
|
-
unstyled: {
|
|
22
|
-
false: {
|
|
23
|
-
userSelect: 'none',
|
|
24
|
-
color: '$color',
|
|
25
|
-
ellipsis: true,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
} as const,
|
|
29
|
-
|
|
30
|
-
defaultVariants: {
|
|
31
|
-
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
32
|
-
},
|
|
17
|
+
export const SelectItemTextFrame = styled(Text, {
|
|
18
|
+
displayName: ITEM_TEXT_NAME,
|
|
33
19
|
})
|
|
34
20
|
|
|
35
21
|
type SelectItemTextExtraProps = SelectScopedProps<{}>
|
|
36
22
|
export type SelectItemTextProps = GetProps<typeof SelectItemTextFrame> &
|
|
37
23
|
SelectItemTextExtraProps
|
|
38
24
|
|
|
39
|
-
export const SelectItemText =
|
|
40
|
-
|
|
25
|
+
export const SelectItemText = createStyledHOC(
|
|
26
|
+
SelectItemTextFrame,
|
|
27
|
+
function SelectItemText(props: SelectItemTextProps, forwardedRef) {
|
|
41
28
|
const { scope, className, ...itemTextProps } = props
|
|
42
29
|
// note: only uses itemParentContext (not selectContext) to avoid re-renders
|
|
43
30
|
// when activeIndex changes on hover
|
|
@@ -46,13 +33,15 @@ export const SelectItemText = SelectItemTextFrame.styleable<SelectItemTextExtraP
|
|
|
46
33
|
const composedRefs = useComposedRefs(forwardedRef, ref)
|
|
47
34
|
const itemContext = useSelectItemContext(scope)
|
|
48
35
|
const contents = React.useRef<React.ReactNode>(null)
|
|
36
|
+
const label = React.useRef(props.children)
|
|
37
|
+
label.current = props.children
|
|
38
|
+
const labelText = getSelectLabelText(props.children)
|
|
49
39
|
|
|
50
40
|
// we portal this to the selected area, which is fine to be a bit unsafe concurrently (mostly? its not changing often)...
|
|
51
41
|
// until react native supports portals this is best i think
|
|
52
42
|
contents.current = (
|
|
53
43
|
<SelectItemTextFrame
|
|
54
44
|
className={className}
|
|
55
|
-
size={itemParentContext.size as any}
|
|
56
45
|
id={itemContext.textId}
|
|
57
46
|
{...itemTextProps}
|
|
58
47
|
ref={composedRefs}
|
|
@@ -60,18 +49,12 @@ export const SelectItemText = SelectItemTextFrame.styleable<SelectItemTextExtraP
|
|
|
60
49
|
)
|
|
61
50
|
|
|
62
51
|
useIsomorphicLayoutEffect(() => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return itemParentContext.valueSubscribe((val) => {
|
|
70
|
-
if (val === itemContext.value) {
|
|
71
|
-
itemParentContext.setSelectedItem(contents.current)
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
}, [itemContext.value])
|
|
52
|
+
return itemParentContext.registry.registerLabel(
|
|
53
|
+
itemContext.value,
|
|
54
|
+
label.current,
|
|
55
|
+
itemContext.textValue
|
|
56
|
+
)
|
|
57
|
+
}, [itemContext.textValue, itemContext.value, itemParentContext.registry, labelText])
|
|
75
58
|
|
|
76
59
|
if (itemParentContext.shouldRenderWebNative) {
|
|
77
60
|
return <>{props.children}</>
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
import { createStyledHOC, styled, View } from '@tamagui/core'
|
|
1
2
|
import type { SelectScopedProps, SelectScrollButtonProps } from './types'
|
|
2
3
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
4
|
+
export const SelectScrollButtonFrame = styled(View, {
|
|
5
|
+
displayName: 'SelectScrollButton',
|
|
6
|
+
})
|
|
5
7
|
|
|
6
|
-
export const
|
|
7
|
-
|
|
8
|
+
export const SelectScrollUpButton = createStyledHOC(
|
|
9
|
+
SelectScrollButtonFrame,
|
|
10
|
+
(_props: SelectScopedProps<SelectScrollButtonProps>) => null
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
export const SelectScrollDownButton = createStyledHOC(
|
|
14
|
+
SelectScrollButtonFrame,
|
|
15
|
+
(_props: SelectScopedProps<SelectScrollButtonProps>) => null
|
|
16
|
+
)
|