ipanel-rnapp-ajb-base 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/App.js +3 -0
- package/app/components/common/PickerV1/index.js +93 -0
- package/app/components/common/PickerV2/index.js +96 -0
- package/app/components/common/PickerV2Base/_util/devWarning.tsx +5 -0
- package/app/components/common/PickerV2Base/_util/getLocale.tsx +42 -0
- package/app/components/common/PickerV2Base/_util/hooks/useAnimations.tsx +57 -0
- package/app/components/common/PickerV2Base/_util/hooks/useClickAway.ts +30 -0
- package/app/components/common/PickerV2Base/_util/isPromise.ts +5 -0
- package/app/components/common/PickerV2Base/_util/reactNode.ts +27 -0
- package/app/components/common/PickerV2Base/_util/version.json +1 -0
- package/app/components/common/PickerV2Base/_util/with-default-props.tsx +15 -0
- package/app/components/common/PickerV2Base/config-provider/DisabledContext.tsx +24 -0
- package/app/components/common/PickerV2Base/config-provider/index.tsx +7 -0
- package/app/components/common/PickerV2Base/locale-provider/en_US.tsx +22 -0
- package/app/components/common/PickerV2Base/locale-provider/es_ES.tsx +22 -0
- package/app/components/common/PickerV2Base/locale-provider/fa_IR.tsx +22 -0
- package/app/components/common/PickerV2Base/locale-provider/index.en-US.md +37 -0
- package/app/components/common/PickerV2Base/locale-provider/index.tsx +126 -0
- package/app/components/common/PickerV2Base/locale-provider/index.zh-CN.md +38 -0
- package/app/components/common/PickerV2Base/locale-provider/ko_KR.tsx +22 -0
- package/app/components/common/PickerV2Base/locale-provider/pt_BR.tsx +22 -0
- package/app/components/common/PickerV2Base/locale-provider/ru_RU.tsx +22 -0
- package/app/components/common/PickerV2Base/locale-provider/sv_SE.tsx +22 -0
- package/app/components/common/PickerV2Base/locale-provider/useLocale.tsx +36 -0
- package/app/components/common/PickerV2Base/locale-provider/zh_CN.tsx +22 -0
- package/app/components/common/PickerV2Base/picker/Picker.tsx +216 -0
- package/app/components/common/PickerV2Base/picker/Popup.tsx +77 -0
- package/app/components/common/PickerV2Base/picker/PopupPickerTypes.tsx +16 -0
- package/app/components/common/PickerV2Base/picker/PropsType.tsx +26 -0
- package/app/components/common/PickerV2Base/picker/index.en-US.md +98 -0
- package/app/components/common/PickerV2Base/picker/index.tsx +85 -0
- package/app/components/common/PickerV2Base/picker/index.zh-CN.md +97 -0
- package/app/components/common/PickerV2Base/picker/locale/en_US.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/locale/es_ES.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/locale/fa_IR.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/locale/id_ID.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/locale/ko_KR.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/locale/pt_BR.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/locale/ru_RU.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/locale/sv_SE.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/locale/zh_CN.tsx +5 -0
- package/app/components/common/PickerV2Base/picker/style/index.tsx +57 -0
- package/app/components/common/PickerV2Base/picker-view/PropsType.tsx +37 -0
- package/app/components/common/PickerV2Base/picker-view/Wheel.tsx +200 -0
- package/app/components/common/PickerV2Base/picker-view/columns-extend.tsx +86 -0
- package/app/components/common/PickerV2Base/picker-view/index.en-US.md +71 -0
- package/app/components/common/PickerV2Base/picker-view/index.tsx +69 -0
- package/app/components/common/PickerV2Base/picker-view/index.zh-CN.md +72 -0
- package/app/components/common/PickerV2Base/picker-view/picker-view.tsx +166 -0
- package/app/components/common/PickerV2Base/picker-view/style/index.tsx +51 -0
- package/app/components/common/PickerV2Base/style/index.tsx +96 -0
- package/app/components/common/PickerV2Base/style/themes/default.tsx +203 -0
- package/app/components/index.js +2 -0
- package/app/eventBus/index.js +1 -0
- package/app/hooks/shopMessageManager.js +5 -3
- package/app/screens/LoginScreen/OpenLogSheet.js +6 -1
- package/app/utils/common.js +0 -1
- package/package.json +19 -19
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useMemo,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react'
|
|
9
|
+
import { mergeProps } from '../_util/with-default-props'
|
|
10
|
+
import {
|
|
11
|
+
PickerColumn,
|
|
12
|
+
PickerColumnItem,
|
|
13
|
+
PickerValue,
|
|
14
|
+
PickerValueExtend,
|
|
15
|
+
} from '../picker-view/PropsType'
|
|
16
|
+
import { getColumns, getValueExtend } from '../picker-view/columns-extend'
|
|
17
|
+
import RMCPicker, { PickerRef } from './Picker'
|
|
18
|
+
import { PickerPropsType } from './PropsType'
|
|
19
|
+
|
|
20
|
+
export interface PickerProps extends PickerPropsType {}
|
|
21
|
+
export { PickerColumn, PickerColumnItem, PickerValue, PickerValueExtend }
|
|
22
|
+
|
|
23
|
+
const defaultProps = {
|
|
24
|
+
defaultValue: [],
|
|
25
|
+
cols: 3,
|
|
26
|
+
cascade: true,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const Picker = forwardRef<PickerRef, PickerProps>((props, ref) => {
|
|
30
|
+
const p = mergeProps(defaultProps, props)
|
|
31
|
+
|
|
32
|
+
const [innerValue, setInnerValue] = useState<PickerValue[]>(
|
|
33
|
+
p.value === undefined ? p.defaultValue : p.value,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const pickerRef = React.useRef<PickerRef>(null)
|
|
37
|
+
|
|
38
|
+
useImperativeHandle(ref, () => pickerRef.current as PickerRef)
|
|
39
|
+
|
|
40
|
+
const columns = useMemo(
|
|
41
|
+
() => getColumns(p.data, innerValue, p.cols, p.cascade),
|
|
42
|
+
[p.data, innerValue, p.cols, p.cascade],
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const handleSelect = useCallback(
|
|
46
|
+
(val: PickerValue, index: number) => {
|
|
47
|
+
const value = innerValue?.slice?.(0) || []
|
|
48
|
+
value[index] = val
|
|
49
|
+
const { nextValue } = getValueExtend(p.data, value, p.cols, p.cascade)
|
|
50
|
+
setInnerValue(nextValue)
|
|
51
|
+
p.onPickerChange?.(nextValue, index)
|
|
52
|
+
},
|
|
53
|
+
[p, innerValue, setInnerValue],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
const onVisibleChange = useCallback(
|
|
57
|
+
(visible) => {
|
|
58
|
+
p.onVisibleChange?.(visible)
|
|
59
|
+
if (!visible && p.value !== innerValue) {
|
|
60
|
+
// 关闭时,如果选中值不同步,恢复为原选中值
|
|
61
|
+
setInnerValue(p.value || [])
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[innerValue, p],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setInnerValue(p.value || [])
|
|
69
|
+
}, [p.value])
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<RMCPicker
|
|
73
|
+
{...p}
|
|
74
|
+
innerValue={innerValue}
|
|
75
|
+
columns={columns}
|
|
76
|
+
handleSelect={handleSelect}
|
|
77
|
+
onVisibleChange={onVisibleChange}
|
|
78
|
+
ref={pickerRef}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
Picker.displayName = 'AntmPicker'
|
|
84
|
+
|
|
85
|
+
export default Picker
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Components
|
|
3
|
+
type: Data Entry
|
|
4
|
+
title: Picker
|
|
5
|
+
subtitle: 选择器
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
在一组预设数据中进行选择,e.g. 省市区选择。
|
|
9
|
+
|
|
10
|
+
### 规则
|
|
11
|
+
- 尽量使用 Picker 来帮助用户完成输入,避免用户通过键盘直接输入。
|
|
12
|
+
- DatePicker 是 Picker 的特定模式。
|
|
13
|
+
|
|
14
|
+
## API
|
|
15
|
+
|
|
16
|
+
### 属性
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
type PickerColumnItem = {
|
|
20
|
+
label: string | ReactNode
|
|
21
|
+
value: string | number
|
|
22
|
+
key?: string | number
|
|
23
|
+
children?: PickerColumnItem[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type PickerColumn = PickerColumnItem[]
|
|
27
|
+
|
|
28
|
+
type PickerValue = string | number
|
|
29
|
+
|
|
30
|
+
type PickerValueExtend = {
|
|
31
|
+
columns: PickerColumn[]
|
|
32
|
+
items: (PickerColumnItem | undefined)[]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
属性 | 说明 | 类型 | 默认值 | 版本
|
|
37
|
+
----|-----|------|-------|------
|
|
38
|
+
| data | 数据源 | `PickerColumn` / `PickerColumn[]` | - | |
|
|
39
|
+
| value | 选中项 | `PickerValue[]` | - | |
|
|
40
|
+
| defaultValue | 默认选中项 | `PickerValue[]` | - | |
|
|
41
|
+
| cascade | 是否级联。<br/>子级来自`data`参数内的`children` | Boolean | `true` | |
|
|
42
|
+
| cols | 列数 | Number | `3` | |
|
|
43
|
+
| onChange | 选中后的回调,可使用[rc-form](https://github.com/react-component/form) | `(value: PickerValue[], extend: PickerValueExtend) => void` | - | |
|
|
44
|
+
| onPickerChange | 每列数据选择变化后的回调函数 | `(value: PickerValue[], index: number) => void` | - | |
|
|
45
|
+
| onVisibleChange | 当显隐状态变化时回调函数 | `(visible: bool): void` | - | |
|
|
46
|
+
| renderLabel | 自定义渲染每列展示的内容 | `(item: PickerColumnItem, index: number) => ReactNode` | `(item) => item.label` | |
|
|
47
|
+
| locale | 国际化,可覆盖全局[LocaleProvider](/components/locale-provider-cn)的配置 | Object: {okText, dismissText, extra} | - | |
|
|
48
|
+
| title | 大标题 | ReactNode | - | |
|
|
49
|
+
| okText | 选中的文案 | String | `确定` | |
|
|
50
|
+
| dismissText | 取消选中的文案 | String | `取消` | |
|
|
51
|
+
| onOk | 点击选中时执行的回调 | `(value: PickerValue[], extend: PickerValueExtend) => void` | - | |
|
|
52
|
+
| onDismiss | 点击取消时执行的回调 | (): void | - | |
|
|
53
|
+
| okButtonProps | ok 按钮 props | [TouchableHighlightProps](https://reactnative.dev/docs/touchablehighlight) | `{ activeOpacity:1, underlayColor:'#ddd' }` | `5.1.3` |
|
|
54
|
+
| dismissButtonProps | dismiss 按钮 props | [TouchableHighlightProps](https://reactnative.dev/docs/touchablehighlight) | `{ activeOpacity:1, underlayColor:'#ddd' }` | `5.1.3` |
|
|
55
|
+
| visible | 是否显示选择器 | Boolean | - | |
|
|
56
|
+
| loading | 是否处于加载状态 | Boolean | - | |
|
|
57
|
+
| loadingContent | 加载状态下展示的内容 | ReactNode | - | |
|
|
58
|
+
| indicatorStyle | 默认Indicator的样式 | Object | - | |
|
|
59
|
+
|
|
60
|
+
### 自定义样式
|
|
61
|
+
|
|
62
|
+
属性 | 说明 | 类型 | 默认值
|
|
63
|
+
----|-----|------|------
|
|
64
|
+
| style | 外部样式 | `StyleProp<ViewStyle>` | - |
|
|
65
|
+
| styles | 内部组件样式集 | 同 [PickerViewStyle](/components//picker-view-cn/#pickerviewstyle-语义化样式) | - |
|
|
66
|
+
| itemStyle| 每列样式 | `StyleProp<TextStyle>` | - |
|
|
67
|
+
| itemHeight | 每列固定高度,未设值时会根据`numberOfLines`动态计算;`itemStyle`属性设置`{height}`值是无效的 | Number | - |
|
|
68
|
+
| numberOfLines | 允许每列显示行数 | Number | `1` |
|
|
69
|
+
|
|
70
|
+
#### 遮罩层
|
|
71
|
+
还支持自定义遮罩样式,如使用渐变组件`<LinearGradient />`。当前默认为白色半透明。
|
|
72
|
+
|
|
73
|
+
属性 | 说明 | 类型 | 默认值
|
|
74
|
+
----|-----|------|------
|
|
75
|
+
| renderMaskTop | 自定义渲染上半部分遮罩层 | `()=> ReactNode` | `<View style={{ flex: 1, backgroundColor: 'rgba(255,255,255,0.8)' }} />` |
|
|
76
|
+
| renderMaskBottom | 自定义渲染下半部分遮罩层 | `()=> ReactNode` | `<View style={{ flex: 1, backgroundColor: 'rgba(255,255,255,0.8)' }} />` |
|
|
77
|
+
|
|
78
|
+
### 自定义Children
|
|
79
|
+
通常是 [List.Item](/components/list-cn/#List.Item) ,以下属性也是围绕着`List.Item`展开:
|
|
80
|
+
|
|
81
|
+
属性 | 说明 | 类型 | 默认值 | 版本
|
|
82
|
+
----|-----|------|------|------
|
|
83
|
+
| children| Picker占位组件,通常是`List.Item` | `ReactNode` / `({extra, value, toggle})=>ReactNode` | - | `5.2.0`新增函数作为Children |
|
|
84
|
+
| extra | Picker children的`extra`属性,无选中项时展示 | String | `请选择` | |
|
|
85
|
+
| format | 格式化选中值的函数,用于回显在`extra`属性上 | (labels: string[]): any | `(labels) => { return labels.join(','); } ` | |
|
|
86
|
+
| triggerType | 按钮事件名称 | String | `onPress` | |
|
|
87
|
+
| disabled | 是否不可用 | Boolean | `false` | |
|
|
88
|
+
|
|
89
|
+
### PickerActions
|
|
90
|
+
属性 | 说明 | 类型
|
|
91
|
+
----|-----|------
|
|
92
|
+
| close |关闭 Picker|`() => void`|
|
|
93
|
+
| open |显示 Picker|`() => void`|
|
|
94
|
+
| toggle|切换 Picker 的显示和隐藏状态|`() => void`|
|
|
95
|
+
|
|
96
|
+
### Ref
|
|
97
|
+
同 PickerActions
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { StyleSheet, TextStyle, ViewStyle } from 'react-native'
|
|
2
|
+
|
|
3
|
+
export interface PickerStyle {
|
|
4
|
+
modal: ViewStyle
|
|
5
|
+
container: ViewStyle
|
|
6
|
+
header: ViewStyle
|
|
7
|
+
headerItem: ViewStyle
|
|
8
|
+
actionText: TextStyle
|
|
9
|
+
title: TextStyle
|
|
10
|
+
okText: TextStyle
|
|
11
|
+
dismissText: TextStyle
|
|
12
|
+
maskColor:ViewStyle
|
|
13
|
+
okView:ViewStyle
|
|
14
|
+
dismissView:ViewStyle
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default () =>
|
|
18
|
+
StyleSheet.create<PickerStyle>({
|
|
19
|
+
modal: {
|
|
20
|
+
flex: 1,
|
|
21
|
+
flexDirection: 'column',
|
|
22
|
+
justifyContent: 'flex-end',
|
|
23
|
+
// backgroundColor: 'rgba(0,0,0,.6)'
|
|
24
|
+
},
|
|
25
|
+
container: {},
|
|
26
|
+
header: {
|
|
27
|
+
height: 44,
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
flexDirection: 'row',
|
|
30
|
+
justifyContent: 'center',
|
|
31
|
+
borderBottomWidth: 1,
|
|
32
|
+
borderBottomColor: '#e7e7e7',
|
|
33
|
+
},
|
|
34
|
+
headerItem: {
|
|
35
|
+
height: 44,
|
|
36
|
+
flex: 1,
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
justifyContent: 'center',
|
|
39
|
+
},
|
|
40
|
+
actionText: {
|
|
41
|
+
color: '#0ae',
|
|
42
|
+
fontSize: 18,
|
|
43
|
+
textAlign: 'center',
|
|
44
|
+
},
|
|
45
|
+
okText: {},
|
|
46
|
+
okView:{},
|
|
47
|
+
dismissText: {},
|
|
48
|
+
dismissView:{},
|
|
49
|
+
title: {
|
|
50
|
+
color: '#666',
|
|
51
|
+
fontSize: 18,
|
|
52
|
+
textAlign: 'center',
|
|
53
|
+
},
|
|
54
|
+
maskColor:{
|
|
55
|
+
backgroundColor: 'rgba(0,0,0,.6)'
|
|
56
|
+
}
|
|
57
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { StyleProp, TextStyle, ViewStyle } from 'react-native'
|
|
3
|
+
|
|
4
|
+
export type PickerValue = string | number
|
|
5
|
+
|
|
6
|
+
export type PickerValueExtend = {
|
|
7
|
+
columns: PickerColumn[]
|
|
8
|
+
items: (PickerColumnItem | undefined)[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type PickerColumnItem = {
|
|
12
|
+
label: string | ReactNode
|
|
13
|
+
value: string | number
|
|
14
|
+
key?: string | number
|
|
15
|
+
children?: PickerColumnItem[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type PickerColumn = PickerColumnItem[]
|
|
19
|
+
|
|
20
|
+
export interface PickerViewPropsType {
|
|
21
|
+
value?: PickerValue[]
|
|
22
|
+
defaultValue?: PickerValue[]
|
|
23
|
+
data: PickerColumn | PickerColumn[]
|
|
24
|
+
cols?: number
|
|
25
|
+
cascade?: boolean
|
|
26
|
+
loading?: boolean
|
|
27
|
+
loadingContent?: ReactNode
|
|
28
|
+
numberOfLines?: number
|
|
29
|
+
style?: StyleProp<ViewStyle>
|
|
30
|
+
itemStyle?: StyleProp<TextStyle>
|
|
31
|
+
itemHeight?: number
|
|
32
|
+
indicatorStyle?: StyleProp<ViewStyle>
|
|
33
|
+
onChange?: (value: PickerValue[], extend: PickerValueExtend) => void
|
|
34
|
+
renderLabel?: (item: PickerColumnItem, index: number) => ReactNode
|
|
35
|
+
renderMaskTop?: () => ReactNode
|
|
36
|
+
renderMaskBottom?: () => ReactNode
|
|
37
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import {
|
|
4
|
+
NativeScrollEvent,
|
|
5
|
+
NativeSyntheticEvent,
|
|
6
|
+
Platform,
|
|
7
|
+
ScrollView,
|
|
8
|
+
View,
|
|
9
|
+
} from 'react-native'
|
|
10
|
+
|
|
11
|
+
type ColumnItem = {
|
|
12
|
+
label: string | ReactNode
|
|
13
|
+
value: string | number
|
|
14
|
+
}
|
|
15
|
+
type Value = string | number
|
|
16
|
+
|
|
17
|
+
type Props = {
|
|
18
|
+
index: number
|
|
19
|
+
column: ColumnItem[]
|
|
20
|
+
value?: Value
|
|
21
|
+
onSelect: (value: Value, index: number) => void
|
|
22
|
+
renderLabel: (item: ColumnItem, index: number) => ReactNode
|
|
23
|
+
itemHeight: number
|
|
24
|
+
wheelHeight: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class Wheel extends React.Component<Props> {
|
|
28
|
+
scrollerRef: any
|
|
29
|
+
|
|
30
|
+
scrollBuffer: number
|
|
31
|
+
|
|
32
|
+
selectValue: any
|
|
33
|
+
isScrolling: boolean
|
|
34
|
+
|
|
35
|
+
componentDidMount() {
|
|
36
|
+
const { value, column, itemHeight } = this.props
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
this.scrollTo(this.getSelectIndex(column, value) * itemHeight)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
shouldComponentUpdate(nextProps: Props) {
|
|
43
|
+
const { value, column, itemHeight, wheelHeight } = nextProps
|
|
44
|
+
if (
|
|
45
|
+
value !== this.props.value ||
|
|
46
|
+
itemHeight !== this.props.itemHeight ||
|
|
47
|
+
wheelHeight !== this.props.wheelHeight
|
|
48
|
+
) {
|
|
49
|
+
this.scrollTo(this.getSelectIndex(column, value) * itemHeight)
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
return column !== this.props.column
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
componentDidUpdate(prevProps: Props) {
|
|
56
|
+
const { column } = prevProps
|
|
57
|
+
if (column !== this.props.column) {
|
|
58
|
+
this.scrollTo(
|
|
59
|
+
this.getSelectIndex(this.props.column, this.props.value) *
|
|
60
|
+
this.props.itemHeight,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
scrollTo = (y: any) => {
|
|
66
|
+
this.scrollerRef?.scrollTo?.({
|
|
67
|
+
y,
|
|
68
|
+
animated: false,
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getSelectIndex(column: ColumnItem[], value?: Value) {
|
|
73
|
+
return Math.max(
|
|
74
|
+
column.findIndex((item) => item.value === value),
|
|
75
|
+
0,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
handleSelect = () => {
|
|
80
|
+
if (this.props.value !== this.selectValue && this.props.onSelect) {
|
|
81
|
+
this.props.onSelect(this.selectValue, this.props.index)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
this.scrollTo(
|
|
86
|
+
this.getSelectIndex(this.props.column, this.props.value) *
|
|
87
|
+
this.props.itemHeight,
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
onScrollEndForWeb = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
93
|
+
e.persist?.()
|
|
94
|
+
let contentOffset = e.nativeEvent.contentOffset
|
|
95
|
+
// android/web hack
|
|
96
|
+
if (!contentOffset) {
|
|
97
|
+
//@ts-ignore
|
|
98
|
+
const { position } = e.nativeEvent
|
|
99
|
+
contentOffset = {
|
|
100
|
+
x: 0,
|
|
101
|
+
y: position * this.props.itemHeight,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const selectIndex = Math.round(contentOffset.y / this.props.itemHeight)
|
|
106
|
+
if (this.props.column[selectIndex]) {
|
|
107
|
+
this.selectValue = this.props.column[selectIndex].value
|
|
108
|
+
if (!this.isScrolling) {
|
|
109
|
+
this.updateSelectThrottle()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/*** for web event ***/
|
|
115
|
+
onTouchStartForWeb = () => {
|
|
116
|
+
this.isScrolling = true
|
|
117
|
+
}
|
|
118
|
+
onTouchEndForWeb = () => {
|
|
119
|
+
this.isScrolling = false
|
|
120
|
+
this.updateSelectThrottle()
|
|
121
|
+
}
|
|
122
|
+
onScrollForWeb = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
123
|
+
this.onScrollEndForWeb(e)
|
|
124
|
+
}
|
|
125
|
+
updateSelectThrottle = () => {
|
|
126
|
+
clearTimeout(this.scrollBuffer)
|
|
127
|
+
this.scrollBuffer = setTimeout(() => {
|
|
128
|
+
this.handleSelect()
|
|
129
|
+
}, 100) //idle time
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
onMomentumScrollBegin = () => {
|
|
133
|
+
this.isScrolling = true
|
|
134
|
+
}
|
|
135
|
+
onMomentumScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
136
|
+
e.persist?.()
|
|
137
|
+
if (this.isScrolling) {
|
|
138
|
+
let contentOffset = e.nativeEvent.contentOffset
|
|
139
|
+
// android/web hack
|
|
140
|
+
if (!contentOffset) {
|
|
141
|
+
//@ts-ignore
|
|
142
|
+
const { position } = e.nativeEvent
|
|
143
|
+
contentOffset = {
|
|
144
|
+
x: 0,
|
|
145
|
+
y: position * this.props.itemHeight,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const selectIndex = Math.round(contentOffset.y / this.props.itemHeight)
|
|
149
|
+
if (this.props.column[selectIndex]) {
|
|
150
|
+
this.selectValue = this.props.column[selectIndex].value
|
|
151
|
+
this.handleSelect()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
this.isScrolling = false
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
renderItems = () => {
|
|
158
|
+
const { column, renderLabel, itemHeight, wheelHeight } = this.props
|
|
159
|
+
return (
|
|
160
|
+
<View
|
|
161
|
+
style={{
|
|
162
|
+
paddingVertical: (wheelHeight - itemHeight) / 2,
|
|
163
|
+
}}>
|
|
164
|
+
{column.map((item, index) => renderLabel(item, index))}
|
|
165
|
+
</View>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
render() {
|
|
170
|
+
return (
|
|
171
|
+
<ScrollView
|
|
172
|
+
style={{ flex: 1 }}
|
|
173
|
+
nestedScrollEnabled
|
|
174
|
+
horizontal={false}
|
|
175
|
+
ref={(el: any) => (this.scrollerRef = el)}
|
|
176
|
+
showsHorizontalScrollIndicator={false}
|
|
177
|
+
showsVerticalScrollIndicator={false}
|
|
178
|
+
pagingEnabled={false}
|
|
179
|
+
automaticallyAdjustContentInsets={false}
|
|
180
|
+
directionalLockEnabled={true}
|
|
181
|
+
decelerationRate="fast"
|
|
182
|
+
snapToInterval={this.props.itemHeight}
|
|
183
|
+
{...(Platform.OS === 'web'
|
|
184
|
+
? {
|
|
185
|
+
onTouchStart: this.onTouchStartForWeb,
|
|
186
|
+
onTouchEnd: this.onTouchEndForWeb,
|
|
187
|
+
onScroll: this.onScrollForWeb,
|
|
188
|
+
scrollEventThrottle: 16,
|
|
189
|
+
}
|
|
190
|
+
: {
|
|
191
|
+
onMomentumScrollBegin: this.onMomentumScrollBegin,
|
|
192
|
+
onMomentumScrollEnd: this.onMomentumScrollEnd,
|
|
193
|
+
})}>
|
|
194
|
+
{this.renderItems()}
|
|
195
|
+
</ScrollView>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export default Wheel
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { PickerColumn, PickerValue } from './PropsType'
|
|
2
|
+
|
|
3
|
+
export function getColumns(
|
|
4
|
+
d: PickerColumn | PickerColumn[],
|
|
5
|
+
val: PickerValue[],
|
|
6
|
+
cols: number,
|
|
7
|
+
cascade: boolean,
|
|
8
|
+
): PickerColumn[] {
|
|
9
|
+
if (!d || d.length === 0) {
|
|
10
|
+
return []
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!cascade) {
|
|
14
|
+
// when d is PickerColumn
|
|
15
|
+
if (!Array.isArray(d[0])) {
|
|
16
|
+
return [d as PickerColumn]
|
|
17
|
+
}
|
|
18
|
+
// when d is PickerColumn[]
|
|
19
|
+
return (d as PickerColumn[]).slice(0, cols!)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// cascade data
|
|
23
|
+
const columns = []
|
|
24
|
+
let currentOptions = (d as PickerColumn).slice()
|
|
25
|
+
let selected = val || []
|
|
26
|
+
let i = 0
|
|
27
|
+
while (i < cols!) {
|
|
28
|
+
columns.push(
|
|
29
|
+
currentOptions.map((option: any) => ({
|
|
30
|
+
label: option.label,
|
|
31
|
+
value: option.value,
|
|
32
|
+
})),
|
|
33
|
+
)
|
|
34
|
+
const x = selected[i]
|
|
35
|
+
const targetOptions =
|
|
36
|
+
currentOptions.find((option: any) => option.value === x) ||
|
|
37
|
+
currentOptions[0]
|
|
38
|
+
if (!targetOptions.children) {
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
currentOptions = targetOptions.children
|
|
42
|
+
i++
|
|
43
|
+
}
|
|
44
|
+
return columns
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getValueExtend(
|
|
48
|
+
d: PickerColumn | PickerColumn[],
|
|
49
|
+
val: PickerValue[],
|
|
50
|
+
cols: number,
|
|
51
|
+
cascade: boolean,
|
|
52
|
+
) {
|
|
53
|
+
if (!d || d.length === 0) {
|
|
54
|
+
return { nextValue: [], extend: [] }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!cascade) {
|
|
58
|
+
const columns = getColumns(d, val, cols, false).map(
|
|
59
|
+
(column: PickerColumn, index: number) =>
|
|
60
|
+
column.find((item) => item.value === val[index]) ?? column[0],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return { nextValue: columns.map((item) => item.value), extend: columns }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// cascade data
|
|
67
|
+
let currentOptions = (d as PickerColumn).slice()
|
|
68
|
+
const nextValue = []
|
|
69
|
+
const extend = []
|
|
70
|
+
let selected = val || []
|
|
71
|
+
let i = 0
|
|
72
|
+
while (i < cols!) {
|
|
73
|
+
const x = selected[i]
|
|
74
|
+
const targetOptions =
|
|
75
|
+
currentOptions.find((option: any) => option.value === x) ||
|
|
76
|
+
currentOptions[0]
|
|
77
|
+
nextValue[i] = targetOptions.value
|
|
78
|
+
extend[i] = targetOptions
|
|
79
|
+
if (!targetOptions.children) {
|
|
80
|
+
break
|
|
81
|
+
}
|
|
82
|
+
currentOptions = targetOptions.children
|
|
83
|
+
i++
|
|
84
|
+
}
|
|
85
|
+
return { nextValue, extend }
|
|
86
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: Components
|
|
3
|
+
type: Data Entry
|
|
4
|
+
title: PickerView
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
PickerView's functions like Picker, but it is rendered directly in the area instead of the pop-up window.
|
|
8
|
+
|
|
9
|
+
## API
|
|
10
|
+
|
|
11
|
+
### Props
|
|
12
|
+
|
|
13
|
+
Properties | Descrition | Type | Default
|
|
14
|
+
-----------|------------|------|--------
|
|
15
|
+
| data | data source | `PickerColumn` / `PickerColumn[]` | - |
|
|
16
|
+
| value | Selected options | `PickerValue[]` | - |
|
|
17
|
+
| defaultValue | Default selected options | `PickerValue[]` | - |
|
|
18
|
+
| cascade | whether cascade <br/>child cascade get from `data[].children` | Boolean | `true` |
|
|
19
|
+
| cols | col numbers | Number | `3` |
|
|
20
|
+
| onChange | selected callback function, can use [rc-form](https://github.com/react-component/form) | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
|
|
21
|
+
| renderLabel | The function to custom rendering the label shown on a column | `(item: PickerColumnItem, index: number) => ReactNode` | `(item) => item.label` |
|
|
22
|
+
| loading | Should the Picker displays as loading state | Boolean | - |
|
|
23
|
+
| loadingContent | The loading content displayed in loading state | ReactNode | `<ActivityIndicator/>` |
|
|
24
|
+
| indicatorStyle | style of default Indicator | Object | - |
|
|
25
|
+
|
|
26
|
+
For the type definition of `PickerColumnItem` `PickerColumn` `PickerValue` `PickerValueExtend`, please refer to the document of [Picker](/components/picker/).
|
|
27
|
+
|
|
28
|
+
### Custom Style
|
|
29
|
+
|
|
30
|
+
Properties | Descrition | Type | Default
|
|
31
|
+
-----------|------------|------|--------
|
|
32
|
+
| style | style | `StyleProp<ViewStyle>` | - |
|
|
33
|
+
| styles | Semantic DOM style | [PickerViewStyle](#pickerviewstyle-interface) | - |
|
|
34
|
+
| itemStyle| style to apply to each of the item labels | `StyleProp<TextStyle>` | - |
|
|
35
|
+
| itemHeight | Height of option item, calculated by `numberOfLines` when without value; `itemStyle` was not allowed to set `{height}` | Number | - |
|
|
36
|
+
| numberOfLines | Used to truncate the text with an ellipsis after computing the text layout, including line wrapping, such that the total number of lines does not exceed this number | Number | `1` |
|
|
37
|
+
|
|
38
|
+
#### PickerViewStyle interface
|
|
39
|
+
|
|
40
|
+
```jsx
|
|
41
|
+
interface PickerViewStyle {
|
|
42
|
+
wrappper: ViewStyle
|
|
43
|
+
wheelWrapper: ViewStyle
|
|
44
|
+
mask: ViewStyle
|
|
45
|
+
maskTop: ViewStyle
|
|
46
|
+
maskMiddle: ViewStyle
|
|
47
|
+
maskBottom: ViewStyle
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Mask View
|
|
52
|
+
|
|
53
|
+
Support custom mask style, such as using the gradient component `<LinearGradient />`. Default is white translucency.
|
|
54
|
+
|
|
55
|
+
Properties | Descrition | Type | Default
|
|
56
|
+
-----------|------------|------|--------
|
|
57
|
+
| renderMaskTop | The function to custom rendering the mask top half view | `()=> ReactNode` | `<View style={{ flex: 1, backgroundColor: 'rgba(255,255,255,0.8)' }} />` |
|
|
58
|
+
| renderMaskBottom | The function to custom rendering the mask bottom half view | `()=> ReactNode` | `<View style={{ flex: 1, backgroundColor: 'rgba(255,255,255,0.8)' }} />` |
|
|
59
|
+
|
|
60
|
+
## FAQ
|
|
61
|
+
|
|
62
|
+
### On the Android platform, when using `PickerView` nested in `ScrollView`, the Picker Item cannot slide. What should I do?
|
|
63
|
+
|
|
64
|
+
Support in `5.1.3`. Set the `nestedScrollEnabled` property of `ScrollView` to `true`.
|
|
65
|
+
|
|
66
|
+
```jsx
|
|
67
|
+
<ScrollView nestedScrollEnabled={true}>
|
|
68
|
+
...
|
|
69
|
+
<PickerView />
|
|
70
|
+
</ScrollView>
|
|
71
|
+
```
|