From 41fd430709d3a01b418b71ef2774091b5dc45173 Mon Sep 17 00:00:00 2001 From: Vlad Moroz Date: Wed, 5 Jun 2024 11:56:26 +0200 Subject: [PATCH] Update the Select --- packages/react/select/src/Select.tsx | 46 ++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/react/select/src/Select.tsx b/packages/react/select/src/Select.tsx index 2707811b0..bc8c245c1 100644 --- a/packages/react/select/src/Select.tsx +++ b/packages/react/select/src/Select.tsx @@ -220,6 +220,7 @@ const SelectTrigger = React.forwardRef const isDisabled = context.disabled || disabled; const composedRefs = useComposedRefs(forwardedRef, context.onTriggerChange); const getItems = useCollection(__scopeSelect); + const pointerTypeRef = React.useRef('touch'); const [searchRef, handleTypeaheadSearch, resetTypeahead] = useTypeaheadSearch((search) => { const enabledItems = getItems().filter((item) => !item.disabled); @@ -230,12 +231,19 @@ const SelectTrigger = React.forwardRef } }); - const handleOpen = () => { + const handleOpen = (pointerEvent?: React.MouseEvent | React.PointerEvent) => { if (!isDisabled) { context.onOpenChange(true); // reset typeahead when we open resetTypeahead(); } + + if (pointerEvent) { + context.triggerPointerDownPosRef.current = { + x: Math.round(pointerEvent.pageX), + y: Math.round(pointerEvent.pageY), + }; + } }; return ( @@ -262,8 +270,15 @@ const SelectTrigger = React.forwardRef // because we are preventing default in `onPointerDown` so effectively // this only runs for a label "click" event.currentTarget.focus(); + + // Open on click when using a touch or pen device + if (pointerTypeRef.current !== 'mouse') { + handleOpen(event); + } })} onPointerDown={composeEventHandlers(triggerProps.onPointerDown, (event) => { + pointerTypeRef.current = event.pointerType; + // prevent implicit pointer capture // https://www.w3.org/TR/pointerevents3/#implicit-pointer-capture const target = event.target as HTMLElement; @@ -272,13 +287,10 @@ const SelectTrigger = React.forwardRef } // only call handler if it's the left button (mousedown gets triggered by all mouse buttons) - // but not when the control key is pressed (avoiding MacOS right click) - if (event.button === 0 && event.ctrlKey === false) { - handleOpen(); - context.triggerPointerDownPosRef.current = { - x: Math.round(event.pageX), - y: Math.round(event.pageY), - }; + // but not when the control key is pressed (avoiding MacOS right click); also not for touch + // devices because that would open the menu on scroll. (pen devices behave as touch on iOS). + if (event.button === 0 && event.ctrlKey === false && event.pointerType === 'mouse') { + handleOpen(event); // prevent trigger from stealing focus from the active item after opening. event.preventDefault(); } @@ -1197,6 +1209,7 @@ const SelectItem = React.forwardRef( contentContext.itemRefCallback?.(node, value, disabled) ); const textId = useId(); + const pointerTypeRef = React.useRef('touch'); const handleSelect = () => { if (!disabled) { @@ -1242,11 +1255,24 @@ const SelectItem = React.forwardRef( ref={composedRefs} onFocus={composeEventHandlers(itemProps.onFocus, () => setIsFocused(true))} onBlur={composeEventHandlers(itemProps.onBlur, () => setIsFocused(false))} - onPointerUp={composeEventHandlers(itemProps.onPointerUp, handleSelect)} + onClick={composeEventHandlers(itemProps.onClick, () => { + // Open on click when using a touch or pen device + if (pointerTypeRef.current !== 'mouse') handleSelect(); + })} + onPointerUp={composeEventHandlers(itemProps.onPointerUp, () => { + // Using a mouse you should be able to do pointer down, move through + // the list, and release the pointer over the item to select it. + if (pointerTypeRef.current === 'mouse') handleSelect(); + })} + onPointerDown={composeEventHandlers(itemProps.onPointerDown, (event) => { + pointerTypeRef.current = event.pointerType; + })} onPointerMove={composeEventHandlers(itemProps.onPointerMove, (event) => { + // Remember pointer type when sliding over to this item from another one + pointerTypeRef.current = event.pointerType; if (disabled) { contentContext.onItemLeave?.(); - } else { + } else if (pointerTypeRef.current === 'mouse') { // even though safari doesn't support this option, it's acceptable // as it only means it might scroll a few pixels when using the pointer. event.currentTarget.focus({ preventScroll: true });