@@ -41,22 +41,24 @@ import {edgeToText} from '../style/spectrum-theme' with {type: 'macro'};
4141import {
4242 FieldLabel
4343} from './Field' ;
44- import { FocusableRef , FocusableRefValue , SpectrumLabelableProps } from '@react-types/shared' ;
44+ import { FocusableRef , FocusableRefValue , PressEvent , SpectrumLabelableProps } from '@react-types/shared' ;
4545import { forwardRefType } from './types' ;
4646import { HeaderContext , HeadingContext , Text , TextContext } from './Content' ;
4747import { IconContext } from './Icon' ;
4848import { Placement , useLocale } from 'react-aria' ;
4949import { Popover } from './Popover' ;
50+ import { PressResponder } from '@react-aria/interactions' ;
5051import { pressScale } from './pressScale' ;
5152import { raw } from '../style/style-macro' with { type : 'macro' } ;
52- import React , { createContext , forwardRef , ReactNode , useContext , useRef } from 'react' ;
53+ import React , { createContext , forwardRef , ReactNode , useContext , useRef , useState } from 'react' ;
5354import { useFocusableRef } from '@react-spectrum/utils' ;
5455import { useFormProps } from './Form' ;
56+ import { useGlobalListeners } from '@react-aria/utils' ;
5557import { useSpectrumContextProps } from './useSpectrumContextProps' ;
5658export interface PickerStyleProps {
5759}
5860export interface PickerProps < T extends object > extends
59- Omit < AriaSelectProps < T > , 'children' | 'style' | 'className' | 'placeholder' > ,
61+ Omit < AriaSelectProps < T > , 'children' | 'style' | 'className' | 'placeholder' | 'isTriggerUpWhenOpen' > ,
6062 PickerStyleProps ,
6163 StyleProps ,
6264 SpectrumLabelableProps ,
@@ -182,70 +184,85 @@ function Picker<T extends object>(props: PickerProps<T>, ref: FocusableRef<HTMLB
182184 let { direction : dir } = useLocale ( ) ;
183185 let RTLFlipOffset = dir === 'rtl' ? - 1 : 1 ;
184186
187+ // For mouse interactions, pickers open on press start. When the popover underlay appears
188+ // it covers the trigger button, causing onPressEnd to fire immediately and no press scaling
189+ // to occur. We override this by listening for pointerup on the document ourselves.
190+ let [ isPressed , setPressed ] = useState ( false ) ;
191+ let { addGlobalListener} = useGlobalListeners ( ) ;
192+ let onPressStart = ( e : PressEvent ) => {
193+ if ( e . pointerType !== 'mouse' ) {
194+ return ;
195+ }
196+ setPressed ( true ) ;
197+ addGlobalListener ( document , 'pointerup' , ( ) => {
198+ setPressed ( false ) ;
199+ } , { once : true , capture : true } ) ;
200+ } ;
201+
185202 return (
186203 < div >
187204 < AriaSelect
188205 { ...pickerProps }
206+ isTriggerUpWhenOpen
189207 className = ""
190208 aria-labelledby = { `${ labelBehavior === 'hide' ? valueId : '' } ${ ariaLabelledby } ` } >
191209 { ( { isOpen} ) => (
192210 < >
193211 < FieldLabel isQuiet = { isQuiet } />
194- < Button
195- ref = { domRef }
196- style = { renderProps => pressScale ( domRef ) ( renderProps ) }
197- // Prevent press scale from sticking while Picker is open.
198- // @ts -ignore
199- isPressed = { false }
200- className = { renderProps => inputButton ( {
201- ...renderProps ,
202- size : 'M' ,
203- isOpen,
204- isQuiet,
205- density
206- } ) } >
207- < SelectValue className = { valueStyles + ' ' + raw ( '&> * {display: none;}' ) } >
208- { ( { defaultChildren} ) => {
209- return (
210- < Provider
211- values = { [
212- [ IconContext , {
213- slots : {
214- icon : {
215- render : centerBaseline ( { slot : 'icon' , styles : iconCenterWrapper ( { labelBehavior} ) } ) ,
216- styles : icon
212+ < PressResponder onPressStart = { onPressStart } isPressed = { isPressed } >
213+ < Button
214+ ref = { domRef }
215+ style = { renderProps => pressScale ( domRef ) ( renderProps ) }
216+ className = { renderProps => inputButton ( {
217+ ...renderProps ,
218+ size : 'M' ,
219+ isOpen,
220+ isQuiet,
221+ density
222+ } ) } >
223+ < SelectValue className = { valueStyles + ' ' + raw ( '&> * {display: none;}' ) } >
224+ { ( { defaultChildren} ) => {
225+ return (
226+ < Provider
227+ values = { [
228+ [ IconContext , {
229+ slots : {
230+ icon : {
231+ render : centerBaseline ( { slot : 'icon' , styles : iconCenterWrapper ( { labelBehavior} ) } ) ,
232+ styles : icon
233+ }
217234 }
218- }
219- } ] ,
220- [ TextContext , {
221- slots : {
222- // Default slot is useful when converting other collections to PickerItems.
223- [ DEFAULT_SLOT ] : {
224- id : valueId ,
225- styles : style ( {
226- display : {
227- default : 'block' ,
228- labelBehavior : {
229- hide : 'none'
230- }
231- } ,
232- flexGrow : 1 ,
233- truncate : true
234- } ) ( { labelBehavior } )
235+ } ] ,
236+ [ TextContext , {
237+ slots : {
238+ // Default slot is useful when converting other collections to PickerItems.
239+ [ DEFAULT_SLOT ] : {
240+ id : valueId ,
241+ styles : style ( {
242+ display : {
243+ default : 'block' ,
244+ labelBehavior : {
245+ hide : 'none'
246+ }
247+ } ,
248+ flexGrow : 1 ,
249+ truncate : true
250+ } ) ( { labelBehavior } )
251+ }
235252 }
236- }
237- } ] ,
238- [ InsideSelectValueContext , true ]
239- ] } >
240- { defaultChildren }
241- </ Provider >
242- ) ;
243- } }
244- </ SelectValue >
245- < ChevronIcon
246- size = { size }
247- className = { iconStyles } / >
248- </ Button >
253+ } ] ,
254+ [ InsideSelectValueContext , true ]
255+ ] } >
256+ { defaultChildren }
257+ </ Provider >
258+ ) ;
259+ } }
260+ </ SelectValue >
261+ < ChevronIcon
262+ size = { size }
263+ className = { iconStyles } />
264+ </ Button >
265+ </ PressResponder >
249266 < Popover
250267 hideArrow
251268 offset = { menuOffset }
0 commit comments