@tamagui/select 2.7.6 → 3.0.0-beta.637.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.
Files changed (248) hide show
  1. package/dist/cjs/Select.cjs +409 -467
  2. package/dist/cjs/Select.native.cjs +495 -0
  3. package/dist/cjs/Select.native.js +445 -492
  4. package/dist/cjs/Select.native.js.map +1 -1
  5. package/dist/cjs/SelectContent.cjs +80 -79
  6. package/dist/cjs/SelectContent.native.cjs +30 -0
  7. package/dist/cjs/SelectContent.native.js +18 -22
  8. package/dist/cjs/SelectContent.native.js.map +1 -1
  9. package/dist/cjs/SelectImpl.cjs +299 -319
  10. package/dist/cjs/SelectImpl.native.cjs +30 -0
  11. package/dist/cjs/SelectImpl.native.js +17 -24
  12. package/dist/cjs/SelectImpl.native.js.map +1 -1
  13. package/dist/cjs/SelectItem.cjs +268 -205
  14. package/dist/cjs/SelectItem.native.cjs +317 -0
  15. package/dist/cjs/SelectItem.native.js +291 -211
  16. package/dist/cjs/SelectItem.native.js.map +1 -1
  17. package/dist/cjs/SelectItemText.cjs +49 -79
  18. package/dist/cjs/SelectItemText.native.cjs +74 -0
  19. package/dist/cjs/SelectItemText.native.js +50 -79
  20. package/dist/cjs/SelectItemText.native.js.map +1 -1
  21. package/dist/cjs/SelectScrollButton.cjs +111 -130
  22. package/dist/cjs/SelectScrollButton.native.cjs +38 -0
  23. package/dist/cjs/SelectScrollButton.native.js +25 -22
  24. package/dist/cjs/SelectScrollButton.native.js.map +1 -1
  25. package/dist/cjs/SelectTrigger.cjs +69 -84
  26. package/dist/cjs/SelectTrigger.native.cjs +91 -0
  27. package/dist/cjs/SelectTrigger.native.js +71 -66
  28. package/dist/cjs/SelectTrigger.native.js.map +1 -1
  29. package/dist/cjs/SelectViewport.cjs +119 -142
  30. package/dist/cjs/SelectViewport.native.cjs +73 -0
  31. package/dist/cjs/SelectViewport.native.js +54 -50
  32. package/dist/cjs/SelectViewport.native.js.map +1 -1
  33. package/dist/cjs/constants.cjs +21 -22
  34. package/dist/cjs/constants.native.cjs +41 -0
  35. package/dist/cjs/constants.native.js +21 -21
  36. package/dist/cjs/constants.native.js.map +1 -1
  37. package/dist/cjs/context.cjs +38 -48
  38. package/dist/cjs/context.native.cjs +58 -0
  39. package/dist/cjs/context.native.js +40 -49
  40. package/dist/cjs/context.native.js.map +1 -1
  41. package/dist/cjs/index.cjs +15 -11
  42. package/dist/cjs/index.native.cjs +27 -0
  43. package/dist/cjs/index.native.js +15 -10
  44. package/dist/cjs/index.native.js.map +1 -1
  45. package/dist/cjs/selectionController.cjs +334 -0
  46. package/dist/cjs/selectionController.native.cjs +403 -0
  47. package/dist/cjs/selectionController.native.js +405 -0
  48. package/dist/cjs/selectionController.native.js.map +1 -0
  49. package/dist/cjs/types.cjs +10 -11
  50. package/dist/cjs/types.native.cjs +19 -0
  51. package/dist/cjs/types.native.js +10 -10
  52. package/dist/cjs/types.native.js.map +1 -1
  53. package/dist/esm/Select.mjs +382 -443
  54. package/dist/esm/Select.mjs.map +1 -1
  55. package/dist/esm/Select.native.js +418 -469
  56. package/dist/esm/Select.native.js.map +1 -1
  57. package/dist/esm/SelectContent.mjs +67 -61
  58. package/dist/esm/SelectContent.mjs.map +1 -1
  59. package/dist/esm/SelectContent.native.js +4 -5
  60. package/dist/esm/SelectContent.native.js.map +1 -1
  61. package/dist/esm/SelectImpl.mjs +284 -294
  62. package/dist/esm/SelectImpl.mjs.map +1 -1
  63. package/dist/esm/SelectImpl.native.js +5 -8
  64. package/dist/esm/SelectImpl.native.js.map +1 -1
  65. package/dist/esm/SelectItem.mjs +252 -182
  66. package/dist/esm/SelectItem.mjs.map +1 -1
  67. package/dist/esm/SelectItem.native.js +276 -190
  68. package/dist/esm/SelectItem.native.js.map +1 -1
  69. package/dist/esm/SelectItemText.mjs +33 -55
  70. package/dist/esm/SelectItemText.mjs.map +1 -1
  71. package/dist/esm/SelectItemText.native.js +34 -56
  72. package/dist/esm/SelectItemText.native.js.map +1 -1
  73. package/dist/esm/SelectScrollButton.mjs +95 -107
  74. package/dist/esm/SelectScrollButton.mjs.map +1 -1
  75. package/dist/esm/SelectScrollButton.native.js +11 -7
  76. package/dist/esm/SelectScrollButton.native.js.map +1 -1
  77. package/dist/esm/SelectTrigger.mjs +56 -58
  78. package/dist/esm/SelectTrigger.mjs.map +1 -1
  79. package/dist/esm/SelectTrigger.native.js +54 -44
  80. package/dist/esm/SelectTrigger.native.js.map +1 -1
  81. package/dist/esm/SelectViewport.mjs +104 -119
  82. package/dist/esm/SelectViewport.mjs.map +1 -1
  83. package/dist/esm/SelectViewport.native.js +44 -39
  84. package/dist/esm/SelectViewport.native.js.map +1 -1
  85. package/dist/esm/constants.mjs +1 -0
  86. package/dist/esm/constants.mjs.map +1 -1
  87. package/dist/esm/constants.native.js +1 -0
  88. package/dist/esm/constants.native.js.map +1 -1
  89. package/dist/esm/context.mjs +20 -28
  90. package/dist/esm/context.mjs.map +1 -1
  91. package/dist/esm/context.native.js +23 -31
  92. package/dist/esm/context.native.js.map +1 -1
  93. package/dist/esm/index.js +13 -3
  94. package/dist/esm/index.mjs +13 -3
  95. package/dist/esm/index.native.js +13 -3
  96. package/dist/esm/selectionController.mjs +305 -0
  97. package/dist/esm/selectionController.mjs.map +1 -0
  98. package/dist/esm/selectionController.native.js +372 -0
  99. package/dist/esm/selectionController.native.js.map +1 -0
  100. package/dist/esm/types.mjs +0 -2
  101. package/dist/esm/types.native.js +0 -2
  102. package/dist/jsx/Select.mjs +382 -443
  103. package/dist/jsx/Select.mjs.map +1 -1
  104. package/dist/jsx/Select.native.cjs +495 -0
  105. package/dist/jsx/Select.native.js +445 -492
  106. package/dist/jsx/Select.native.js.map +1 -1
  107. package/dist/jsx/SelectContent.mjs +67 -61
  108. package/dist/jsx/SelectContent.mjs.map +1 -1
  109. package/dist/jsx/SelectContent.native.cjs +30 -0
  110. package/dist/jsx/SelectContent.native.js +18 -22
  111. package/dist/jsx/SelectContent.native.js.map +1 -1
  112. package/dist/jsx/SelectImpl.mjs +284 -294
  113. package/dist/jsx/SelectImpl.mjs.map +1 -1
  114. package/dist/jsx/SelectImpl.native.cjs +30 -0
  115. package/dist/jsx/SelectImpl.native.js +17 -24
  116. package/dist/jsx/SelectImpl.native.js.map +1 -1
  117. package/dist/jsx/SelectItem.mjs +252 -182
  118. package/dist/jsx/SelectItem.mjs.map +1 -1
  119. package/dist/jsx/SelectItem.native.cjs +317 -0
  120. package/dist/jsx/SelectItem.native.js +291 -211
  121. package/dist/jsx/SelectItem.native.js.map +1 -1
  122. package/dist/jsx/SelectItemText.mjs +33 -55
  123. package/dist/jsx/SelectItemText.mjs.map +1 -1
  124. package/dist/jsx/SelectItemText.native.cjs +74 -0
  125. package/dist/jsx/SelectItemText.native.js +50 -79
  126. package/dist/jsx/SelectItemText.native.js.map +1 -1
  127. package/dist/jsx/SelectScrollButton.mjs +95 -107
  128. package/dist/jsx/SelectScrollButton.mjs.map +1 -1
  129. package/dist/jsx/SelectScrollButton.native.cjs +38 -0
  130. package/dist/jsx/SelectScrollButton.native.js +25 -22
  131. package/dist/jsx/SelectScrollButton.native.js.map +1 -1
  132. package/dist/jsx/SelectTrigger.mjs +56 -58
  133. package/dist/jsx/SelectTrigger.mjs.map +1 -1
  134. package/dist/jsx/SelectTrigger.native.cjs +91 -0
  135. package/dist/jsx/SelectTrigger.native.js +71 -66
  136. package/dist/jsx/SelectTrigger.native.js.map +1 -1
  137. package/dist/jsx/SelectViewport.mjs +104 -119
  138. package/dist/jsx/SelectViewport.mjs.map +1 -1
  139. package/dist/jsx/SelectViewport.native.cjs +73 -0
  140. package/dist/jsx/SelectViewport.native.js +54 -50
  141. package/dist/jsx/SelectViewport.native.js.map +1 -1
  142. package/dist/jsx/constants.mjs +1 -0
  143. package/dist/jsx/constants.mjs.map +1 -1
  144. package/dist/jsx/constants.native.cjs +41 -0
  145. package/dist/jsx/constants.native.js +21 -21
  146. package/dist/jsx/constants.native.js.map +1 -1
  147. package/dist/jsx/context.mjs +20 -28
  148. package/dist/jsx/context.mjs.map +1 -1
  149. package/dist/jsx/context.native.cjs +58 -0
  150. package/dist/jsx/context.native.js +40 -49
  151. package/dist/jsx/context.native.js.map +1 -1
  152. package/dist/jsx/index.js +13 -3
  153. package/dist/jsx/index.mjs +13 -3
  154. package/dist/jsx/index.native.cjs +27 -0
  155. package/dist/jsx/index.native.js +15 -10
  156. package/dist/jsx/index.native.js.map +1 -1
  157. package/dist/jsx/selectionController.mjs +305 -0
  158. package/dist/jsx/selectionController.mjs.map +1 -0
  159. package/dist/jsx/selectionController.native.cjs +403 -0
  160. package/dist/jsx/selectionController.native.js +405 -0
  161. package/dist/jsx/selectionController.native.js.map +1 -0
  162. package/dist/jsx/types.mjs +0 -2
  163. package/dist/jsx/types.native.cjs +19 -0
  164. package/dist/jsx/types.native.js +10 -10
  165. package/dist/jsx/types.native.js.map +1 -1
  166. package/package.json +28 -28
  167. package/src/Select.tsx +457 -357
  168. package/src/SelectContent.tsx +46 -16
  169. package/src/SelectImpl.native.tsx +0 -8
  170. package/src/SelectImpl.tsx +87 -100
  171. package/src/SelectItem.tsx +282 -136
  172. package/src/SelectItemText.tsx +16 -33
  173. package/src/SelectScrollButton.native.tsx +13 -4
  174. package/src/SelectScrollButton.tsx +56 -41
  175. package/src/SelectTrigger.tsx +77 -53
  176. package/src/SelectViewport.native.tsx +42 -25
  177. package/src/SelectViewport.tsx +52 -51
  178. package/src/context.tsx +6 -2
  179. package/src/index.tsx +5 -2
  180. package/src/selectionController.ts +389 -0
  181. package/src/types.tsx +88 -45
  182. package/types/Select.d.ts +139 -201
  183. package/types/Select.d.ts.map +1 -1
  184. package/types/SelectContent.d.ts +1 -1
  185. package/types/SelectContent.d.ts.map +1 -1
  186. package/types/SelectContent.native.d.ts.map +1 -1
  187. package/types/SelectImpl.d.ts +2 -1
  188. package/types/SelectImpl.d.ts.map +1 -1
  189. package/types/SelectImpl.native.d.ts +1 -5
  190. package/types/SelectImpl.native.d.ts.map +1 -1
  191. package/types/SelectItem.d.ts +13 -16
  192. package/types/SelectItem.d.ts.map +1 -1
  193. package/types/SelectItemText.d.ts +9 -13
  194. package/types/SelectItemText.d.ts.map +1 -1
  195. package/types/SelectScrollButton.d.ts +15 -2
  196. package/types/SelectScrollButton.d.ts.map +1 -1
  197. package/types/SelectScrollButton.native.d.ts +16 -3
  198. package/types/SelectScrollButton.native.d.ts.map +1 -1
  199. package/types/SelectTrigger.d.ts +11 -23
  200. package/types/SelectTrigger.d.ts.map +1 -1
  201. package/types/SelectViewport.d.ts +11 -38
  202. package/types/SelectViewport.d.ts.map +1 -1
  203. package/types/SelectViewport.native.d.ts +15 -4
  204. package/types/SelectViewport.native.d.ts.map +1 -1
  205. package/types/context.d.ts +2 -0
  206. package/types/context.d.ts.map +1 -1
  207. package/types/index.d.ts +5 -2
  208. package/types/index.d.ts.map +1 -1
  209. package/types/selectionController.d.ts +88 -0
  210. package/types/selectionController.d.ts.map +1 -0
  211. package/types/types.d.ts +47 -37
  212. package/types/types.d.ts.map +1 -1
  213. package/dist/cjs/BubbleSelect.cjs +0 -50
  214. package/dist/cjs/BubbleSelect.native.js +0 -30
  215. package/dist/cjs/BubbleSelect.native.js.map +0 -1
  216. package/dist/cjs/useSelectBreakpointActive.cjs +0 -32
  217. package/dist/cjs/useSelectBreakpointActive.native.js +0 -35
  218. package/dist/cjs/useSelectBreakpointActive.native.js.map +0 -1
  219. package/dist/esm/BubbleSelect.mjs +0 -27
  220. package/dist/esm/BubbleSelect.mjs.map +0 -1
  221. package/dist/esm/BubbleSelect.native.js +0 -4
  222. package/dist/esm/BubbleSelect.native.js.map +0 -1
  223. package/dist/esm/index.js.map +0 -1
  224. package/dist/esm/index.mjs.map +0 -1
  225. package/dist/esm/index.native.js.map +0 -1
  226. package/dist/esm/types.mjs.map +0 -1
  227. package/dist/esm/types.native.js.map +0 -1
  228. package/dist/esm/useSelectBreakpointActive.mjs +0 -7
  229. package/dist/esm/useSelectBreakpointActive.mjs.map +0 -1
  230. package/dist/esm/useSelectBreakpointActive.native.js +0 -7
  231. package/dist/esm/useSelectBreakpointActive.native.js.map +0 -1
  232. package/dist/jsx/BubbleSelect.mjs +0 -27
  233. package/dist/jsx/BubbleSelect.mjs.map +0 -1
  234. package/dist/jsx/BubbleSelect.native.js +0 -30
  235. package/dist/jsx/BubbleSelect.native.js.map +0 -1
  236. package/dist/jsx/index.js.map +0 -1
  237. package/dist/jsx/index.mjs.map +0 -1
  238. package/dist/jsx/types.mjs.map +0 -1
  239. package/dist/jsx/useSelectBreakpointActive.mjs +0 -7
  240. package/dist/jsx/useSelectBreakpointActive.mjs.map +0 -1
  241. package/dist/jsx/useSelectBreakpointActive.native.js +0 -35
  242. package/dist/jsx/useSelectBreakpointActive.native.js.map +0 -1
  243. package/src/BubbleSelect.tsx +0 -50
  244. package/src/useSelectBreakpointActive.tsx +0 -7
  245. package/types/BubbleSelect.d.ts +0 -2
  246. package/types/BubbleSelect.d.ts.map +0 -1
  247. package/types/useSelectBreakpointActive.d.ts +0 -3
  248. package/types/useSelectBreakpointActive.d.ts.map +0 -1
