diff --git a/README.md b/README.md index eb0f265..3b67a0f 100644 --- a/README.md +++ b/README.md @@ -127,13 +127,14 @@ function MyPicker() { the main Picker container component. -| Prop | Default | Description | -| :---- | :------- | :----------- | -| value | N/A | `{ [name: string]: string }`
Selected value pairs | -| onChange | N/A | `(value: T, key: string) => void`
Callback function when the selected value changes | -| height | 216 | `number`
Height of the picker in `px` | -| itemHeight | 36 | `number`
Height of each item (that is each option) in `px` | -| wheelMode | `'off'` | `'off' \| 'natural' \| 'normal'`
Enable wheel scrolling on desktop browsers | +| Prop | Default | Description | +|:-----------|:----------|:-----------------------------------------------------------------------------------------| +| value | N/A | `{ [name: string]: string }`
Selected value pairs | +| onChange | N/A | `(value: T, key: string) => void`
Callback function when the selected value changes | +| height | 216 | `number`
Height of the picker in `px` | +| itemHeight | 36 | `number`
Height of each item (that is each option) in `px` | +| wheelMode | `'off'` | `'off' \| 'natural' \| 'normal'`
Enable wheel scrolling on desktop browsers | +| mouseMode | `'click'` | `'click' \| 'drag'`
How to select an item with a mouse — by click or drag. | ### Picker.Column diff --git a/examples/containers/InlinePicker.tsx b/examples/containers/InlinePicker.tsx index 7915616..575f74b 100644 --- a/examples/containers/InlinePicker.tsx +++ b/examples/containers/InlinePicker.tsx @@ -30,6 +30,7 @@ export default function InlinePicker() { value={pickerValue} onChange={setPickerValue} wheelMode="natural" + mouseMode="drag" > {renderOptions(['Mr.', 'Mrs.', 'Ms.', 'Dr.'], 'text-red-600')} diff --git a/lib/components/Picker.tsx b/lib/components/Picker.tsx index 56c4b19..9315bdb 100644 --- a/lib/components/Picker.tsx +++ b/lib/components/Picker.tsx @@ -3,6 +3,7 @@ import { CSSProperties, HTMLProps, MutableRefObject, createContext, useCallback, const DEFAULT_HEIGHT = 216 const DEFAULT_ITEM_HEIGHT = 36 const DEFAULT_WHEEL_MODE = 'off' +const DEFAULT_MOUSE_MODE = 'click' interface Option { value: string | number @@ -19,12 +20,14 @@ export interface PickerRootProps extends Omit(null) @@ -118,6 +121,7 @@ function PickerRoot(props: PickerRootProps) { height = DEFAULT_HEIGHT, itemHeight = DEFAULT_ITEM_HEIGHT, wheelMode = DEFAULT_WHEEL_MODE, + mouseMode= DEFAULT_MOUSE_MODE, ...restProps } = props @@ -149,8 +153,8 @@ function PickerRoot(props: PickerRootProps) { const [optionGroups, dispatch] = useReducer(pickerReducer, {}) const pickerData = useMemo( - () => ({ height, itemHeight, wheelMode, value, optionGroups }), - [height, itemHeight, value, optionGroups, wheelMode] + () => ({ height, itemHeight, wheelMode, value, optionGroups, mouseMode }), + [height, itemHeight, value, optionGroups, wheelMode, mouseMode] ) const triggerChange = useCallback((key: string, nextValue: string) => { diff --git a/lib/components/PickerColumn.tsx b/lib/components/PickerColumn.tsx index bc5cb51..4daf7c9 100644 --- a/lib/components/PickerColumn.tsx +++ b/lib/components/PickerColumn.tsx @@ -28,7 +28,7 @@ function PickerColumn({ name: key, ...restProps }: PickerColumnProps) { - const { height, itemHeight, wheelMode, value: groupValue, optionGroups } = usePickerData('Picker.Column') + const { height, itemHeight, wheelMode, value: groupValue, optionGroups, mouseMode } = usePickerData('Picker.Column') // Caculate the selected index const value = useMemo( @@ -104,6 +104,21 @@ function PickerColumn({ setStartScrollerTranslate(scrollerTranslate) }, [scrollerTranslate]) + const handleMouseStart = useCallback((event: React.MouseEvent) => { + if (mouseMode === 'click') return + + setIsMoving(true) + setStartTouchY(event.pageY) + setStartScrollerTranslate(scrollerTranslate) + }, [scrollerTranslate]) + + const handleMouseMove = useCallback((event: MouseEvent) => { + if (event.cancelable) event.preventDefault() + if (!isMoving || mouseMode === 'click') return + + updateScrollerWhileMoving(startScrollerTranslate + event.pageY - startTouchY) + }, [isMoving, startScrollerTranslate, startTouchY, updateScrollerWhileMoving]) + const handleTouchMove = useCallback((event: TouchEvent) => { if (event.cancelable) { event.preventDefault() @@ -188,11 +203,13 @@ function PickerColumn({ if (container) { container.addEventListener('touchmove', handleTouchMove, { passive: false }) container.addEventListener('wheel', handleWheel, { passive: false }) + container.addEventListener('mousemove', handleMouseMove, { passive: false }) } return () => { if (container) { container.removeEventListener('touchmove', handleTouchMove) container.removeEventListener('wheel', handleWheel) + container.removeEventListener('mousemove', handleMouseMove) } } }, [handleTouchMove, handleWheel]) @@ -205,6 +222,7 @@ function PickerColumn({ transitionTimingFunction: 'cubic-bezier(0, 0, 0.2, 1)', transitionDuration: isMoving ? '0ms' : '300ms', transform: `translate3d(0, ${scrollerTranslate}px, 0)`, + ...(mouseMode === 'drag' && { cursor: 'grab', userSelect: 'none' }), }), [scrollerTranslate, isMoving], ) @@ -221,6 +239,9 @@ function PickerColumn({ ...style, }} ref={containerRef} + onMouseDown={handleMouseStart} + onMouseUp={handleTouchEnd} + onMouseLeave={handleTouchCancel} onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd} onTouchCancel={handleTouchCancel} diff --git a/lib/components/PickerItem.tsx b/lib/components/PickerItem.tsx index 75f916d..21ae538 100644 --- a/lib/components/PickerItem.tsx +++ b/lib/components/PickerItem.tsx @@ -23,7 +23,7 @@ function PickerItem({ ...restProps }: PickerItemProps) { const optionRef = useRef(null) - const { itemHeight, value: pickerValue } = usePickerData('Picker.Item') + const { itemHeight, value: pickerValue, mouseMode } = usePickerData('Picker.Item') const pickerActions = usePickerActions('Picker.Item') const { key } = useColumnData('Picker.Item') @@ -43,6 +43,7 @@ function PickerItem({ ) const handleClick = useCallback(() => { + if (mouseMode === 'drag') return pickerActions.change(key, value) }, [pickerActions, key, value])