@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/SelectContent.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useAdaptContext, useAdaptIsActive } from '@tamagui/adapt'
|
|
2
2
|
import { Dismissable } from '@tamagui/dismissable'
|
|
3
3
|
import type { FocusScopeProps } from '@tamagui/focus-scope'
|
|
4
4
|
import { FocusScope } from '@tamagui/focus-scope'
|
|
5
|
+
import { composeEventHandlers } from '@tamagui/helpers'
|
|
5
6
|
|
|
6
7
|
import { Portal } from '@tamagui/portal'
|
|
7
8
|
import { RemoveScroll } from '@tamagui/remove-scroll'
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
useSelectItemParentContext,
|
|
13
14
|
} from './context'
|
|
14
15
|
import type { SelectContentProps } from './types'
|
|
15
|
-
import {
|
|
16
|
+
import type { SelectOpenChangeDetails } from './types'
|
|
16
17
|
|
|
17
18
|
/* -------------------------------------------------------------------------------------------------
|
|
18
19
|
* SelectContent
|
|
@@ -21,12 +22,17 @@ import { useShowSelectSheet } from './useSelectBreakpointActive'
|
|
|
21
22
|
export const SelectContent = ({
|
|
22
23
|
children,
|
|
23
24
|
scope,
|
|
25
|
+
onEscapeKeyDown,
|
|
26
|
+
onPointerDownOutside,
|
|
27
|
+
onFocusOutside,
|
|
28
|
+
onInteractOutside,
|
|
24
29
|
...focusScopeProps
|
|
25
30
|
}: SelectContentProps & FocusScopeProps) => {
|
|
26
31
|
const context = useSelectContext(scope)
|
|
27
32
|
const itemParentContext = useSelectItemParentContext(scope)
|
|
28
33
|
const zIndex = useContext(SelectZIndexContext)
|
|
29
|
-
const
|
|
34
|
+
const isAdapted = useAdaptIsActive(context.adaptScope)
|
|
35
|
+
const adaptContext = useAdaptContext(context.adaptScope)
|
|
30
36
|
|
|
31
37
|
const contents = children
|
|
32
38
|
|
|
@@ -34,8 +40,11 @@ export const SelectContent = ({
|
|
|
34
40
|
return <>{children}</>
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
if (
|
|
38
|
-
|
|
43
|
+
if (isAdapted) {
|
|
44
|
+
// content is published into the Sheet via SelectViewport's AdaptPortalContents;
|
|
45
|
+
// keep it mounted through the sheet slide-out (adaptContext.targetFullyHidden),
|
|
46
|
+
// otherwise the sheet body vanishes mid-slide on close. mirrors Popover/Dialog.
|
|
47
|
+
if (!context.open && adaptContext.targetFullyHidden) {
|
|
39
48
|
return null
|
|
40
49
|
}
|
|
41
50
|
return <>{contents}</>
|
|
@@ -47,13 +56,35 @@ export const SelectContent = ({
|
|
|
47
56
|
<Dismissable
|
|
48
57
|
asChild
|
|
49
58
|
forceUnmount={!context.open}
|
|
50
|
-
onDismiss={() =>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
onDismiss={(details) => {
|
|
60
|
+
if (details.reason !== 'focus-out') {
|
|
61
|
+
itemParentContext.requestOpenChange(
|
|
62
|
+
false,
|
|
63
|
+
details as SelectOpenChangeDetails
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
}}
|
|
67
|
+
onEscapeKeyDown={(details) => {
|
|
68
|
+
const event = details.event as unknown as KeyboardEvent | undefined
|
|
69
|
+
event?.stopImmediatePropagation()
|
|
70
|
+
onEscapeKeyDown?.(details)
|
|
71
|
+
}}
|
|
72
|
+
onInteractOutside={onInteractOutside}
|
|
73
|
+
// focus changes during open should not dismiss the list
|
|
74
|
+
onFocusOutside={composeEventHandlers(onFocusOutside, (e) => e.cancel(), {
|
|
75
|
+
checkDefaultPrevented: false,
|
|
76
|
+
})}
|
|
77
|
+
onPointerDownOutside={composeEventHandlers(
|
|
78
|
+
onPointerDownOutside,
|
|
79
|
+
(details) => {
|
|
80
|
+
const trigger = context.floatingContext?.refs?.reference?.current
|
|
81
|
+
const target = details.event?.target
|
|
82
|
+
if (trigger instanceof HTMLElement && target instanceof Node) {
|
|
83
|
+
if (trigger.contains(target)) details.cancel()
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{ checkDefaultPrevented: false }
|
|
87
|
+
)}
|
|
57
88
|
>
|
|
58
89
|
<FocusScope
|
|
59
90
|
{...focusScopeProps}
|
|
@@ -61,19 +92,18 @@ export const SelectContent = ({
|
|
|
61
92
|
trapped
|
|
62
93
|
onMountAutoFocus={(e) => {
|
|
63
94
|
// prevent FocusScope from auto-focusing - floating-ui handles focus in SelectItem
|
|
64
|
-
e.
|
|
95
|
+
e.cancel()
|
|
65
96
|
}}
|
|
66
97
|
onUnmountAutoFocus={(e) => {
|
|
67
98
|
// return focus to trigger on close
|
|
68
|
-
e.
|
|
99
|
+
e.cancel()
|
|
69
100
|
const trigger = context.floatingContext?.refs?.reference?.current
|
|
70
101
|
if (trigger instanceof HTMLElement) {
|
|
71
102
|
trigger.focus()
|
|
72
103
|
}
|
|
73
104
|
}}
|
|
74
105
|
>
|
|
75
|
-
{
|
|
76
|
-
{isWeb ? <div style={{ display: 'contents' }}>{contents}</div> : contents}
|
|
106
|
+
{contents}
|
|
77
107
|
</FocusScope>
|
|
78
108
|
</Dismissable>
|
|
79
109
|
</RemoveScroll>
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import type { SelectImplProps } from './types'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* TODO can just make it a sheet for native
|
|
5
|
-
* later on can have `native` prop
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
3
|
export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
9
|
-
if (process.env.NODE_ENV === 'development') {
|
|
10
|
-
console.warn('Sheet not implemented inline on native')
|
|
11
|
-
}
|
|
12
4
|
return <>{props.children}</>
|
|
13
5
|
}
|
package/src/SelectImpl.tsx
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
autoUpdate,
|
|
3
|
-
flip,
|
|
4
3
|
inner,
|
|
5
4
|
offset,
|
|
6
|
-
shift,
|
|
7
5
|
size,
|
|
8
6
|
useClick,
|
|
9
7
|
useFloatingRaw as useFloatingDom,
|
|
@@ -16,38 +14,41 @@ import {
|
|
|
16
14
|
type SideObject,
|
|
17
15
|
} from '@tamagui/floating'
|
|
18
16
|
import { useIsomorphicLayoutEffect } from '@tamagui/constants'
|
|
19
|
-
import { useEvent, useIsTouchDevice } from '@tamagui/core'
|
|
17
|
+
import { createChangeEventDetails, useEvent, useIsTouchDevice } from '@tamagui/core'
|
|
18
|
+
import { composeEventHandlers } from '@tamagui/helpers'
|
|
20
19
|
import * as React from 'react'
|
|
21
|
-
import {
|
|
22
|
-
import { SCROLL_ARROW_THRESHOLD, WINDOW_PADDING } from './constants'
|
|
20
|
+
import { SCROLL_ARROW_THRESHOLD } from './constants'
|
|
23
21
|
import {
|
|
24
22
|
SelectItemParentProvider,
|
|
25
23
|
SelectProvider,
|
|
26
24
|
useSelectContext,
|
|
27
25
|
useSelectItemParentContext,
|
|
28
26
|
} from './context'
|
|
29
|
-
import type {
|
|
27
|
+
import type {
|
|
28
|
+
SelectActiveChangeDetails,
|
|
29
|
+
SelectImplProps,
|
|
30
|
+
SelectOpenChangeDetails,
|
|
31
|
+
} from './types'
|
|
30
32
|
|
|
31
|
-
// TODO use id for focusing from label
|
|
32
33
|
export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
33
|
-
const { scope, children, open = false, listContentRef
|
|
34
|
+
const { scope, children, open = false, listContentRef } = props
|
|
34
35
|
|
|
35
36
|
const selectContext = useSelectContext(scope)
|
|
36
37
|
const selectItemParentContext = useSelectItemParentContext(scope)
|
|
37
38
|
const { setActiveIndex, selectedIndex, activeIndexRef } = selectContext
|
|
38
39
|
|
|
39
|
-
const {
|
|
40
|
+
const { requestOpenChange, registry } = selectItemParentContext
|
|
40
41
|
|
|
41
|
-
const [scrollTop, setScrollTop] = React.useState(0)
|
|
42
42
|
const touch = useIsTouchDevice()
|
|
43
43
|
|
|
44
|
-
const listItemsRef =
|
|
44
|
+
const listItemsRef = selectItemParentContext.listRef!
|
|
45
45
|
const overflowRef = React.useRef<null | SideObject>(null)
|
|
46
46
|
const upArrowRef = React.useRef<HTMLDivElement | null>(null)
|
|
47
47
|
const downArrowRef = React.useRef<HTMLDivElement | null>(null)
|
|
48
48
|
const allowSelectRef = React.useRef(false)
|
|
49
49
|
const allowMouseUpRef = React.useRef(true)
|
|
50
50
|
const selectTimeoutRef = React.useRef<any>(null)
|
|
51
|
+
const latestKeyboardEventRef = React.useRef<Event | undefined>(undefined)
|
|
51
52
|
const state = React.useRef({
|
|
52
53
|
isMouseOutside: false,
|
|
53
54
|
isTyping: false,
|
|
@@ -57,19 +58,15 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
57
58
|
const [fallback, setFallback] = React.useState(false)
|
|
58
59
|
const [innerOffset, setInnerOffset] = React.useState(0)
|
|
59
60
|
const [blockSelection, setBlockSelection] = React.useState(false)
|
|
60
|
-
const floatingStyle = React.useRef({})
|
|
61
61
|
|
|
62
|
-
// sync activeIndex on open/close
|
|
63
62
|
React.useEffect(() => {
|
|
64
|
-
if (open) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
setScrollTop(0)
|
|
63
|
+
if (!open) {
|
|
64
|
+
setCanScrollUp(false)
|
|
65
|
+
setCanScrollDown(false)
|
|
68
66
|
setFallback(false)
|
|
69
|
-
setActiveIndexFast(null)
|
|
70
67
|
setControlledScrolling(false)
|
|
71
68
|
}
|
|
72
|
-
}, [open
|
|
69
|
+
}, [open])
|
|
73
70
|
|
|
74
71
|
// close when mouseup outside select
|
|
75
72
|
if (process.env.TAMAGUI_TARGET === 'web') {
|
|
@@ -77,7 +74,10 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
77
74
|
if (!open) return
|
|
78
75
|
const mouseUp = (e: MouseEvent) => {
|
|
79
76
|
if (state.current.isMouseOutside) {
|
|
80
|
-
|
|
77
|
+
requestOpenChange(
|
|
78
|
+
false,
|
|
79
|
+
createChangeEventDetails('outside-press', e) as SelectOpenChangeDetails
|
|
80
|
+
)
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
document.addEventListener('mouseup', mouseUp)
|
|
@@ -98,103 +98,97 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
98
98
|
open,
|
|
99
99
|
placement: 'bottom-start',
|
|
100
100
|
whileElementsMounted: autoUpdate,
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
offset
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
]
|
|
124
|
-
: [
|
|
125
|
-
size({
|
|
126
|
-
apply({
|
|
127
|
-
rects: {
|
|
128
|
-
reference: { width },
|
|
129
|
-
},
|
|
130
|
-
}) {
|
|
131
|
-
Object.assign(floatingStyle.current, {
|
|
132
|
-
minWidth: width + 8,
|
|
133
|
-
})
|
|
134
|
-
if (refs.floating.current) {
|
|
135
|
-
Object.assign(refs.floating.current.style, floatingStyle.current)
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
}),
|
|
139
|
-
inner({
|
|
140
|
-
listRef: listItemsRef,
|
|
141
|
-
overflowRef,
|
|
142
|
-
index: selectedIndex,
|
|
143
|
-
offset: innerOffset,
|
|
144
|
-
onFallbackChange: setFallback,
|
|
145
|
-
padding: 10,
|
|
146
|
-
minItemsVisible: touch ? 10 : 4,
|
|
147
|
-
referenceOverflowThreshold: 20,
|
|
148
|
-
}),
|
|
149
|
-
offset({ crossAxis: -5 }),
|
|
150
|
-
],
|
|
101
|
+
// The removed alternate stack was the logic from floating-ui,
|
|
102
|
+
// but it causes issues (open, drag select, close, then re-open its not positioned "over")
|
|
103
|
+
// https://github.com/floating-ui/floating-ui/blob/master/packages/react/test/visual/components/MacSelect.tsx
|
|
104
|
+
middleware: [
|
|
105
|
+
size({
|
|
106
|
+
apply({ rects, elements }) {
|
|
107
|
+
// the list overhangs the trigger by 4px a side (with the -4 cross offset)
|
|
108
|
+
elements.floating.style.minWidth = `${rects.reference.width + 8}px`
|
|
109
|
+
},
|
|
110
|
+
}),
|
|
111
|
+
inner({
|
|
112
|
+
listRef: listItemsRef,
|
|
113
|
+
overflowRef,
|
|
114
|
+
index: selectedIndex,
|
|
115
|
+
offset: innerOffset,
|
|
116
|
+
onFallbackChange: setFallback,
|
|
117
|
+
padding: 10,
|
|
118
|
+
minItemsVisible: touch ? 10 : 4,
|
|
119
|
+
referenceOverflowThreshold: 20,
|
|
120
|
+
}),
|
|
121
|
+
offset({ crossAxis: -4 }),
|
|
122
|
+
],
|
|
151
123
|
} as any)
|
|
152
124
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
125
|
+
// the arrows are booleans that flip at a threshold, never the scroll offset
|
|
126
|
+
// itself, so a scroll event costs no render unless an arrow appears or goes
|
|
127
|
+
const [canScrollUp, setCanScrollUp] = React.useState(false)
|
|
128
|
+
const [canScrollDown, setCanScrollDown] = React.useState(false)
|
|
129
|
+
const updateScrollArrows = React.useCallback(() => {
|
|
130
|
+
const el = refs.floating.current
|
|
131
|
+
if (!el) return
|
|
132
|
+
setCanScrollUp(el.scrollTop > SCROLL_ARROW_THRESHOLD)
|
|
133
|
+
setCanScrollDown(
|
|
134
|
+
el.scrollTop < el.scrollHeight - el.clientHeight - SCROLL_ARROW_THRESHOLD
|
|
135
|
+
)
|
|
136
|
+
}, [refs])
|
|
137
|
+
|
|
138
|
+
const showUpArrow = open && canScrollUp
|
|
139
|
+
const showDownArrow = open && canScrollDown
|
|
164
140
|
const isScrollable = showDownArrow || showUpArrow
|
|
165
141
|
|
|
142
|
+
// autoUpdate handles resize and scroll while mounted; this covers the open
|
|
166
143
|
useIsomorphicLayoutEffect(() => {
|
|
167
|
-
if (
|
|
168
|
-
window.addEventListener('resize', update)
|
|
169
|
-
if (open) {
|
|
170
|
-
update()
|
|
171
|
-
}
|
|
172
|
-
return () => window.removeEventListener('resize', update)
|
|
144
|
+
if (open) update()
|
|
173
145
|
}, [update, open])
|
|
174
146
|
|
|
175
147
|
const onMatch = useEvent((index: number) => {
|
|
176
|
-
|
|
177
|
-
|
|
148
|
+
if (!open) return
|
|
149
|
+
setActiveIndex(index, {
|
|
150
|
+
reason: 'keyboard',
|
|
151
|
+
event: latestKeyboardEventRef.current,
|
|
152
|
+
trigger: undefined,
|
|
153
|
+
index,
|
|
154
|
+
} as SelectActiveChangeDetails)
|
|
178
155
|
})
|
|
179
156
|
|
|
180
|
-
//
|
|
157
|
+
// the interaction context for the floating hooks. every hook memoizes its
|
|
158
|
+
// props on this object, and those props sit in the item context, so a new
|
|
159
|
+
// identity here re-renders every item
|
|
181
160
|
const dataRef = React.useRef<{ openEvent?: Event; placement?: string }>({})
|
|
182
161
|
dataRef.current.placement = computedPlacement
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
162
|
+
const onOpenChange = useEvent((nextOpen: boolean, event?: Event, reason?: string) => {
|
|
163
|
+
requestOpenChange(
|
|
164
|
+
nextOpen,
|
|
165
|
+
createChangeEventDetails(
|
|
166
|
+
reason === 'list-navigation' ? 'keyboard' : 'trigger-press',
|
|
167
|
+
event,
|
|
168
|
+
refs.reference.current
|
|
169
|
+
) as SelectOpenChangeDetails
|
|
170
|
+
)
|
|
171
|
+
})
|
|
172
|
+
const referenceElement = (refs.reference.current as Element | null) || null
|
|
173
|
+
const floatingElement = refs.floating.current || null
|
|
174
|
+
const interactionContext: FloatingInteractionContext = React.useMemo(
|
|
175
|
+
() => ({
|
|
176
|
+
open,
|
|
177
|
+
onOpenChange,
|
|
178
|
+
refs: {
|
|
179
|
+
reference: refs.reference as any,
|
|
180
|
+
floating: refs.floating,
|
|
181
|
+
domReference: refs.reference as any,
|
|
182
|
+
},
|
|
183
|
+
elements: {
|
|
184
|
+
reference: referenceElement,
|
|
185
|
+
floating: floatingElement,
|
|
186
|
+
domReference: referenceElement,
|
|
187
|
+
},
|
|
188
|
+
dataRef,
|
|
189
|
+
}),
|
|
190
|
+
[open, onOpenChange, refs, referenceElement, floatingElement]
|
|
191
|
+
)
|
|
198
192
|
|
|
199
193
|
const interactionsProps = [
|
|
200
194
|
useClick(interactionContext, { event: 'mousedown', keyboardHandlers: false }),
|
|
@@ -208,20 +202,31 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
208
202
|
}),
|
|
209
203
|
useListNavigation(interactionContext, {
|
|
210
204
|
listRef: listItemsRef,
|
|
211
|
-
|
|
205
|
+
// items focus themselves from the active-index emitter, and the hook's
|
|
206
|
+
// own index syncs from item focus, so it never needs the value
|
|
207
|
+
activeIndex: null,
|
|
212
208
|
selectedIndex,
|
|
213
209
|
onNavigate: (index) => {
|
|
214
210
|
if (index !== null) {
|
|
215
|
-
setActiveIndex(index
|
|
211
|
+
setActiveIndex(index, {
|
|
212
|
+
reason: 'list-navigation',
|
|
213
|
+
event: latestKeyboardEventRef.current,
|
|
214
|
+
trigger: undefined,
|
|
215
|
+
index,
|
|
216
|
+
} as SelectActiveChangeDetails)
|
|
216
217
|
}
|
|
217
218
|
},
|
|
219
|
+
disabledIndices: registry.getDisabledIndices(),
|
|
218
220
|
scrollItemIntoView: false,
|
|
221
|
+
// items own hover: the hook's leave handler would focus the viewport when
|
|
222
|
+
// a scroll moves the list under a still pointer, losing the keyboard position
|
|
223
|
+
focusItemOnHover: false,
|
|
219
224
|
}),
|
|
220
225
|
useTypeahead(interactionContext, {
|
|
221
226
|
listRef: listContentRef,
|
|
222
227
|
onMatch,
|
|
223
228
|
selectedIndex,
|
|
224
|
-
activeIndex:
|
|
229
|
+
activeIndex: null,
|
|
225
230
|
onTypingChange: (e) => {
|
|
226
231
|
state.current.isTyping = e
|
|
227
232
|
},
|
|
@@ -234,39 +239,38 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
234
239
|
}, interactionsProps)
|
|
235
240
|
)
|
|
236
241
|
|
|
242
|
+
// the trigger and viewport getters. items get getItemProps alone, which is
|
|
243
|
+
// stable once the list mounts, so the floating element mounting (a new
|
|
244
|
+
// identity here) never re-renders the list
|
|
237
245
|
const interactionsContext = React.useMemo(() => {
|
|
238
246
|
return {
|
|
239
|
-
|
|
240
|
-
getReferenceProps() {
|
|
247
|
+
getReferenceProps(props: Record<string, any> = {}) {
|
|
241
248
|
return interactions.getReferenceProps({
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
249
|
+
...props,
|
|
250
|
+
onKeyDown: composeEventHandlers(props.onKeyDown, (event: any) => {
|
|
251
|
+
latestKeyboardEventRef.current = event.nativeEvent || event
|
|
245
252
|
if (
|
|
246
253
|
event.key === 'Enter' ||
|
|
247
254
|
event.code === 'Space' ||
|
|
248
255
|
(event.key === ' ' && !state.current.isTyping)
|
|
249
256
|
) {
|
|
250
257
|
event.preventDefault()
|
|
251
|
-
|
|
258
|
+
requestOpenChange(
|
|
259
|
+
true,
|
|
260
|
+
createChangeEventDetails(
|
|
261
|
+
'keyboard',
|
|
262
|
+
event.nativeEvent || event,
|
|
263
|
+
refs.reference.current
|
|
264
|
+
) as SelectOpenChangeDetails
|
|
265
|
+
)
|
|
252
266
|
}
|
|
253
|
-
},
|
|
267
|
+
}),
|
|
254
268
|
})
|
|
255
269
|
},
|
|
256
270
|
getFloatingProps(props) {
|
|
257
271
|
return interactions.getFloatingProps({
|
|
258
272
|
ref: refs.floating,
|
|
259
|
-
className: 'Select',
|
|
260
273
|
...props,
|
|
261
|
-
style: {
|
|
262
|
-
position: strategy,
|
|
263
|
-
top: y ?? '',
|
|
264
|
-
left: x ?? '',
|
|
265
|
-
outline: 0,
|
|
266
|
-
scrollbarWidth: 'none',
|
|
267
|
-
...floatingStyle.current,
|
|
268
|
-
...props?.style,
|
|
269
|
-
},
|
|
270
274
|
onPointerEnter() {
|
|
271
275
|
setControlledScrolling(false)
|
|
272
276
|
state.current.isMouseOutside = false
|
|
@@ -278,21 +282,23 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
278
282
|
state.current.isMouseOutside = false
|
|
279
283
|
setControlledScrolling(false)
|
|
280
284
|
},
|
|
281
|
-
onKeyDown() {
|
|
285
|
+
onKeyDown(event) {
|
|
286
|
+
latestKeyboardEventRef.current = event.nativeEvent || event
|
|
282
287
|
setControlledScrolling(true)
|
|
283
288
|
},
|
|
284
289
|
onContextMenu(e) {
|
|
285
290
|
e.preventDefault()
|
|
286
291
|
},
|
|
287
|
-
onScroll
|
|
288
|
-
flushSync(() => {
|
|
289
|
-
setScrollTop(event.currentTarget.scrollTop)
|
|
290
|
-
})
|
|
291
|
-
},
|
|
292
|
+
onScroll: updateScrollArrows,
|
|
292
293
|
})
|
|
293
294
|
},
|
|
294
295
|
}
|
|
295
|
-
}, [refs
|
|
296
|
+
}, [refs, interactions, requestOpenChange, updateScrollArrows])
|
|
297
|
+
|
|
298
|
+
const floatingPosition = React.useMemo(
|
|
299
|
+
() => ({ position: strategy, top: y ?? '', left: x ?? '' }),
|
|
300
|
+
[strategy, x, y]
|
|
301
|
+
)
|
|
296
302
|
|
|
297
303
|
// effects
|
|
298
304
|
|
|
@@ -322,30 +328,6 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
322
328
|
}
|
|
323
329
|
}, [open])
|
|
324
330
|
|
|
325
|
-
// dismiss on outside pointer down (arrows are outside the floating DOM tree)
|
|
326
|
-
useIsomorphicLayoutEffect(() => {
|
|
327
|
-
function onPointerDown(e: PointerEvent) {
|
|
328
|
-
const target = e.target as Node
|
|
329
|
-
if (
|
|
330
|
-
!(
|
|
331
|
-
refs.floating.current?.contains(target) ||
|
|
332
|
-
upArrowRef.current?.contains(target) ||
|
|
333
|
-
downArrowRef.current?.contains(target)
|
|
334
|
-
)
|
|
335
|
-
) {
|
|
336
|
-
setOpen(false)
|
|
337
|
-
setControlledScrolling(false)
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (open) {
|
|
342
|
-
document.addEventListener('pointerdown', onPointerDown)
|
|
343
|
-
return () => {
|
|
344
|
-
document.removeEventListener('pointerdown', onPointerDown)
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}, [open, refs, setOpen])
|
|
348
|
-
|
|
349
331
|
// scroll activeIndex into view during keyboard nav
|
|
350
332
|
React.useEffect(() => {
|
|
351
333
|
if (!open) return
|
|
@@ -354,13 +336,18 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
354
336
|
if (controlledScrolling && index != null) {
|
|
355
337
|
listItemsRef.current[index]?.scrollIntoView({ block: 'nearest' })
|
|
356
338
|
}
|
|
357
|
-
|
|
339
|
+
updateScrollArrows()
|
|
358
340
|
}
|
|
359
341
|
|
|
360
342
|
scrollActiveIntoView(activeIndexRef.current)
|
|
361
343
|
|
|
362
344
|
return selectItemParentContext.activeIndexSubscribe(scrollActiveIntoView)
|
|
363
|
-
}, [
|
|
345
|
+
}, [
|
|
346
|
+
open,
|
|
347
|
+
controlledScrolling,
|
|
348
|
+
selectItemParentContext.activeIndexSubscribe,
|
|
349
|
+
updateScrollArrows,
|
|
350
|
+
])
|
|
364
351
|
|
|
365
352
|
React.useEffect(() => {
|
|
366
353
|
if (open && fallback) {
|
|
@@ -389,10 +376,12 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
389
376
|
<SelectProvider
|
|
390
377
|
scope={scope}
|
|
391
378
|
{...(selectContext as Required<typeof selectContext>)}
|
|
392
|
-
|
|
379
|
+
updateScrollArrows={updateScrollArrows}
|
|
393
380
|
setInnerOffset={setInnerOffset}
|
|
394
381
|
fallback={fallback}
|
|
395
382
|
floatingContext={floatingContext as any}
|
|
383
|
+
interactions={interactionsContext}
|
|
384
|
+
floatingPosition={floatingPosition}
|
|
396
385
|
canScrollDown={!!showDownArrow}
|
|
397
386
|
canScrollUp={!!showUpArrow}
|
|
398
387
|
controlledScrolling={controlledScrolling}
|
|
@@ -407,7 +396,7 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
|
|
|
407
396
|
allowMouseUpRef={allowMouseUpRef}
|
|
408
397
|
allowSelectRef={allowSelectRef}
|
|
409
398
|
dataRef={dataRef as any}
|
|
410
|
-
|
|
399
|
+
getItemProps={interactions.getItemProps}
|
|
411
400
|
listRef={listItemsRef}
|
|
412
401
|
selectTimeoutRef={selectTimeoutRef}
|
|
413
402
|
>
|