Skip to content

Commit 77b4b62

Browse files
committed
fix delay when opening many items S2 select
1 parent fac1920 commit 77b4b62

File tree

1 file changed

+103
-76
lines changed

1 file changed

+103
-76
lines changed

packages/@react-spectrum/s2/src/Picker.tsx

Lines changed: 103 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import {
5353
FieldLabel,
5454
HelpText
5555
} from './Field';
56-
import {FocusableRef, FocusableRefValue, HelpTextProps, PressEvent, SpectrumLabelableProps} from '@react-types/shared';
56+
import {FocusableRef, FocusableRefValue, HelpTextProps, PressEvent, RefObject, SpectrumLabelableProps} from '@react-types/shared';
5757
import {FormContext, useFormProps} from './Form';
5858
import {forwardRefType} from './types';
5959
import {HeaderContext, HeadingContext, Text, TextContext} from './Content';
@@ -278,21 +278,6 @@ export const Picker = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pick
278278
menuOffset = 8;
279279
}
280280

281-
// For mouse interactions, pickers open on press start. When the popover underlay appears
282-
// it covers the trigger button, causing onPressEnd to fire immediately and no press scaling
283-
// to occur. We override this by listening for pointerup on the document ourselves.
284-
let [isPressed, setPressed] = useState(false);
285-
let {addGlobalListener} = useGlobalListeners();
286-
let onPressStart = (e: PressEvent) => {
287-
if (e.pointerType !== 'mouse') {
288-
return;
289-
}
290-
setPressed(true);
291-
addGlobalListener(document, 'pointerup', () => {
292-
setPressed(false);
293-
}, {once: true, capture: true});
294-
};
295-
296281
return (
297282
<AriaSelect
298283
{...pickerProps}
@@ -317,66 +302,14 @@ export const Picker = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pick
317302
contextualHelp={props.contextualHelp}>
318303
{label}
319304
</FieldLabel>
320-
<PressResponder onPressStart={onPressStart} isPressed={isPressed}>
321-
<Button
322-
ref={domRef}
323-
style={renderProps => pressScale(domRef)(renderProps)}
324-
// Prevent press scale from sticking while Picker is open.
325-
// @ts-ignore
326-
isPressed={false}
327-
className={renderProps => inputButton({
328-
...renderProps,
329-
size: size,
330-
isOpen,
331-
isQuiet
332-
})}>
333-
{(renderProps) => (
334-
<>
335-
<SelectValue className={valueStyles({isQuiet}) + ' ' + raw('&> * {display: none;}')}>
336-
{({defaultChildren}) => {
337-
return (
338-
<Provider
339-
values={[
340-
[IconContext, {
341-
slots: {
342-
icon: {
343-
render: centerBaseline({slot: 'icon', styles: iconCenterWrapper}),
344-
styles: icon
345-
}
346-
}
347-
}],
348-
[TextContext, {
349-
slots: {
350-
description: {},
351-
label: {styles: style({
352-
display: 'block',
353-
flexGrow: 1,
354-
truncate: true
355-
})}
356-
}
357-
}],
358-
[InsideSelectValueContext, true]
359-
]}>
360-
{defaultChildren}
361-
</Provider>
362-
);
363-
}}
364-
</SelectValue>
365-
{isInvalid && (
366-
<FieldErrorIcon isDisabled={isDisabled} />
367-
)}
368-
<ChevronIcon
369-
size={size}
370-
className={iconStyles} />
371-
{isFocusVisible && isQuiet && <span className={quietFocusLine} /> }
372-
{isInvalid && !isDisabled && !isQuiet &&
373-
// @ts-ignore known limitation detecting functions from the theme
374-
<div className={invalidBorder({...renderProps, size})} />
375-
}
376-
</>
377-
)}
378-
</Button>
379-
</PressResponder>
305+
<PickerButton
306+
isOpen={isOpen}
307+
isQuiet={isQuiet}
308+
isFocusVisible={isFocusVisible}
309+
size={size}
310+
isInvalid={isInvalid}
311+
isDisabled={isDisabled}
312+
buttonRef={domRef} />
380313
<HelpText
381314
size={size}
382315
isDisabled={isDisabled}
@@ -437,6 +370,100 @@ export const Picker = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pick
437370
);
438371
});
439372

373+
interface PickerButtonInnerProps extends PickerStyleProps, Omit<AriaSelectRenderProps, 'isRequired' | 'isFocused'> {
374+
buttonRef: RefObject<HTMLButtonElement | null>
375+
}
376+
377+
function PickerButton(props: PickerButtonInnerProps) {
378+
let {
379+
isOpen,
380+
isQuiet,
381+
isFocusVisible,
382+
size,
383+
isInvalid,
384+
isDisabled,
385+
buttonRef
386+
} = props;
387+
388+
// For mouse interactions, pickers open on press start. When the popover underlay appears
389+
// it covers the trigger button, causing onPressEnd to fire immediately and no press scaling
390+
// to occur. We override this by listening for pointerup on the document ourselves.
391+
let [isPressed, setPressed] = useState(false);
392+
let {addGlobalListener} = useGlobalListeners();
393+
let onPressStart = (e: PressEvent) => {
394+
if (e.pointerType !== 'mouse') {
395+
return;
396+
}
397+
setPressed(true);
398+
addGlobalListener(document, 'pointerup', () => {
399+
setPressed(false);
400+
}, {once: true, capture: true});
401+
};
402+
403+
return (
404+
<PressResponder onPressStart={onPressStart} isPressed={isPressed}>
405+
<Button
406+
ref={buttonRef}
407+
style={renderProps => pressScale(buttonRef)(renderProps)}
408+
// Prevent press scale from sticking while Picker is open.
409+
// @ts-ignore
410+
isPressed={false}
411+
className={renderProps => inputButton({
412+
...renderProps,
413+
size: size,
414+
isOpen,
415+
isQuiet
416+
})}>
417+
{(renderProps) => (
418+
<>
419+
<SelectValue className={valueStyles({isQuiet}) + ' ' + raw('&> * {display: none;}')}>
420+
{({defaultChildren}) => {
421+
return (
422+
<Provider
423+
values={[
424+
[IconContext, {
425+
slots: {
426+
icon: {
427+
render: centerBaseline({slot: 'icon', styles: iconCenterWrapper}),
428+
styles: icon
429+
}
430+
}
431+
}],
432+
[TextContext, {
433+
slots: {
434+
description: {},
435+
label: {styles: style({
436+
display: 'block',
437+
flexGrow: 1,
438+
truncate: true
439+
})}
440+
}
441+
}],
442+
[InsideSelectValueContext, true]
443+
]}>
444+
{defaultChildren}
445+
</Provider>
446+
);
447+
}}
448+
</SelectValue>
449+
{isInvalid && (
450+
<FieldErrorIcon isDisabled={isDisabled} />
451+
)}
452+
<ChevronIcon
453+
size={size}
454+
className={iconStyles} />
455+
{isFocusVisible && isQuiet && <span className={quietFocusLine} /> }
456+
{isInvalid && !isDisabled && !isQuiet &&
457+
// @ts-ignore known limitation detecting functions from the theme
458+
<div className={invalidBorder({...renderProps, size})} />
459+
}
460+
</>
461+
)}
462+
</Button>
463+
</PressResponder>
464+
);
465+
}
466+
440467
export interface PickerItemProps extends Omit<ListBoxItemProps, 'children' | 'style' | 'className'>, StyleProps {
441468
children: ReactNode
442469
}

0 commit comments

Comments
 (0)