Skip to content

Commit 27c5cfd

Browse files
authored
feat: (Picker & PickerView) add mouseWheel prop (#5052)
2 parents 90778df + c130df9 commit 27c5cfd

File tree

10 files changed

+86
-35
lines changed

10 files changed

+86
-35
lines changed

src/components/date-picker-view/date-picker-view.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import type {
1515
DatePickerFilter,
1616
} from '../date-picker/date-picker-utils'
1717

18-
export type DatePickerViewProps = Pick<PickerViewProps, 'style'> & {
18+
export type DatePickerViewProps = Pick<
19+
PickerViewProps,
20+
'style' | 'mouseWheel'
21+
> & {
1922
value?: Date
2023
defaultValue?: Date
2124
onChange?: (value: Date) => void
@@ -73,6 +76,7 @@ export const DatePickerView: FC<DatePickerViewProps> = p => {
7376
)
7477
}
7578
value={pickerValue}
79+
mouseWheel={props.mouseWheel}
7680
onChange={onChange}
7781
/>
7882
)

src/components/date-picker/date-picker.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export type DatePickerProps = Pick<
2929
| 'title'
3030
| 'stopPropagation'
3131
| 'style'
32+
| 'mouseWheel'
3233
> & {
3334
value?: Date | null
3435
defaultValue?: Date | null
@@ -117,6 +118,7 @@ export const DatePicker: FC<DatePickerProps> = p => {
117118
onClick={props.onClick}
118119
title={props.title}
119120
stopPropagation={props.stopPropagation}
121+
mouseWheel={props.mouseWheel}
120122
>
121123
{() => props.children?.(value)}
122124
</Picker>

src/components/picker-view/demos/demo1.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export default () => {
2929
}}
3030
/>
3131
</DemoBlock>
32+
33+
<DemoBlock title='通过鼠标滚轮进行选择' padding='0'>
34+
<PickerView columns={basicColumns} mouseWheel={true} />
35+
</DemoBlock>
3236
</>
3337
)
3438
}