@@ -1,7 +1,8 @@
1
- import { isWeb } from '@tamagui/core'
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 { useShowSelectSheet } from './useSelectBreakpointActive'
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 showSheet = useShowSelectSheet(context)
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 (showSheet) {
38
- if (!context.open) {
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={() => itemParentContext.setOpen(false)}
51
- // prevent focus-outside and pointer-outside from triggering dismiss:
52
- // SelectImpl has its own document pointerdown listener for outside clicks,
53
- // and focus changes during open (e.g. FocusScope trapping) shouldn't dismiss.
54
- // only escape key should trigger onDismiss here.
55
- onFocusOutside={(e) => e.preventDefault()}
56
- onPointerDownOutside={(e) => e.preventDefault()}
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.preventDefault()
95
+ e.cancel()
65
96
  }}
66
97
  onUnmountAutoFocus={(e) => {
67
98
  // return focus to trigger on close
68
- e.preventDefault()
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
- {/* div needed for FocusScope ref, display:contents keeps layout neutral */}
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
  }
@@ -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,43 @@ 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
20
  import { flushSync } from 'react-dom'
22
- import { SCROLL_ARROW_THRESHOLD, WINDOW_PADDING } from './constants'
21
+ import { SCROLL_ARROW_THRESHOLD } from './constants'
23
22
  import {
24
23
  SelectItemParentProvider,
25
24
  SelectProvider,
26
25
  useSelectContext,
27
26
  useSelectItemParentContext,
28
27
  } from './context'
29
- import type { SelectImplProps } from './types'
28
+ import type {
29
+ SelectActiveChangeDetails,
30
+ SelectImplProps,
31
+ SelectOpenChangeDetails,
32
+ } from './types'
30
33
 
31
- // TODO use id for focusing from label
32
34
  export const SelectInlineImpl = (props: SelectImplProps) => {
33
- const { scope, children, open = false, listContentRef, setActiveIndexFast } = props
35
+ const { scope, children, open = false, listContentRef } = props
34
36
 
35
37
  const selectContext = useSelectContext(scope)
36
38
  const selectItemParentContext = useSelectItemParentContext(scope)
37
39
  const { setActiveIndex, selectedIndex, activeIndexRef } = selectContext
38
40
 
39
- const { setOpen, setSelectedIndex } = selectItemParentContext
41
+ const { requestOpenChange, registry } = selectItemParentContext
40
42
 
41
43
  const [scrollTop, setScrollTop] = React.useState(0)
42
44
  const touch = useIsTouchDevice()
43
45
 
44
- const listItemsRef = React.useRef<Array<HTMLElement | null>>([])
46
+ const listItemsRef = selectItemParentContext.listRef!
45
47
  const overflowRef = React.useRef<null | SideObject>(null)
46
48
  const upArrowRef = React.useRef<HTMLDivElement | null>(null)
47
49
  const downArrowRef = React.useRef<HTMLDivElement | null>(null)
48
50
  const allowSelectRef = React.useRef(false)
49
51
  const allowMouseUpRef = React.useRef(true)
50
52
  const selectTimeoutRef = React.useRef<any>(null)
53
+ const latestKeyboardEventRef = React.useRef<Event | undefined>(undefined)
51
54
  const state = React.useRef({
52
55
  isMouseOutside: false,
53
56
  isTyping: false,
@@ -61,15 +64,12 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
61
64
 
62
65
  // sync activeIndex on open/close
63
66
  React.useEffect(() => {
64
- if (open) {
65
- setActiveIndexFast(selectedIndex ?? 0)
66
- } else {
67
+ if (!open) {
67
68
  setScrollTop(0)
68
69
  setFallback(false)
69
- setActiveIndexFast(null)
70
70
  setControlledScrolling(false)
71
71
  }
72
- }, [open, selectedIndex, setActiveIndexFast])
72
+ }, [open])
73
73
 
74
74
  // close when mouseup outside select
75
75
  if (process.env.TAMAGUI_TARGET === 'web') {
@@ -77,7 +77,10 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
77
77
  if (!open) return
78
78
  const mouseUp = (e: MouseEvent) => {
79
79
  if (state.current.isMouseOutside) {
80
- setOpen(false)
80
+ requestOpenChange(
81
+ false,
82
+ createChangeEventDetails('outside-press', e) as SelectOpenChangeDetails
83
+ )
81
84
  }
82
85
  }
83
86
  document.addEventListener('mouseup', mouseUp)
@@ -98,56 +101,36 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
98
101
  open,
99
102
  placement: 'bottom-start',
100
103
  whileElementsMounted: autoUpdate,
101
- // eslint-disable-next-line no-constant-condition
102
- middleware: false
103
- ? // this is the logic from floating-ui
104
- // but i find it causes issues (open, drag select, close, then re-open its not positioned "over")
105
- // https://github.com/floating-ui/floating-ui/blob/master/packages/react/test/visual/components/MacSelect.tsx
106
- [
107
- offset(5),
108
- touch
109
- ? shift({ crossAxis: true, padding: WINDOW_PADDING })
110
- : flip({ padding: WINDOW_PADDING }),
111
- size({
112
- apply({ availableHeight, rects }) {
113
- Object.assign(floatingStyle.current, {
114
- maxHeight: `${availableHeight}px`,
115
- minWidth: `${rects.reference.width}px`,
116
- })
117
- if (refs.floating.current) {
118
- Object.assign(refs.floating.current.style, floatingStyle.current)
119
- }
120
- },
121
- padding: 10,
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
- ],
104
+ // The removed alternate stack was the logic from floating-ui,
105
+ // but it causes issues (open, drag select, close, then re-open its not positioned "over")
106
+ // https://github.com/floating-ui/floating-ui/blob/master/packages/react/test/visual/components/MacSelect.tsx
107
+ middleware: [
108
+ size({
109
+ apply({
110
+ rects: {
111
+ reference: { width },
112
+ },
113
+ }) {
114
+ Object.assign(floatingStyle.current, {
115
+ minWidth: width + 8,
116
+ })
117
+ if (refs.floating.current) {
118
+ Object.assign(refs.floating.current.style, floatingStyle.current)
119
+ }
120
+ },
121
+ }),
122
+ inner({
123
+ listRef: listItemsRef,
124
+ overflowRef,
125
+ index: selectedIndex,
126
+ offset: innerOffset,
127
+ onFallbackChange: setFallback,
128
+ padding: 10,
129
+ minItemsVisible: touch ? 10 : 4,
130
+ referenceOverflowThreshold: 20,
131
+ }),
132
+ offset({ crossAxis: -5 }),
133
+ ],
151
134
  } as any)
152
135
 
153
136
  const floatingRef = refs.floating
@@ -173,8 +156,13 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
173
156
  }, [update, open])
174
157
 
175
158
  const onMatch = useEvent((index: number) => {
176
- const fn = open ? setActiveIndex : setSelectedIndex
177
- return fn(index)
159
+ if (!open) return
160
+ setActiveIndex(index, {
161
+ reason: 'keyboard',
162
+ event: latestKeyboardEventRef.current,
163
+ trigger: undefined,
164
+ index,
165
+ } as SelectActiveChangeDetails)
178
166
  })
179
167
 
180
168
  // construct interaction context for our custom hooks
@@ -182,7 +170,16 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
182
170
  dataRef.current.placement = computedPlacement
183
171
  const interactionContext: FloatingInteractionContext = {
184
172
  open,
185
- onOpenChange: (val) => setOpen(val),
173
+ onOpenChange: (nextOpen, event, reason) => {
174
+ requestOpenChange(
175
+ nextOpen,
176
+ createChangeEventDetails(
177
+ reason === 'list-navigation' ? 'keyboard' : 'trigger-press',
178
+ event,
179
+ refs.reference.current
180
+ ) as SelectOpenChangeDetails
181
+ )
182
+ },
186
183
  refs: {
187
184
  reference: refs.reference as any,
188
185
  floating: refs.floating,
@@ -212,9 +209,15 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
212
209
  selectedIndex,
213
210
  onNavigate: (index) => {
214
211
  if (index !== null) {
215
- setActiveIndex(index)
212
+ setActiveIndex(index, {
213
+ reason: 'list-navigation',
214
+ event: latestKeyboardEventRef.current,
215
+ trigger: undefined,
216
+ index,
217
+ } as SelectActiveChangeDetails)
216
218
  }
217
219
  },
220
+ disabledIndices: registry.getDisabledIndices(),
218
221
  scrollItemIntoView: false,
219
222
  }),
220
223
  useTypeahead(interactionContext, {
@@ -237,20 +240,27 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
237
240
  const interactionsContext = React.useMemo(() => {
238
241
  return {
239
242
  ...interactions,
240
- getReferenceProps() {
243
+ getReferenceProps(props: Record<string, any> = {}) {
241
244
  return interactions.getReferenceProps({
242
- ref: refs.reference as any,
243
- className: 'SelectTrigger',
244
- onKeyDown(event) {
245
+ ...props,
246
+ onKeyDown: composeEventHandlers(props.onKeyDown, (event: any) => {
247
+ latestKeyboardEventRef.current = event.nativeEvent || event
245
248
  if (
246
249
  event.key === 'Enter' ||
247
250
  event.code === 'Space' ||
248
251
  (event.key === ' ' && !state.current.isTyping)
249
252
  ) {
250
253
  event.preventDefault()
251
- setOpen(true)
254
+ requestOpenChange(
255
+ true,
256
+ createChangeEventDetails(
257
+ 'keyboard',
258
+ event.nativeEvent || event,
259
+ refs.reference.current
260
+ ) as SelectOpenChangeDetails
261
+ )
252
262
  }
253
- },
263
+ }),
254
264
  })
255
265
  },
256
266
  getFloatingProps(props) {
@@ -278,7 +288,8 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
278
288
  state.current.isMouseOutside = false
279
289
  setControlledScrolling(false)
280
290
  },
281
- onKeyDown() {
291
+ onKeyDown(event) {
292
+ latestKeyboardEventRef.current = event.nativeEvent || event
282
293
  setControlledScrolling(true)
283
294
  },
284
295
  onContextMenu(e) {
@@ -322,30 +333,6 @@ export const SelectInlineImpl = (props: SelectImplProps) => {
322
333
  }
323
334
  }, [open])
324
335
 
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
336
  // scroll activeIndex into view during keyboard nav
350
337
  React.useEffect(() => {
351
338
  if (!open) return