src/components/picker-view/index.en.md

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ PickerView is the content area of [Picker](./picker/#picker).
1717
| defaultValue | Default selected options | `PickerValue[]` | `[]` |
1818
| onChange | Triggered when the options are changed | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
1919
| renderLabel | The function to custom rendering the label shown on a column | `(item: PickerColumnItem) => ReactNode` | `(item) => item.label` |
20+
| mouseWheel | Whether to allow interact with mouse wheel | `boolean` | `false` |
2021

2122
For the type definition of `PickerColumnItem` `PickerColumn` `PickerValue` `PickerValueExtend`, please refer to the document of [Picker](./picker).
2223

@@ -70,6 +71,7 @@ DatePickerView is the content area of [DatePicker](./picker/#datepicker).
7071
| precision | Precision | `'year' \| 'month' \| 'day' \| 'hour' \| 'minute' \| 'second' \| 'week' \| 'week-day'` | `'day'` |
7172
| renderLabel | The function to custom rendering the label shown on a column. `type` means any value in `precision`, `data` means the default number | `(type: string, data: number) => ReactNode` | - |
7273
| filter | Filter available time | `DatePickerFilter` | - |
74+
| mouseWheel | Whether to allow interact with mouse wheel | `boolean` | `false` |
7375

7476
For the type definition and usage of `DatePickerFilter`, please refer to the document of [DatePicker](./picker#datepicker).
7577

src/components/picker-view/index.zh.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ PickerView 是 [Picker](./picker/#picker) 的内容区域。
1010

1111
### 属性
1212

13-
| 属性 | 说明 | 类型 | 默认值 |
14-
| ------------ | ------------------------ | -------------------------------------------------------------- | ---------------------- |
15-
| columns | 配置每一列的选项 | `PickerColumn[] \| ((value: PickerValue[]) => PickerColumn[])` | - |
16-
| value | 选中项 | `PickerValue[]` | - |
17-
| defaultValue | 默认选中项 | `PickerValue[]` | `[]` |
18-
| onChange | 选项改变时触发 | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
19-
| renderLabel | 自定义渲染每列展示的内容 | `(item: PickerColumnItem) => ReactNode` | `(item) => item.label` |
13+
| 属性 | 说明 | 类型 | 默认值 |
14+
| ------------ | ---------------------------- | -------------------------------------------------------------- | ---------------------- |
15+
| columns | 配置每一列的选项 | `PickerColumn[] \| ((value: PickerValue[]) => PickerColumn[])` | - |
16+
| value | 选中项 | `PickerValue[]` | - |
17+
| defaultValue | 默认选中项 | `PickerValue[]` | `[]` |
18+
| onChange | 选项改变时触发 | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
19+
| renderLabel | 自定义渲染每列展示的内容 | `(item: PickerColumnItem) => ReactNode` | `(item) => item.label` |
20+
| mouseWheel | 是否允许通过鼠标滚轮进行选择 | `boolean` | `false` |
2021

2122
关于 `PickerColumnItem` `PickerColumn` `PickerValue` `PickerValueExtend` 的类型定义,请参考 [Picker](./picker) 的文档。
2223

@@ -70,6 +71,7 @@ DatePickerView 是 [DatePicker](./picker/#datepicker) 的内容区域。
7071
| precision | 精度 | `'year' \| 'month' \| 'day' \| 'hour' \| 'minute' \| 'second' \| 'week' \| 'week-day'` | `'day'` |
7172
| renderLabel | 自定义渲染每列展示的内容。其中 `type` 参数为 `precision` 中的任意值,`data` 参数为默认渲染的数字 | `(type: string, data: number) => ReactNode` | - |
7273
| filter | 过滤可供选择的时间 | `DatePickerFilter` | - |
74+
| mouseWheel | 是否允许通过鼠标滚轮进行选择 | `boolean` | `false` |
7375

7476
关于 `DatePickerFilter` 的类型定义和使用,请参考 [DatePicker](./picker#datepicker) 的文档。
7577

src/components/picker-view/picker-view.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ export type PickerViewProps = {
2828
columns: PickerColumn[] | ((value: PickerValue[]) => PickerColumn[])
2929
value?: PickerValue[]
3030
defaultValue?: PickerValue[]
31+
mouseWheel?: boolean
3132
onChange?: (value: PickerValue[], extend: PickerValueExtend) => void
3233
} & Pick<PickerProps, 'renderLabel'> &
3334
NativeProps<'--height' | '--item-height' | '--item-font-size'>
3435

3536
const defaultProps = {
3637
defaultValue: [],
3738
renderLabel: defaultRenderLabel,
39+
mouseWheel: false,
3840
}
3941

4042
export const PickerView = memo<PickerViewProps>(p => {
@@ -98,6 +100,7 @@ export const PickerView = memo<PickerViewProps>(p => {
98100
value={innerValue[index]}
99101
onSelect={handleSelect}
100102
renderLabel={props.renderLabel}
103+
mouseWheel={props.mouseWheel}
101104
/>
102105
))}
103106
<div className={`${classPrefix}-mask`}>

src/components/picker-view/wheel.tsx

+55-27
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import React, { memo, ReactNode, useRef } from 'react'
22
import { useSpring, animated } from '@react-spring/web'
3-
import { useDrag } from '@use-gesture/react'
3+
import { useDrag, useWheel } from '@use-gesture/react'
44
import { rubberbandIfOutOfBounds } from '../../utils/rubberband'
55
import { bound } from '../../utils/bound'
66
import { PickerColumnItem, PickerValue } from './index'
77
import isEqual from 'lodash/isEqual'
88
import { useIsomorphicLayoutEffect } from 'ahooks'
99
import { measureCSSLength } from '../../utils/measure-css-length'
10+
import { supportsPassive } from '../../utils/supports-passive'
11+
import { FullGestureState } from '@use-gesture/core/src/types/state'
1012

1113
const classPrefix = `adm-picker-view`
1214

@@ -16,6 +18,7 @@ type Props = {
1618
value: PickerValue
1719
onSelect: (value: PickerValue, index: number) => void
1820
renderLabel: (item: PickerColumnItem) => ReactNode
21+
mouseWheel: boolean
1922
}
2023

2124
export const Wheel = memo<Props>(
@@ -76,38 +79,63 @@ export const Wheel = memo<Props>(
7679
onSelect(item.value)
7780
}
7881

79-
const bind = useDrag(
82+
const handleDrag = (
83+
state: Pick<
84+
FullGestureState<'drag'>,
85+
'last' | 'offset' | 'velocity' | 'direction'
86+
>
87+
) => {
88+
draggingRef.current = true
89+
const min = -((column.length - 1) * itemHeight.current)
90+
const max = 0
91+
if (state.last) {
92+
draggingRef.current = false
93+
const position =
94+
state.offset[1] + state.velocity[1] * state.direction[1] * 50
95+
const targetIndex =
96+
min < max
97+
? -Math.round(bound(position, min, max) / itemHeight.current)
98+
: 0
99+
scrollSelect(targetIndex)
100+
} else {
101+
const position = state.offset[1]
102+
api.start({
103+
y: rubberbandIfOutOfBounds(
104+
position,
105+
min,
106+
max,
107+
itemHeight.current * 50,
108+
0.2
109+
),
110+
})
111+
}
112+
}
113+
114+
useDrag(
80115
state => {
81-
draggingRef.current = true
82-
const min = -((column.length - 1) * itemHeight.current)
83-
const max = 0
84-
if (state.last) {
85-
draggingRef.current = false
86-
const position =
87-
state.offset[1] + state.velocity[1] * state.direction[1] * 50
88-
const targetIndex =
89-
min < max
90-
? -Math.round(bound(position, min, max) / itemHeight.current)
91-
: 0
92-
scrollSelect(targetIndex)
93-
} else {
94-
const position = state.offset[1]
95-
api.start({
96-
y: rubberbandIfOutOfBounds(
97-
position,
98-
min,
99-
max,
100-
itemHeight.current * 50,
101-
0.2
102-
),
103-
})
104-
}
116+
state.event.stopPropagation()
117+
handleDrag(state)
105118
},
106119
{
107120
axis: 'y',
108121
from: () => [0, y.get()],
109122
filterTaps: true,
110123
pointer: { touch: true },
124+
target: rootRef,
125+
}
126+
)
127+
128+
useWheel(
129+
state => {
130+
state.event.stopPropagation()
131+
handleDrag(state)
132+
},
133+
{
134+
axis: 'y',
135+
from: () => [0, y.get()],
136+
preventDefault: true,
137+
target: props.mouseWheel ? rootRef : undefined,
138+
eventOptions: supportsPassive ? { passive: false } : false,
111139
}
112140
)
113141

@@ -166,7 +194,7 @@ export const Wheel = memo<Props>(
166194
}
167195

168196
return (
169-
<div ref={rootRef} className={`${classPrefix}-column`} {...bind()}>
197+
<div ref={rootRef} className={`${classPrefix}-column`}>
170198
<animated.div
171199
style={{ translateY: y }}
172200
className={`${classPrefix}-column-wheel`}

src/components/picker/index.en.md

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type PickerValueExtend = {
4242
| cancelText | Text of the cancel button | `ReactNode` | `'取消'` |
4343
| children | Render function of the selected options | `(items: PickerColumnItem[]) => ReactNode` | - |
4444
| renderLabel | The function to custom rendering the label shown on a column | `(item: PickerColumnItem) => ReactNode` | `(item) => item.label` |
45+
| mouseWheel | Whether to allow interact with mouse wheel | `boolean` | `false` |
4546
4647
In addition, the following attributes of [Popup](./popup) are supported: `getContainer` `afterShow` `afterClose` `onClick` `stopPropagation`
4748
@@ -96,6 +97,7 @@ Same as `Picker`.
9697
| children | The rendering function of the selected items | `(value: Date) => ReactNode` | - |
9798
| renderLabel | The function to custom rendering the label shown on a column. `type` means any value in `precision`, `data` means the default number | `(type: string, data: number) => ReactNode` | - |
9899
| filter | Filter available time | `DatePickerFilter` | - |
100+
| mouseWheel | Whether to allow interact with mouse wheel | `boolean` | `false` |
99101
100102
```typescript | pure
101103
type DatePickerFilter = Partial<

src/components/picker/index.zh.md

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type PickerValueExtend = {
4242
| cancelText | 取消按钮的文字 | `ReactNode` | `'取消'` |
4343
| children | 所选项的渲染函数 | `(items: PickerColumnItem[]) => ReactNode` | - |
4444
| renderLabel | 自定义渲染每列展示的内容 | `(item: PickerColumnItem) => ReactNode` | `(item) => item.label` |
45+
| mouseWheel | 是否允许通过鼠标滚轮进行选择 | `boolean` | `false` |
4546
4647
此外还支持 [Popup](./popup) 的以下属性:`getContainer` `afterShow` `afterClose` `onClick` `stopPropagation`
4748
@@ -122,6 +123,7 @@ type CascadePickerOption = {
122123
| children | 所选项的渲染函数 | `(value: Date) => ReactNode` | - |
123124
| renderLabel | 自定义渲染每列展示的内容。其中 `type` 参数为 `precision` 中的任意值,`data` 参数为默认渲染的数字 | `(type: string, data: number) => ReactNode` | - |
124125
| filter | 过滤可供选择的时间 | `DatePickerFilter` | - |
126+
| mouseWheel | 是否允许通过鼠标滚轮进行选择 | `boolean` | `false` |
125127
126128
```typescript | pure
127129
type DatePickerFilter = Partial<

src/components/picker/picker.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export type PickerProps = {
3636
cancelText?: ReactNode
3737
children?: (items: (PickerColumnItem | null)[]) => ReactNode
3838
renderLabel?: (item: PickerColumnItem) => ReactNode
39+
mouseWheel?: boolean
3940
} & Pick<
4041
PopupProps,
4142
'getContainer' | 'afterShow' | 'afterClose' | 'onClick' | 'stopPropagation'
@@ -122,6 +123,7 @@ export const Picker = memo<PickerProps>(p => {
122123
columns={props.columns}
123124
renderLabel={props.renderLabel}
124125
value={innerValue}
126+
mouseWheel={props.mouseWheel}
125127
onChange={onChange}
126128
/>
127129
</div>

0 commit comments

Comments
 (0)