diff --git a/.jest.config.js b/.jest.config.js
index e5e3dbe45..9f9bed91f 100644
--- a/.jest.config.js
+++ b/.jest.config.js
@@ -8,7 +8,6 @@ const transformPackages = [
'react-native-collapsible',
'@bang88/react-native-ultimate-listview',
'@react-native-community',
- '@react-native-picker/picker',
'react-native-gesture-handler'
];
diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md
index beba87ad3..940c7eab2 100644
--- a/CHANGELOG.en-US.md
+++ b/CHANGELOG.en-US.md
@@ -14,6 +14,22 @@ toc: false
---
+### 5.1.0
+`2024-02-20`
+- Refactor **Picker** & **PickerView**
+ - 🔥 Remove dependence `@react-native-picker/picker`
+ - 💄 Refactor extends by `ScrollView {snapToInterval}` to support web
+ - 🆕 Refactor `itemStyle` prop, make styles more flexible [#1311](https://github.com/ant-design/ant-design-mobile-rn/issues/1311) [#1316](https://github.com/ant-design/ant-design-mobile-rn/issues/1316)
+ - 🆕 Picker support (`visible`) new prop
+- Refactor **DatePicker** & **DatePickerView**
+ - 💄 **Style** 和 **Base Props** extends by Picker & PickerView
+ - 🆕 Support (`precision` `filter` ) new props
+ - ⚡️ Deprecated (`mode`)prop; date format by [Day.js](https://day.js.org/docs/en/parse/string-format)
+- ❗️Delete **ImagePicker** and remove dependence `@react-native-camera-roll/camera-roll`
+- **Switch**
+ - fix: `checked` prop support controlled mode [#1325](https://github.com/ant-design/ant-design-mobile-rn/issues/1325)
+ - feat: `onChange` prop when the Promise is returned, the loading status will be displayed automatically
+
### 5.0.5
`2023-11-08`
- fix: Picker support `numberOfLines` property [#1311](https://github.com/ant-design/ant-design-mobile-rn/issues/1311)
diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md
index a70ea6fd7..4557ee017 100644
--- a/CHANGELOG.zh-CN.md
+++ b/CHANGELOG.zh-CN.md
@@ -14,6 +14,22 @@ toc: false
---
+### 5.1.0
+`2024-02-23`
+- 重构 **Picker** & **PickerView**
+ - 🔥 重构开发并移除 `@react-native-picker/picker` 依赖
+ - 💄 基于 `ScrollView {snapToInterval}` 开发并支持`web`端
+ - 🆕 重构 `itemStyle` 样式,显示更灵活 [#1311](https://github.com/ant-design/ant-design-mobile-rn/issues/1311) [#1316](https://github.com/ant-design/ant-design-mobile-rn/issues/1316)
+ - 🆕 Picker 新增 (`visible`) 属性支持
+- 重构 **DatePicker** & **DatePickerView**
+ - 💄 **样式** 和 **基础属性** 继承 Picker & PickerView
+ - 🆕 新增 (`precision` `filter` ) 属性支持
+ - ⚡️ 废弃(`mode`)属性;时间格式引用[Day.js](https://day.js.org/docs/zh-CN/parse/string-format)
+- ❗️删除 **ImagePicker** 并移除 `@react-native-camera-roll/camera-roll` 依赖
+- **Switch**
+ - fix: `checked`属性支持全受控模式 [#1325](https://github.com/ant-design/ant-design-mobile-rn/issues/1325)
+ - feat: `onChange`属性当返回 Promise 时,会自动显示加载状态
+
### 5.0.5
`2023-11-08`
- fix: Picker support `numberOfLines` property [#1311](https://github.com/ant-design/ant-design-mobile-rn/issues/1311)
diff --git a/README.md b/README.md
index 35fce1f28..01e55fe01 100644
--- a/README.md
+++ b/README.md
@@ -93,13 +93,13 @@ yarn add @ant-design/react-native
### Installing peer dependencies
```bash
-npm install @react-native-camera-roll/camera-roll @react-native-picker/picker @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
+npm install @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
```
or
```bash
-yarn add @react-native-camera-roll/camera-roll @react-native-picker/picker @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
+yarn add @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
```
> You need go to ios folder and run `pod install` (auto linking),Android will handle it by itself.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 131d61e2e..cc5ed069d 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -93,13 +93,13 @@ yarn add @ant-design/react-native
### 安装peer依赖
```bash
-npm install @react-native-camera-roll/camera-roll @react-native-picker/picker @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
+npm install @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
```
or
```bash
-yarn add @react-native-camera-roll/camera-roll @react-native-picker/picker @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
+yarn add @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
```
> 安装完依赖后需要到 iOS 目录 `pod install`(auto linking),Android 不需要手动处理
diff --git a/components/_util/hooks/useAnimations.tsx b/components/_util/hooks/useAnimations.tsx
index b48d974a9..3189c5386 100644
--- a/components/_util/hooks/useAnimations.tsx
+++ b/components/_util/hooks/useAnimations.tsx
@@ -45,6 +45,7 @@ export function useAnimatedTiming(): [Animated.Value, animateType] {
useNativeDriver,
}).start()
},
+ // @ts-ignore
[animatedValue],
)
var [animatedValue] = useAnimate({
diff --git a/components/_util/isPromise.ts b/components/_util/isPromise.ts
new file mode 100644
index 000000000..d7013d76a
--- /dev/null
+++ b/components/_util/isPromise.ts
@@ -0,0 +1,5 @@
+export function isPromise(obj: unknown): obj is Promise {
+ return (
+ !!obj && typeof obj === 'object' && typeof (obj as any).then === 'function'
+ )
+}
diff --git a/components/_util/with-default-props.tsx b/components/_util/with-default-props.tsx
new file mode 100644
index 000000000..d209b3010
--- /dev/null
+++ b/components/_util/with-default-props.tsx
@@ -0,0 +1,15 @@
+const assignWith = require('lodash.assignwith')
+
+export function mergeProps(a: A, b: B): B & A
+export function mergeProps(a: A, b: B, c: C): C & B & A
+export function mergeProps(...items: any[]) {
+ function customizer(objValue: any, srcValue: any) {
+ return srcValue === undefined ? objValue : srcValue
+ }
+
+ let ret = { ...items[0] }
+ for (let i = 1; i < items.length; i++) {
+ ret = assignWith(ret, items[i], customizer)
+ }
+ return ret
+}
diff --git a/components/carousel/__tests__/__snapshots__/demo.test.js.snap b/components/carousel/__tests__/__snapshots__/demo.test.js.snap
index 5c2855905..ec5fcae89 100644
--- a/components/carousel/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/carousel/__tests__/__snapshots__/demo.test.js.snap
@@ -27,6 +27,7 @@ exports[`renders ./components/carousel/demo/basic.tsx correctly 1`] = `
automaticallyAdjustContentInsets={false}
autoplay={true}
autoplayInterval={3000}
+ collapsable={false}
contentOffset={
Object {
"x": 0,
@@ -39,6 +40,8 @@ exports[`renders ./components/carousel/demo/basic.tsx correctly 1`] = `
dots={true}
horizontal={true}
infinite={true}
+ onGestureHandlerEvent={[Function]}
+ onGestureHandlerStateChange={[Function]}
onMomentumScrollEnd={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
@@ -492,6 +495,7 @@ exports[`renders ./components/carousel/demo/basic.tsx correctly 1`] = `
automaticallyAdjustContentInsets={false}
autoplay={true}
autoplayInterval={3000}
+ collapsable={false}
contentOffset={
Object {
"x": 0,
@@ -504,6 +508,8 @@ exports[`renders ./components/carousel/demo/basic.tsx correctly 1`] = `
dots={true}
horizontal={false}
infinite={true}
+ onGestureHandlerEvent={[Function]}
+ onGestureHandlerStateChange={[Function]}
onMomentumScrollEnd={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
diff --git a/components/carousel/index.tsx b/components/carousel/index.tsx
index f358c9bed..708db7716 100644
--- a/components/carousel/index.tsx
+++ b/components/carousel/index.tsx
@@ -4,14 +4,14 @@ import {
NativeScrollEvent,
NativeSyntheticEvent,
Platform,
- ScrollView,
ScrollViewProps,
StyleProp,
View,
ViewStyle,
} from 'react-native'
-import { WithTheme, WithThemeStyles } from '../style'
+import { ScrollView } from 'react-native-gesture-handler'
import devWarning from '../_util/devWarning'
+import { WithTheme, WithThemeStyles } from '../style'
import CarouselStyles, { CarouselStyle } from './style/index'
export interface CarouselPropsType
diff --git a/components/date-picker-view/PropsType.tsx b/components/date-picker-view/PropsType.tsx
index 3dd010355..84d8cddbe 100644
--- a/components/date-picker-view/PropsType.tsx
+++ b/components/date-picker-view/PropsType.tsx
@@ -1,7 +1,39 @@
-import { DatePickerPropsType } from '../date-picker/PropsType'
+import type { ReactNode } from 'react'
+import { DatePickerFilter, Precision } from '../date-picker/date-picker-utils'
+import { PickerDate } from '../date-picker/util'
+import {
+ PickerValueExtend,
+ PickerViewPropsType,
+} from '../picker-view/PropsType'
-export interface DatePickerProps extends DatePickerPropsType {
- onScrollChange?: (newValue: any, vals: any, index: number) => void
- triggerTypes?: string
- styles?: any
+export type RenderLabel = (type: Precision | 'now', data: number) => ReactNode
+
+export interface DatePickerViewPropsType
+ extends Pick<
+ PickerViewPropsType,
+ | 'style'
+ | 'itemStyle'
+ | 'numberOfLines'
+ | 'renderMaskTop'
+ | 'renderMaskBottom'
+ > {
+ value?: PickerDate
+ defaultValue?: PickerDate
+ mode?: Precision | 'date'
+ minDate?: Date
+ maxDate?: Date
+ onChange?: (value: Date, extend?: PickerValueExtend) => void
+ onValueChange?: (vals: any, index: number) => void
+ precision?: Precision
+ renderLabel?: RenderLabel
+ locale?: {
+ year?: string
+ month?: string
+ day?: string
+ hour?: string
+ minute?: string
+ am?: string
+ pm?: string
+ }
+ filter?: DatePickerFilter
}
diff --git a/components/date-picker-view/date-picker-view.tsx b/components/date-picker-view/date-picker-view.tsx
index 95325e612..1835b687a 100644
--- a/components/date-picker-view/date-picker-view.tsx
+++ b/components/date-picker-view/date-picker-view.tsx
@@ -1,39 +1,102 @@
-import React from 'react'
-import RCDatePicker from '../date-picker/datepicker'
+import useMergedState from 'rc-util/lib/hooks/useMergedState'
+import React, {
+ FunctionComponent,
+ memo,
+ useCallback,
+ useContext,
+ useMemo,
+} from 'react'
+
import { getComponentLocale } from '../_util/getLocale'
-import { DatePickerProps } from './PropsType'
+import { mergeProps } from '../_util/with-default-props'
+import { getValueExtend, usePickerValue } from '../date-picker/columns-extend'
+import { generateDatePickerColumns } from '../date-picker/date-picker-utils'
import { LocaleContext } from '../locale-provider'
-export default class DatePickerView extends React.Component<
- DatePickerProps,
- any
-> {
- static defaultProps = {
- mode: 'datetime',
- // extra: '请选择',
- minuteStep: 1,
- use12Hours: false,
- }
-
- static contextType = LocaleContext
-
- render() {
- // tslint:disable-next-line:no-this-assignment
- const { props, context } = this
- const locale = getComponentLocale(props, context, 'DatePickerView', () =>
- require('./locale/zh_CN'),
- )
+import RMCPickerView from '../picker-view/picker-view'
+import { PickerViewStyle } from '../picker-view/style'
+import { WithThemeStyles } from '../style'
+import { DatePickerViewPropsType } from './PropsType'
+import useRenderLabel from './useRenderLabel'
- // DatePicker use `defaultDate`, maybe because there are PopupDatePicker inside? @yiminghe
- // Here Use `date` instead of `defaultDate`, make it controlled fully.
- return (
-
- )
- }
+export interface DatePickerViewProps
+ extends DatePickerViewPropsType,
+ WithThemeStyles {}
+
+const defaultProps = {
+ minDate: new Date('2000-1-1'),
+ maxDate: new Date('2030-1-1'),
+ mode: 'date',
}
+
+const DatePickerView: FunctionComponent = memo((props) => {
+ const p = mergeProps(defaultProps, props)
+ const {
+ value,
+ defaultValue,
+ minDate,
+ maxDate,
+ mode,
+ precision,
+ renderLabel,
+ filter,
+ ...restProps
+ } = p
+
+ const _precision = precision || (mode === 'date' ? 'day' : mode) || 'day'
+
+ const [innerValue, setInnerValue] = useMergedState(new Date(), {
+ value: value,
+ defaultValue: defaultValue,
+ })
+
+ const _locale = getComponentLocale(
+ p,
+ useContext(LocaleContext),
+ 'DatePickerView',
+ () => require('./locale/zh_CN'),
+ )
+
+ const mergedRenderLabel = useRenderLabel(renderLabel, _locale)
+
+ const pickerValue = usePickerValue(innerValue, minDate, maxDate, _precision)
+
+ const columns = useMemo(() => {
+ return generateDatePickerColumns(
+ pickerValue,
+ minDate,
+ maxDate,
+ _precision,
+ mergedRenderLabel,
+ filter,
+ false,
+ )
+ }, [maxDate, mergedRenderLabel, minDate, _precision, filter, pickerValue])
+
+ const handleSelect = useCallback(
+ (val: string, i: number) => {
+ const pvalue = pickerValue.slice(0)
+ pvalue[i] = val
+ const { dateValue, extend } = getValueExtend(columns, pvalue, _precision)
+ setInnerValue(dateValue)
+ p.onChange?.(dateValue, {
+ items: extend,
+ columns,
+ })
+ p.onValueChange?.(dateValue, i)
+ },
+ [columns, _precision, p, pickerValue, setInnerValue],
+ )
+
+ return (
+
+ )
+})
+
+DatePickerView.displayName = 'Picker'
+
+export default DatePickerView
diff --git a/components/date-picker-view/demo/basic.md b/components/date-picker-view/demo/basic.md
index 5f0eafa0f..52b19b9dc 100644
--- a/components/date-picker-view/demo/basic.md
+++ b/components/date-picker-view/demo/basic.md
@@ -9,41 +9,118 @@ title:
```jsx
import React from 'react';
-import { Text, View } from 'react-native';
-import { DatePickerView, Provider } from '@ant-design/react-native';
-export default class DatePickerViewExample extends React.Component {
- constructor() {
- super(...arguments);
- this.state = {
- value: undefined,
- };
- this.onChange = value => {
- console.log(value);
- this.setState({ value });
- };
- this.onValueChange = (...args) => {
- console.log(args);
- };
+import { ScrollView, Text } from 'react-native'
+import { DatePickerView } from '@ant-design/react-native';
+import { DatePickerFilter } from '@ant-design/react-native/lib/date-picker/date-picker-utils'
+
+const now = new Date()
+
+export default () => {
+ const [value, setValue] = useState(now)
+
+ return (
+
+ 基础用法
+
+
+ 受控模式
+ {
+ setValue(val)
+ console.log('onChange', val)
+ }}
+ />
+
+ 自定义每列的渲染内容
+
+
+ 周选择器
+ console.log('onChange', val)}
+ precision="week-day"
+ defaultValue={now}
+ renderLabel={weekdayLabelRenderer}
+ />
+
+ 过滤可供选择的时间
+
+
+ )
+}
+
+const labelRenderer = (type: string, data: number) => {
+ switch (type) {
+ case 'year':
+ return data + '年'
+ case 'month':
+ return data + '月'
+ case 'day':
+ return data + '日'
+ case 'hour':
+ return data + '时'
+ case 'minute':
+ return data + '分'
+ case 'second':
+ return data + '秒'
+ default:
+ return data
+ }
+}
+
+const weekdayLabelRenderer = (type: string, data: number) => {
+ switch (type) {
+ case 'year':
+ return data + '年'
+ case 'week':
+ return data + '周'
+ case 'week-day':
+ return weekdayToZh(data)
+ default:
+ return data
}
- render() {
- return (
-
-
- Start DateTime
-
- End DateTime
-
-
-
- );
+}
+
+const dateFilter: DatePickerFilter = {
+ day: (val, { date }) => {
+ // 去除所有周末
+ if (date.getDay() > 5 || date.getDay() === 0) {
+ return false
+ }
+ return true
+ },
+ hour: (val: number) => {
+ // 只保留每天的14点到18点
+ if (val < 14 || val > 18) {
+ return false
+ }
+ return true
+ },
+}
+
+const weekdayToZh = (weekday: number) => {
+ switch (weekday) {
+ case 1:
+ return '周一'
+ case 2:
+ return '周二'
+ case 3:
+ return '周三'
+ case 4:
+ return '周四'
+ case 5:
+ return '周五'
+ case 6:
+ return '周六'
+ case 7:
+ return '周日'
+ default:
+ return weekday
}
}
```
diff --git a/components/date-picker-view/demo/basic.tsx b/components/date-picker-view/demo/basic.tsx
index deb73095a..15f020807 100644
--- a/components/date-picker-view/demo/basic.tsx
+++ b/components/date-picker-view/demo/basic.tsx
@@ -1,42 +1,115 @@
-/* tslint:disable:no-console */
-import React from 'react'
-import { Text, View } from 'react-native'
+import React, { useState } from 'react'
+import { ScrollView, Text } from 'react-native'
import { DatePickerView } from '../../'
+import { DatePickerFilter } from '../../date-picker/date-picker-utils'
-export default class DatePickerViewExample extends React.Component {
- state = {
- value: undefined,
- value12hours: undefined,
- }
- onChange = (value: any) => {
- console.log(value)
- this.setState({ value })
+const now = new Date()
+
+export default () => {
+ const [value, setValue] = useState(now)
+
+ return (
+
+ 基础用法
+
+
+ 受控模式
+ {
+ setValue(val)
+ console.log('onChange', val)
+ }}
+ />
+
+ 自定义每列的渲染内容
+
+
+ 周选择器
+ console.log('onChange', val)}
+ precision="week-day"
+ defaultValue={now}
+ renderLabel={weekdayLabelRenderer}
+ />
+
+ 过滤可供选择的时间
+
+
+ )
+}
+
+const labelRenderer = (type: string, data: number) => {
+ switch (type) {
+ case 'year':
+ return data + '年'
+ case 'month':
+ return data + '月'
+ case 'day':
+ return data + '日'
+ case 'hour':
+ return data + '时'
+ case 'minute':
+ return data + '分'
+ case 'second':
+ return data + '秒'
+ default:
+ return data
}
- onValueChange = (...args: any[]) => {
- console.log(args)
+}
+
+const weekdayLabelRenderer = (type: string, data: number) => {
+ switch (type) {
+ case 'year':
+ return data + '年'
+ case 'week':
+ return data + '周'
+ case 'week-day':
+ return weekdayToZh(data)
+ default:
+ return data
}
- render() {
- return (
-
- use12Hours
- this.setState({ value12hours: v })}
- use12Hours
- />
- Start DateTime
-
- End DateTime
-
-
- )
+}
+
+const dateFilter: DatePickerFilter = {
+ day: (_val, { date }) => {
+ // 去除所有周末
+ if (date.getDay() > 5 || date.getDay() === 0) {
+ return false
+ }
+ return true
+ },
+ hour: (val: number) => {
+ // 只保留每天的14点到18点
+ if (val < 14 || val > 18) {
+ return false
+ }
+ return true
+ },
+}
+
+const weekdayToZh = (weekday: number) => {
+ switch (weekday) {
+ case 1:
+ return '周一'
+ case 2:
+ return '周二'
+ case 3:
+ return '周三'
+ case 4:
+ return '周四'
+ case 5:
+ return '周五'
+ case 6:
+ return '周六'
+ case 7:
+ return '周日'
+ default:
+ return weekday
}
}
diff --git a/components/date-picker-view/index.en-US.md b/components/date-picker-view/index.en-US.md
index 88ea7ab1f..d332ec604 100644
--- a/components/date-picker-view/index.en-US.md
+++ b/components/date-picker-view/index.en-US.md
@@ -8,15 +8,41 @@ DatePickerView's functions like DatePicker, but it is rendered directly in the a
## API
-Properties | Descrition | Type | Default
------------|------------|------|--------
-| mode | mode value, can be a `date` or `time` or `datetime` or `year` or `month` | String | `date` |
+```ts
+type Precision =
+ | 'week'
+ | 'week-day'
+ | 'year'
+ | 'month'
+ | 'day'
+ | 'hour'
+ | 'minute'
+ | 'second'
+
+type DatePickerFilter = Partial<
+ Record<
+ Precision,
+ (
+ val: number,
+ extend: {
+ date: Date
+ }
+ ) => boolean
+ >
+>
+```
+
+Properties | Descrition | Type | Default | Version
+-----------|------------|------|---------|---------
+| precision | Precision | `Precision` | `day` |`5.1.0`|
| value | the currently selected value | Date | - |
+| defaultValue | the default selected value | Date | - ||
| minDate | minimum date | Date | 2000-1-1 |
| maxDate | maximum date | Date | 2030-1-1 |
-| minuteStep | The amount of time, in minutes, between each minute item. | Number | 1 |
-| locale | international, can override the configuration of the global `[LocaleProvider](https://mobile.ant.design/components/locale-provider)` | Object: {DatePickerLocale: {year, month, day, hour, minute, am?, pm?}, okText, dismissText} | - |
-| disabled | disabled | Boolean | false |
-| use12Hours | 12 hours format | Boolean | false |
-| onChange | change handler | (date: Object): void | - |
-| onValueChange | fire when picker col change | (vals: any, index: number) => void | - |
+| onChange | change handler | `(value: Date) => void` | - ||
+| onValueChange | fire when picker col change | `(value: Date, index: number) => void` | - ||
+| renderLabel | The function to custom rendering the label shown on a column. `type` means any value in `precision`, `data` means the default number | `(type:Precision / 'now', data: number) => ReactNode` | - ||
+| filter | Filter available time | `DatePickerFilter` | - | `5.1.0` |
+
+
+In addition, the following attributes of `PickerView` are supported: `style` `styles` `itemStyle` `itemHeight` `numberOfLines` `renderMaskTop` `renderMaskBottom`
diff --git a/components/date-picker-view/index.zh-CN.md b/components/date-picker-view/index.zh-CN.md
index 92a437ef1..b2f984298 100644
--- a/components/date-picker-view/index.zh-CN.md
+++ b/components/date-picker-view/index.zh-CN.md
@@ -2,22 +2,48 @@
category: Components
type: Data Entry
title: DatePickerView
-subtitle: 选择器
+subtitle: 日期选择器
---
DatePickerView 的功能类似于 DatePicker ,但它是直接渲染在区域中,而不是弹出窗口。
## API
-属性 | 说明 | 类型 | 默认值
-----|-----|------|------
-| mode | 日期选择的类型, 可以是日期`date`,时间`time`,日期+时间`datetime`,年`year`,月`month` | String | `date` |
-| value | 当前选中时间 | Date | 无 |
-| minDate | 最小可选日期 | Date | 2000-1-1 |
-| maxDate | 最大可选日期 | Date | 2030-1-1 |
-| minuteStep | 分钟数递增步长设置 | Number | 1 |
-| locale | 国际化,可覆盖全局`[LocaleProvider](https://mobile.ant.design/components/locale-provider)`的配置 | Object: {DatePickerLocale: {year, month, day, hour, minute, am?, pm?}, okText, dismissText } | - |
-| disabled | 是否不可用 | Boolean | false |
-| use12Hours | 12小时制 | Boolean | false |
-| onChange | 时间发生变化的回调函数 | (date: Object): void | - |
-| onValueChange | 每列 picker 改变时的回调 | (vals: any, index: number) => void | - |
+```ts
+type Precision =
+ | 'week'
+ | 'week-day'
+ | 'year'
+ | 'month'
+ | 'day'
+ | 'hour'
+ | 'minute'
+ | 'second'
+
+type DatePickerFilter = Partial<
+ Record<
+ Precision,
+ (
+ val: number,
+ extend: {
+ date: Date
+ }
+ ) => boolean
+ >
+>
+```
+
+属性 | 说明 | 类型 | 默认值 | 版本
+----|-----|------|------|------
+| precision | 精度 | `Precision` | `day` |`5.1.0`|
+| value | 当前选中时间 | Date | 无 ||
+| defaultValue | 默认选中时间 | Date | 无 ||
+| minDate | 最小可选日期 | Date | 2000-1-1 ||
+| maxDate | 最大可选日期 | Date | 2030-1-1 ||
+| onChange | 时间发生变化的回调函数 | `(value: Date) => void` | - ||
+| onValueChange | 每列 picker 改变时的回调 | `(value: Date, index: number) => void` | - ||
+| renderLabel | 自定义渲染每列展示的内容。其中 `type` 参数为 `precision` 中的任意值或 `now`,`data` 参数为默认渲染的数字 | `(type:Precision / 'now', data: number) => ReactNode` | - ||
+| filter | 过滤可供选择的时间 | `DatePickerFilter` | - | `5.1.0` |
+
+
+此外还支持 PickerView 的以下属性:`style` `styles` `itemStyle` `itemHeight` `numberOfLines` `renderMaskTop` `renderMaskBottom`
\ No newline at end of file
diff --git a/components/date-picker-view/locale/en_US.tsx b/components/date-picker-view/locale/en_US.tsx
index 86b41df13..42c9b02df 100644
--- a/components/date-picker-view/locale/en_US.tsx
+++ b/components/date-picker-view/locale/en_US.tsx
@@ -1,3 +1,9 @@
-import DatePickerLocale from '../../date-picker/datepicker/locale/en_US'
-
-export default DatePickerLocale
+export default {
+ year: '',
+ month: '',
+ day: '',
+ hour: '',
+ minute: '',
+ am: 'AM',
+ pm: 'PM',
+}
diff --git a/components/date-picker-view/locale/es_ES.tsx b/components/date-picker-view/locale/es_ES.tsx
index 098313f1d..42c9b02df 100644
--- a/components/date-picker-view/locale/es_ES.tsx
+++ b/components/date-picker-view/locale/es_ES.tsx
@@ -1,3 +1,9 @@
-import DatePickerLocale from '../../date-picker/datepicker/locale/es_ES'
-
-export default DatePickerLocale
+export default {
+ year: '',
+ month: '',
+ day: '',
+ hour: '',
+ minute: '',
+ am: 'AM',
+ pm: 'PM',
+}
diff --git a/components/date-picker-view/locale/fa_IR.tsx b/components/date-picker-view/locale/fa_IR.tsx
index d4f41445f..9809570b1 100644
--- a/components/date-picker-view/locale/fa_IR.tsx
+++ b/components/date-picker-view/locale/fa_IR.tsx
@@ -1,3 +1,9 @@
-import DatePickerLocale from '../../date-picker/datepicker/locale/fa_IR'
-
-export default DatePickerLocale
+export default {
+ year: 'سال',
+ month: 'ماه',
+ day: 'روز',
+ hour: 'ساعت',
+ minute: 'دقیقه',
+ am: 'صبح',
+ pm: 'بعد از ظهر',
+}
diff --git a/components/date-picker/datepicker/locale/id_ID.tsx b/components/date-picker-view/locale/id_ID.tsx
similarity index 100%
rename from components/date-picker/datepicker/locale/id_ID.tsx
rename to components/date-picker-view/locale/id_ID.tsx
diff --git a/components/date-picker-view/locale/ko_KR.tsx b/components/date-picker-view/locale/ko_KR.tsx
new file mode 100644
index 000000000..668fd5683
--- /dev/null
+++ b/components/date-picker-view/locale/ko_KR.tsx
@@ -0,0 +1,9 @@
+export default {
+ year: '년',
+ month: '월',
+ day: '일',
+ hour: '시간',
+ minute: '분',
+ am: '오전',
+ pm: '오후',
+}
diff --git a/components/date-picker-view/locale/pt_BR.tsx b/components/date-picker-view/locale/pt_BR.tsx
index 25989b657..9266e1468 100644
--- a/components/date-picker-view/locale/pt_BR.tsx
+++ b/components/date-picker-view/locale/pt_BR.tsx
@@ -1,3 +1,9 @@
-import DatePickerLocale from '../../date-picker/datepicker/locale/pt_BR'
-
-export default DatePickerLocale
+export default {
+ year: 'Ano',
+ month: 'Mês',
+ day: 'Dia',
+ hour: 'Hora',
+ minute: 'Minuto',
+ am: 'AM',
+ pm: 'PM',
+}
diff --git a/components/date-picker-view/locale/ru_RU.tsx b/components/date-picker-view/locale/ru_RU.tsx
index 78b51a6c9..42c9b02df 100644
--- a/components/date-picker-view/locale/ru_RU.tsx
+++ b/components/date-picker-view/locale/ru_RU.tsx
@@ -1,3 +1,9 @@
-import DatePickerLocale from '../../date-picker/datepicker/locale/ru_RU'
-
-export default DatePickerLocale
+export default {
+ year: '',
+ month: '',
+ day: '',
+ hour: '',
+ minute: '',
+ am: 'AM',
+ pm: 'PM',
+}
diff --git a/components/date-picker-view/locale/sv_SE.tsx b/components/date-picker-view/locale/sv_SE.tsx
index 0b7a64999..42c9b02df 100644
--- a/components/date-picker-view/locale/sv_SE.tsx
+++ b/components/date-picker-view/locale/sv_SE.tsx
@@ -1,3 +1,9 @@
-import DatePickerLocale from '../../date-picker/datepicker/locale/sv_SE'
-
-export default DatePickerLocale
+export default {
+ year: '',
+ month: '',
+ day: '',
+ hour: '',
+ minute: '',
+ am: 'AM',
+ pm: 'PM',
+}
diff --git a/components/date-picker-view/locale/zh_CN.tsx b/components/date-picker-view/locale/zh_CN.tsx
index 3a2b4892e..988abfa5e 100644
--- a/components/date-picker-view/locale/zh_CN.tsx
+++ b/components/date-picker-view/locale/zh_CN.tsx
@@ -1,3 +1,10 @@
-import DatePickerLocale from '../../date-picker/datepicker/locale/zh_CN'
-
-export default DatePickerLocale
+export default {
+ year: '年',
+ month: '月',
+ day: '日',
+ hour: '时',
+ minute: '分',
+ second: '秒',
+ am: '上午',
+ pm: '下午',
+}
diff --git a/components/date-picker-view/useRenderLabel.ts b/components/date-picker-view/useRenderLabel.ts
new file mode 100644
index 000000000..18fa708e2
--- /dev/null
+++ b/components/date-picker-view/useRenderLabel.ts
@@ -0,0 +1,32 @@
+import { useCallback } from 'react'
+import { RenderLabel } from './PropsType'
+
+export default function useRenderLabel(
+ renderLabel?: RenderLabel,
+ locale?: any,
+): RenderLabel {
+ return useCallback(
+ (type, data) => {
+ if (renderLabel) {
+ return renderLabel(type, data)
+ }
+
+ // Default render
+ switch (type) {
+ case 'year':
+ case 'month':
+ case 'day':
+ return data.toString() + (locale?.[type] || '')
+ case 'minute':
+ case 'second':
+ case 'hour':
+ return ('0' + data.toString()).slice(-2) + (locale?.[type] || '')
+ case 'now':
+ return locale.tillNow
+ default:
+ return data.toString()
+ }
+ },
+ [locale, renderLabel],
+ )
+}
diff --git a/components/date-picker/PropsType.tsx b/components/date-picker/PropsType.tsx
index 81f4ab737..30f6a3bf7 100644
--- a/components/date-picker/PropsType.tsx
+++ b/components/date-picker/PropsType.tsx
@@ -1,37 +1,63 @@
-import React from 'react'
-import AntDatePickerProps from './datepicker/DatePickerProps'
-export interface DatePickerPropsType extends AntDatePickerProps {
- value?: Date
- defaultDate?: Date
- mode?: 'datetime' | 'date' | 'year' | 'month' | 'time'
+import type { ReactNode } from 'react'
+import { PickerValueExtend } from '../picker-view/PropsType'
+import { PickerPropsType } from '../picker/PropsType'
+import { DatePickerFilter, Precision } from './date-picker-utils'
+import { PickerDate } from './util'
+
+export type RenderLabel = (type: Precision | 'now', data: number) => ReactNode
+
+export interface DatePickerPropsType
+ extends Pick<
+ PickerPropsType,
+ | 'onPickerChange'
+ | 'onVisibleChange'
+ | 'style'
+ | 'styles'
+ | 'itemStyle'
+ | 'numberOfLines'
+ | 'title'
+ | 'okText'
+ | 'dismissText'
+ | 'visible'
+ | 'children'
+ | 'renderMaskTop'
+ | 'renderMaskBottom'
+ > {
+ value?: PickerDate
+ defaultValue?: PickerDate
+ /**
+ * Please use `defaultValue`.
+ * Although it is also compatible with history, it will be removed in the future.
+ * @deprecated
+ */
+ defaultDate?: PickerDate
+ precision?: Precision
+ /**
+ * Please use `precision`.
+ * Although it is also compatible with history, it will be removed in the future.
+ * @deprecated
+ */
+ mode?: Precision | 'date'
minDate?: Date
maxDate?: Date
- onChange?: (value: Date) => void
- onValueChange?: (vals: any, index: number) => void
- visible?: boolean
- use12Hours?: boolean
- onDismiss?: () => void
- onOk?: () => void
+ onChange?: (value: Date, extend: PickerValueExtend) => void
+ onValueChange?: (value: PickerDate, index: number) => void
+ onOk?: (value: Date, extend: PickerValueExtend) => void
+ renderLabel?: RenderLabel
locale?: {
- okText: string
- dismissText: string
- extra: string
- DatePickerLocale: {
+ okText?: string
+ dismissText?: string
+ extra?: string
+ DatePickerLocale?: {
year: string
month: string
day: string
hour: string
minute: string
- am?: string
- pm?: string
+ am: string
+ pm: string
}
}
- minuteStep?: number
- disabled?: boolean
+ filter?: DatePickerFilter // @5.0.1
format?: string | ((value: Date) => string)
- extra?: string
- children?: React.ReactNode
- dismissText?: React.ReactNode
- okText?: React.ReactNode
- title?: React.ReactNode
}
diff --git a/components/date-picker/columns-extend.tsx b/components/date-picker/columns-extend.tsx
new file mode 100644
index 000000000..0a3554e7a
--- /dev/null
+++ b/components/date-picker/columns-extend.tsx
@@ -0,0 +1,44 @@
+import { useMemo } from 'react'
+import { PickerColumn, PickerValue } from '../picker-view/PropsType'
+import {
+ Precision,
+ convertDateToStringArray,
+ convertStringArrayToDate,
+} from './date-picker-utils'
+
+export function getValueExtend(
+ d: PickerColumn[],
+ val: PickerValue[],
+ mode: Precision,
+) {
+ const extend = d.map(
+ (column: PickerColumn, index: number) =>
+ column.find((item) => item?.value === val[index]) ??
+ (val[index] === undefined ? column[0] : column.slice(-1)[0]),
+ ) as PickerColumn
+ return {
+ dateValue: convertStringArrayToDate(
+ extend.map((item) => item?.value),
+ mode,
+ ),
+ extend,
+ }
+}
+
+// date2array
+export function usePickerValue(
+ val: Date | undefined,
+ minDate: Date,
+ maxDate: Date,
+ mode: Precision,
+) {
+ return useMemo(() => {
+ let value = new Date(val || '')
+ if (isNaN(value.getTime()) || value.getTime() < minDate.getTime()) {
+ value = minDate
+ } else if (value.getTime() > maxDate.getTime()) {
+ value = maxDate
+ }
+ return convertDateToStringArray(value, mode)
+ }, [val, minDate, maxDate, mode])
+}
diff --git a/components/date-picker/date-picker-date-utils.ts b/components/date-picker/date-picker-date-utils.ts
new file mode 100644
index 000000000..e16b26220
--- /dev/null
+++ b/components/date-picker/date-picker-date-utils.ts
@@ -0,0 +1,219 @@
+import dayjs from 'dayjs'
+import isLeapYear from 'dayjs/plugin/isLeapYear'
+import isoWeek from 'dayjs/plugin/isoWeek'
+import isoWeeksInYear from 'dayjs/plugin/isoWeeksInYear'
+import { RenderLabel } from '../date-picker-view/PropsType'
+import { PickerColumn } from '../picker-view/PropsType'
+import type { DatePickerFilter } from './date-picker-utils'
+import { TILL_NOW } from './util'
+
+dayjs.extend(isoWeek)
+dayjs.extend(isoWeeksInYear)
+dayjs.extend(isLeapYear)
+
+export type DatePrecision =
+ | 'year'
+ | 'month'
+ | 'day'
+ | 'hour'
+ | 'minute'
+ | 'second'
+
+const precisionRankRecord: Record = {
+ year: 0,
+ month: 1,
+ day: 2,
+ hour: 3,
+ minute: 4,
+ second: 5,
+}
+
+export function generateDatePickerColumns(
+ selected: string[],
+ min: Date,
+ max: Date,
+ precision: DatePrecision,
+ renderLabel: RenderLabel,
+ filter: DatePickerFilter | undefined,
+ tillNow?: boolean,
+) {
+ const ret: PickerColumn[] = []
+
+ const minYear = min.getFullYear()
+ const minMonth = min.getMonth() + 1
+ const minDay = min.getDate()
+ const minHour = min.getHours()
+ const minMinute = min.getMinutes()
+ const minSecond = min.getSeconds()
+
+ const maxYear = max.getFullYear()
+ const maxMonth = max.getMonth() + 1
+ const maxDay = max.getDate()
+ const maxHour = max.getHours()
+ const maxMinute = max.getMinutes()
+ const maxSecond = max.getSeconds()
+
+ const rank = precisionRankRecord[precision]
+
+ const selectedYear = parseInt(selected[0])
+ const firstDayInSelectedMonth = dayjs(
+ convertStringArrayToDate([selected[0], selected[1], '1']),
+ )
+ const selectedMonth = parseInt(selected[1])
+ const selectedDay = parseInt(selected[2])
+ const selectedHour = parseInt(selected[3])
+ const selectedMinute = parseInt(selected[4])
+
+ const isInMinYear = selectedYear === minYear
+ const isInMaxYear = selectedYear === maxYear
+ const isInMinMonth = isInMinYear && selectedMonth === minMonth
+ const isInMaxMonth = isInMaxYear && selectedMonth === maxMonth
+ const isInMinDay = isInMinMonth && selectedDay === minDay
+ const isInMaxDay = isInMaxMonth && selectedDay === maxDay
+ const isInMinHour = isInMinDay && selectedHour === minHour
+ const isInMaxHour = isInMaxDay && selectedHour === maxHour
+ const isInMinMinute = isInMinHour && selectedMinute === minMinute
+ const isInMaxMinute = isInMaxHour && selectedMinute === maxMinute
+
+ const generateColumn = (
+ from: number,
+ to: number,
+ precision: DatePrecision,
+ ) => {
+ let column: number[] = []
+ for (let i = from; i <= to; i++) {
+ column.push(i)
+ }
+ const prefix = selected.slice(0, precisionRankRecord[precision])
+ const currentFilter = filter?.[precision]
+ if (currentFilter && typeof currentFilter === 'function') {
+ column = column.filter((i) =>
+ currentFilter(i, {
+ get date() {
+ const stringArray = [...prefix, i.toString()]
+ return convertStringArrayToDate(stringArray)
+ },
+ }),
+ )
+ }
+ return column
+ }
+
+ if (rank >= precisionRankRecord.year) {
+ const lower = minYear
+ const upper = maxYear
+ const years = generateColumn(lower, upper, 'year')
+ ret.push(
+ years.map((v) => ({
+ label: renderLabel('year', v),
+ value: v.toString(),
+ })),
+ )
+ }
+
+ if (rank >= precisionRankRecord.month) {
+ const lower = isInMinYear ? minMonth : 1
+ const upper = isInMaxYear ? maxMonth : 12
+ const months = generateColumn(lower, upper, 'month')
+ ret.push(
+ months.map((v) => ({
+ label: renderLabel('month', v),
+ value: v.toString(),
+ })),
+ )
+ }
+ if (rank >= precisionRankRecord.day) {
+ const lower = isInMinMonth ? minDay : 1
+ const upper = isInMaxMonth ? maxDay : firstDayInSelectedMonth.daysInMonth()
+ const days = generateColumn(lower, upper, 'day')
+ ret.push(
+ days.map((v) => ({
+ label: renderLabel('day', v),
+ value: v.toString(),
+ })),
+ )
+ }
+ if (rank >= precisionRankRecord.hour) {
+ const lower = isInMinDay ? minHour : 0
+ const upper = isInMaxDay ? maxHour : 23
+ const hours = generateColumn(lower, upper, 'hour')
+ ret.push(
+ hours.map((v) => ({
+ label: renderLabel('hour', v),
+ value: v.toString(),
+ })),
+ )
+ }
+ if (rank >= precisionRankRecord.minute) {
+ const lower = isInMinHour ? minMinute : 0
+ const upper = isInMaxHour ? maxMinute : 59
+ const minutes = generateColumn(lower, upper, 'minute')
+ ret.push(
+ minutes.map((v) => ({
+ label: renderLabel('minute', v),
+ value: v.toString(),
+ })),
+ )
+ }
+ if (rank >= precisionRankRecord.second) {
+ const lower = isInMinMinute ? minSecond : 0
+ const upper = isInMaxMinute ? maxSecond : 59
+ const seconds = generateColumn(lower, upper, 'second')
+ ret.push(
+ seconds.map((v) => ({
+ label: renderLabel('second', v),
+ value: v.toString(),
+ })),
+ )
+ }
+
+ // Till Now
+ if (tillNow) {
+ ret[0].push({
+ label: renderLabel('now', null!),
+ value: TILL_NOW,
+ })
+
+ if (TILL_NOW === selected?.[0]) {
+ for (let i = 1; i < ret.length; i += 1) {
+ ret[i] = []
+ }
+ }
+ }
+
+ return ret
+}
+
+export function convertDateToStringArray(
+ date: Date | undefined | null,
+): string[] {
+ if (!date) {
+ return []
+ }
+ return [
+ date.getFullYear().toString(),
+ (date.getMonth() + 1).toString(),
+ date.getDate().toString(),
+ date.getHours().toString(),
+ date.getMinutes().toString(),
+ date.getSeconds().toString(),
+ ]
+}
+
+export function convertStringArrayToDate<
+ T extends string | number | null | undefined,
+>(value: T[]): Date {
+ const yearString = value[0] ?? '1900'
+ const monthString = value[1] ?? '1'
+ const dateString = value[2] ?? '1'
+ const hourString = value[3] ?? '0'
+ const minuteString = value[4] ?? '0'
+ const secondString = value[5] ?? '0'
+ return dayjs(
+ `${parseInt(yearString as string)}-${parseInt(
+ monthString as string,
+ )}-${parseInt(dateString as string)} ${parseInt(
+ hourString as string,
+ )}:${parseInt(minuteString as string)}:${parseInt(secondString as string)}`,
+ ).toDate()
+}
diff --git a/components/date-picker/date-picker-utils.ts b/components/date-picker/date-picker-utils.ts
new file mode 100644
index 000000000..2453afdda
--- /dev/null
+++ b/components/date-picker/date-picker-utils.ts
@@ -0,0 +1,94 @@
+import { RenderLabel } from '../date-picker-view/PropsType'
+import type { DatePrecision } from './date-picker-date-utils'
+import * as dateUtils from './date-picker-date-utils'
+import type { WeekPrecision } from './date-picker-week-utils'
+import * as weekUtils from './date-picker-week-utils'
+import type { PickerDate } from './util'
+import { TILL_NOW } from './util'
+
+export type Precision = DatePrecision | WeekPrecision
+
+export type DatePickerFilter = Partial<
+ Record<
+ Precision,
+ (
+ val: number,
+ extend: {
+ date: Date
+ },
+ ) => boolean
+ >
+>
+
+const precisionLengthRecord: Record = {
+ year: 1,
+ month: 2,
+ day: 3,
+ hour: 4,
+ minute: 5,
+ second: 6,
+}
+
+export const convertDateToStringArray = (
+ date: Date | undefined | null,
+ precision: Precision,
+) => {
+ if (precision.includes('week')) {
+ return weekUtils.convertDateToStringArray(date)
+ } else {
+ const datePrecision = precision as DatePrecision
+ const stringArray = dateUtils.convertDateToStringArray(date)
+ return stringArray.slice(0, precisionLengthRecord[datePrecision])
+ }
+}
+
+export const convertStringArrayToDate = <
+ T extends string | number | null | undefined,
+>(
+ value: T[],
+ precision: Precision,
+) => {
+ // Special case for DATE_NOW
+ if (value?.[0] === TILL_NOW) {
+ const now: PickerDate = new Date()
+ now.tillNow = true
+ return now
+ }
+
+ if (precision.includes('week')) {
+ return weekUtils.convertStringArrayToDate(value)
+ } else {
+ return dateUtils.convertStringArrayToDate(value)
+ }
+}
+
+export const generateDatePickerColumns = (
+ selected: string[],
+ min: Date,
+ max: Date,
+ precision: Precision,
+ renderLabel: RenderLabel,
+ filter: DatePickerFilter | undefined,
+ tillNow?: boolean,
+) => {
+ if (precision.startsWith('week')) {
+ return weekUtils.generateDatePickerColumns(
+ selected,
+ min,
+ max,
+ precision as WeekPrecision,
+ renderLabel,
+ filter,
+ )
+ } else {
+ return dateUtils.generateDatePickerColumns(
+ selected,
+ min,
+ max,
+ precision as DatePrecision,
+ renderLabel,
+ filter,
+ tillNow,
+ )
+ }
+}
diff --git a/components/date-picker/date-picker-week-utils.ts b/components/date-picker/date-picker-week-utils.ts
new file mode 100644
index 000000000..79788a763
--- /dev/null
+++ b/components/date-picker/date-picker-week-utils.ts
@@ -0,0 +1,141 @@
+import dayjs from 'dayjs'
+import isLeapYear from 'dayjs/plugin/isLeapYear'
+import isoWeek from 'dayjs/plugin/isoWeek'
+import isoWeeksInYear from 'dayjs/plugin/isoWeeksInYear'
+import type { ReactNode } from 'react'
+import { PickerColumn } from '../picker-view/PropsType'
+import type { DatePickerFilter } from './date-picker-utils'
+
+dayjs.extend(isoWeek)
+dayjs.extend(isoWeeksInYear)
+dayjs.extend(isLeapYear)
+
+export type WeekPrecision = 'year' | 'week' | 'week-day'
+
+const precisionRankRecord: Record = {
+ year: 0,
+ week: 1,
+ 'week-day': 2,
+}
+
+export function generateDatePickerColumns(
+ selected: string[],
+ min: Date,
+ max: Date,
+ precision: WeekPrecision,
+ renderLabel: (type: WeekPrecision, data: number) => ReactNode,
+ filter: DatePickerFilter | undefined,
+) {
+ const ret: PickerColumn[] = []
+
+ const minYear = min.getFullYear()
+ const maxYear = max.getFullYear()
+
+ const rank = precisionRankRecord[precision]
+
+ const selectedYear = parseInt(selected[0])
+ const isInMinYear = selectedYear === minYear
+ const isInMaxYear = selectedYear === maxYear
+
+ const minDay = dayjs(min)
+ const maxDay = dayjs(max)
+ const minWeek = minDay.isoWeek()
+ const maxWeek = maxDay.isoWeek()
+ const minWeekday = minDay.isoWeekday()
+ const maxWeekday = maxDay.isoWeekday()
+ const selectedWeek = parseInt(selected[1])
+ const isInMinWeek = isInMinYear && selectedWeek === minWeek
+ const isInMaxWeek = isInMaxYear && selectedWeek === maxWeek
+ const selectedYearWeeks = dayjs(`${selectedYear}-01-01`).isoWeeksInYear()
+
+ const generateColumn = (
+ from: number,
+ to: number,
+ precision: WeekPrecision,
+ ) => {
+ let column: number[] = []
+ for (let i = from; i <= to; i++) {
+ column.push(i)
+ }
+ const prefix = selected.slice(0, precisionRankRecord[precision])
+ const currentFilter = filter?.[precision]
+ if (currentFilter && typeof currentFilter === 'function') {
+ column = column.filter((i) =>
+ currentFilter(i, {
+ get date() {
+ const stringArray = [...prefix, i.toString()]
+ return convertStringArrayToDate(stringArray)
+ },
+ }),
+ )
+ }
+ return column
+ }
+
+ if (rank >= precisionRankRecord.year) {
+ const lower = minYear
+ const upper = maxYear
+ const years = generateColumn(lower, upper, 'year')
+ ret.push(
+ years.map((v) => ({
+ label: renderLabel('year', v),
+ value: v.toString(),
+ })),
+ )
+ }
+
+ if (rank >= precisionRankRecord.week) {
+ const lower = isInMinYear ? minWeek : 1
+ const upper = isInMaxYear ? maxWeek : selectedYearWeeks
+ const weeks = generateColumn(lower, upper, 'week')
+ ret.push(
+ weeks.map((v) => ({
+ label: renderLabel('week', v),
+ value: v.toString(),
+ })),
+ )
+ }
+ if (rank >= precisionRankRecord['week-day']) {
+ const lower = isInMinWeek ? minWeekday : 1
+ const upper = isInMaxWeek ? maxWeekday : 7
+ const weeks = generateColumn(lower, upper, 'week-day')
+ ret.push(
+ weeks.map((v) => ({
+ label: renderLabel('week-day', v),
+ value: v.toString(),
+ })),
+ )
+ }
+
+ return ret
+}
+
+export function convertDateToStringArray(
+ date: Date | undefined | null,
+): string[] {
+ if (!date) {
+ return []
+ }
+ const day = dayjs(date)
+ return [
+ day.isoWeekYear().toString(),
+ day.isoWeek().toString(),
+ day.isoWeekday().toString(),
+ ]
+}
+
+export function convertStringArrayToDate<
+ T extends string | number | null | undefined,
+>(value: T[]): Date {
+ const yearString = value[0] ?? '1900'
+ const weekString = value[1] ?? '1'
+ const weekdayString = value[2] ?? '1'
+ const day = dayjs()
+ .year(parseInt(yearString as string))
+ .isoWeek(parseInt(weekString as string))
+ .isoWeekday(parseInt(weekdayString as string))
+ .hour(0)
+ .minute(0)
+ .second(0)
+ return day.toDate()
+}
diff --git a/components/date-picker/date-picker.tsx b/components/date-picker/date-picker.tsx
new file mode 100644
index 000000000..64f4355c2
--- /dev/null
+++ b/components/date-picker/date-picker.tsx
@@ -0,0 +1,179 @@
+import dayjs from 'dayjs'
+import React, {
+ forwardRef,
+ useCallback,
+ useContext,
+ useEffect,
+ useImperativeHandle,
+ useMemo,
+ useRef,
+ useState,
+} from 'react'
+import { getComponentLocale } from '../_util/getLocale'
+import { mergeProps } from '../_util/with-default-props'
+import useRenderLabel from '../date-picker-view/useRenderLabel'
+import { LocaleContext } from '../locale-provider'
+import RMCPicker, { PickerRef } from '../picker/Picker'
+import { DatePickerPropsType } from './PropsType'
+import { getValueExtend, usePickerValue } from './columns-extend'
+import {
+ convertStringArrayToDate,
+ generateDatePickerColumns,
+} from './date-picker-utils'
+
+export type DatePickerRef = any
+
+export interface DatePickerProps extends DatePickerPropsType {}
+
+const defaultProps = {
+ defaultDate: new Date(),
+ minDate: new Date('2000-1-1'),
+ maxDate: new Date('2030-1-1'),
+ precision: 'day',
+}
+
+const DatePicker = forwardRef((props, ref) => {
+ const p = mergeProps(defaultProps, props)
+ const {
+ value,
+ defaultValue,
+ defaultDate,
+ minDate,
+ maxDate,
+ mode,
+ precision,
+ renderLabel,
+ filter,
+ ...restProps
+ } = p
+
+ const _precision = precision || (mode === 'date' ? 'day' : mode) || 'day'
+
+ const [innerValue, setInnerValue] = useState(
+ value === undefined ? defaultValue || defaultDate : value,
+ )
+
+ const _locale = getComponentLocale(
+ p,
+ useContext(LocaleContext),
+ 'DatePicker',
+ () => require('./locale/zh_CN'),
+ )
+
+ const mergedRenderLabel = useRenderLabel(
+ renderLabel,
+ _locale.DatePickerLocale,
+ )
+
+ const pickerRef = React.useRef(null)
+ useImperativeHandle(ref, () => pickerRef.current as PickerRef)
+
+ const pickerValue = usePickerValue(innerValue, minDate, maxDate, _precision)
+
+ const columns = useMemo(() => {
+ return generateDatePickerColumns(
+ pickerValue,
+ minDate,
+ maxDate,
+ _precision,
+ mergedRenderLabel,
+ filter,
+ false,
+ )
+ }, [maxDate, mergedRenderLabel, minDate, _precision, filter, pickerValue])
+
+ const handleSelect = useCallback(
+ (val: any, i: number) => {
+ const pvalue = pickerValue.slice(0)
+ pvalue[i] = val
+ const { dateValue } = getValueExtend(columns, pvalue, _precision)
+ setInnerValue(dateValue)
+ p.onValueChange?.(dateValue, i)
+ },
+ [columns, _precision, p, pickerValue],
+ )
+
+ const handleOk = useCallback(
+ (val, ext) => {
+ p.onOk?.(convertStringArrayToDate(val, _precision), ext)
+ },
+ [_precision, p],
+ )
+
+ const handleChange = useCallback(
+ (val, ext) => {
+ p.onChange?.(convertStringArrayToDate(val, _precision), ext)
+ },
+ [_precision, p],
+ )
+
+ // extra format
+ const format = useCallback(
+ (labels: string[]) => {
+ const date = convertStringArrayToDate(labels, _precision)
+ if (typeof p.format === 'function') {
+ return p.format(date)
+ }
+
+ return dayjs(date).format(
+ typeof p.format === 'string'
+ ? p.format
+ : _precision === 'day'
+ ? 'YYYY-MM-DD'
+ : 'YYYY-MM-DD HH:mm:ss',
+ )
+ },
+ [_precision, p],
+ )
+
+ // 记录value是否变化过
+ const isValueChanged = useRef(false)
+
+ const onVisibleChange = useCallback(
+ (visible) => {
+ p.onVisibleChange?.(visible)
+ if (!visible && value !== innerValue && isValueChanged.current) {
+ // 关闭时,如果选中值不同步,恢复为原选中值
+ setInnerValue(value)
+ }
+ },
+ [innerValue, p, value],
+ )
+
+ // for useEffect only on update
+ const isInitialMount = useRef(true)
+
+ useEffect(() => {
+ if (isInitialMount.current) {
+ isInitialMount.current = false
+ // extra update initial
+ pickerRef.current?._updateExtra()
+ } else {
+ isValueChanged.current = true
+ setInnerValue(value)
+ // extra update after value update
+ setTimeout(() => {
+ pickerRef.current?._updateExtra()
+ })
+ }
+ }, [value])
+
+ return (
+
+ )
+})
+
+DatePicker.displayName = 'Picker'
+
+export default DatePicker
diff --git a/components/date-picker/datepicker/DatePicker.tsx b/components/date-picker/datepicker/DatePicker.tsx
deleted file mode 100644
index 4cc08d59b..000000000
--- a/components/date-picker/datepicker/DatePicker.tsx
+++ /dev/null
@@ -1,529 +0,0 @@
-import React from 'react'
-import MultiPicker from '../../picker/MultiPicker'
-import Picker from '../../picker/Picker'
-import DatePickerProps from './DatePickerProps'
-import defaultLocale from './locale/en_US'
-
-function getDaysInMonth(date: any) {
- return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
-}
-
-function pad(n: any) {
- return n < 10 ? `0${n}` : n + ''
-}
-
-function cloneDate(date: any) {
- return new Date(+date)
-}
-
-function setMonth(date: Date, month: number) {
- date.setDate(
- Math.min(
- date.getDate(),
- getDaysInMonth(new Date(date.getFullYear(), month)),
- ),
- )
- date.setMonth(month)
-}
-
-const DATETIME = 'datetime'
-const DATE = 'date'
-const TIME = 'time'
-const MONTH = 'month'
-const YEAR = 'year'
-const ONE_DAY = 24 * 60 * 60 * 1000
-
-class DatePicker extends React.Component {
- static defaultProps = {
- prefixCls: 'rmc-date-picker',
- pickerPrefixCls: 'rmc-picker',
- locale: defaultLocale,
- mode: DATE,
- disabled: false,
- minuteStep: 1,
- onDateChange() {},
- use12Hours: false,
- }
-
- state = {
- date: this.props.date || this.props.defaultDate,
- }
-
- defaultMinDate: any
- defaultMaxDate: any
-
- UNSAFE_componentWillReceiveProps(nextProps: { date: any; defaultDate: any }) {
- if ('date' in nextProps) {
- this.setState({
- date: nextProps.date || nextProps.defaultDate,
- })
- }
- }
-
- getNewDate = (values: any, index: any) => {
- const value = parseInt(values[index], 10)
- const props = this.props
- const { mode } = props
- const newValue = cloneDate(this.getDate())
- if (mode === DATETIME || mode === DATE || mode === YEAR || mode === MONTH) {
- switch (index) {
- case 0:
- newValue.setFullYear(value)
- break
- case 1:
- // Note: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth
- // e.g. from 2017-03-31 to 2017-02-28
- setMonth(newValue, value)
- break
- case 2:
- newValue.setDate(value)
- break
- case 3:
- this.setHours(newValue, value)
- break
- case 4:
- newValue.setMinutes(value)
- break
- case 5:
- this.setAmPm(newValue, value)
- break
- default:
- break
- }
- } else if (mode === TIME) {
- switch (index) {
- case 0:
- this.setHours(newValue, value)
- break
- case 1:
- newValue.setMinutes(value)
- break
- case 2:
- this.setAmPm(newValue, value)
- break
- default:
- break
- }
- }
- return this.clipDate(newValue)
- }
-
- onValueChange = (values: any, index: any) => {
- const props = this.props
- const newValue = this.getNewDate(values, index)
- if (!('date' in props)) {
- this.setState({
- date: newValue,
- })
- }
- if (props.onDateChange) {
- props.onDateChange(newValue)
- }
- if (props.onValueChange) {
- props.onValueChange(values, index)
- }
- }
-
- onScrollChange = (values: any, index: any) => {
- const props = this.props
- if (props.onScrollChange) {
- const newValue = this.getNewDate(values, index)
- props.onScrollChange(newValue, values, index)
- }
- }
-
- setHours(date: Date, hour: number) {
- if (this.props.use12Hours) {
- const dh = date.getHours()
- let nhour = hour
- nhour = dh >= 12 ? hour + 12 : hour
- nhour = nhour >= 24 ? 0 : nhour // Make sure no more than one day
- date.setHours(nhour)
- } else {
- date.setHours(hour)
- }
- }
-
- setAmPm(date: any, index: any) {
- if (index === 0) {
- date.setTime(+date - ONE_DAY / 2)
- } else {
- date.setTime(+date + ONE_DAY / 2)
- }
- }
-
- getDefaultMinDate() {
- if (!this.defaultMinDate) {
- this.defaultMinDate = new Date(2000, 0, 1, 0, 0, 0)
- }
- return this.defaultMinDate
- }
-
- getDefaultMaxDate() {
- if (!this.defaultMaxDate) {
- this.defaultMaxDate = new Date(2030, 0, 1, 23, 59, 59)
- }
- return this.defaultMaxDate
- }
-
- getDate() {
- return this.clipDate(
- this.state.date || this.props.defaultDate || this.getDefaultMinDate(),
- )
- }
-
- // used by rmc-picker/lib/PopupMixin.js
- getValue() {
- return this.getDate()
- }
-
- getMinYear() {
- return this.getMinDate().getFullYear()
- }
-
- getMaxYear() {
- return this.getMaxDate().getFullYear()
- }
-
- getMinMonth() {
- return this.getMinDate().getMonth()
- }
-
- getMaxMonth() {
- return this.getMaxDate().getMonth()
- }
-
- getMinDay() {
- return this.getMinDate().getDate()
- }
-
- getMaxDay() {
- return this.getMaxDate().getDate()
- }
-
- getMinHour() {
- return this.getMinDate().getHours()
- }
-
- getMaxHour() {
- return this.getMaxDate().getHours()
- }
-
- getMinMinute() {
- return this.getMinDate().getMinutes()
- }
-
- getMaxMinute() {
- return this.getMaxDate().getMinutes()
- }
-
- getMinDate() {
- return this.props.minDate || this.getDefaultMinDate()
- }
-
- getMaxDate() {
- return this.props.maxDate || this.getDefaultMaxDate()
- }
-
- getDateData() {
- const { locale, formatMonth, formatDay, mode } = this.props
- const date = this.getDate()
- const selYear = date.getFullYear()
- const selMonth = date.getMonth()
- const minDateYear = this.getMinYear()
- const maxDateYear = this.getMaxYear()
- const minDateMonth = this.getMinMonth()
- const maxDateMonth = this.getMaxMonth()
- const minDateDay = this.getMinDay()
- const maxDateDay = this.getMaxDay()
- const years: any[] = []
- for (let i = minDateYear; i <= maxDateYear; i++) {
- years.push({
- value: i + '',
- label: i + locale.year + '',
- })
- }
- const yearCol = { key: 'year', props: { children: years } }
- if (mode === YEAR) {
- return [yearCol]
- }
-
- const months: any[] = []
- let minMonth = 0
- let maxMonth = 11
- if (minDateYear === selYear) {
- minMonth = minDateMonth
- }
- if (maxDateYear === selYear) {
- maxMonth = maxDateMonth
- }
- for (let i = minMonth; i <= maxMonth; i++) {
- const label = formatMonth
- ? formatMonth(i, date)
- : i + 1 + locale.month + ''
- months.push({
- value: i + '',
- label,
- })
- }
- const monthCol = { key: 'month', props: { children: months } }
- if (mode === MONTH) {
- return [yearCol, monthCol]
- }
-
- const days: any[] = []
- let minDay = 1
- let maxDay = getDaysInMonth(date)
-
- if (minDateYear === selYear && minDateMonth === selMonth) {
- minDay = minDateDay
- }
- if (maxDateYear === selYear && maxDateMonth === selMonth) {
- maxDay = maxDateDay
- }
- for (let i = minDay; i <= maxDay; i++) {
- const label = formatDay ? formatDay(i, date) : i + locale.day + ''
- days.push({
- value: i + '',
- label,
- })
- }
- return [yearCol, monthCol, { key: 'day', props: { children: days } }]
- }
-
- getDisplayHour(rawHour: number) {
- // 12 hour am (midnight 00:00) -> 12 hour pm (noon 12:00) -> 12 hour am (midnight 00:00)
- if (this.props.use12Hours) {
- if (rawHour === 0) {
- rawHour = 12
- }
- if (rawHour > 12) {
- rawHour -= 12
- }
- return rawHour
- }
- return rawHour
- }
-
- getTimeData(date: any) {
- let minHour = 0
- let maxHour = 23
- let minMinute = 0
- let maxMinute = 59
- const { mode, locale, minuteStep, use12Hours } = this.props
- const minDateMinute = this.getMinMinute()
- const maxDateMinute = this.getMaxMinute()
- const minDateHour = this.getMinHour()
- const maxDateHour = this.getMaxHour()
- const hour = date.getHours()
- if (mode === DATETIME) {
- const year = date.getFullYear()
- const month = date.getMonth()
- const day = date.getDate()
- const minDateYear = this.getMinYear()
- const maxDateYear = this.getMaxYear()
- const minDateMonth = this.getMinMonth()
- const maxDateMonth = this.getMaxMonth()
- const minDateDay = this.getMinDay()
- const maxDateDay = this.getMaxDay()
- if (
- minDateYear === year &&
- minDateMonth === month &&
- minDateDay === day
- ) {
- minHour = minDateHour
- if (minDateHour === hour) {
- minMinute = minDateMinute
- }
- }
- if (
- maxDateYear === year &&
- maxDateMonth === month &&
- maxDateDay === day
- ) {
- maxHour = maxDateHour
- if (maxDateHour === hour) {
- maxMinute = maxDateMinute
- }
- }
- } else {
- minHour = minDateHour
- if (minDateHour === hour) {
- minMinute = minDateMinute
- }
- maxHour = maxDateHour
- if (maxDateHour === hour) {
- maxMinute = maxDateMinute
- }
- }
-
- const hours: any[] = []
- if ((minHour === 0 && maxHour === 0) || (minHour !== 0 && maxHour !== 0)) {
- minHour = this.getDisplayHour(minHour)
- } else if (minHour === 0 && use12Hours) {
- minHour = 1
- hours.push({
- value: '0',
- label: locale.hour ? '12' + locale.hour : '12',
- })
- }
- maxHour = this.getDisplayHour(maxHour)
- for (let i = minHour; i <= maxHour; i++) {
- hours.push({
- value: i + '',
- label: locale.hour ? i + locale.hour + '' : pad(i),
- })
- }
-
- const minutes: any[] = []
- const selMinute = date.getMinutes()
- for (let i = minMinute; i <= maxMinute; i += minuteStep!) {
- minutes.push({
- value: i + '',
- label: locale.minute ? i + locale.minute + '' : pad(i),
- })
- if (selMinute > i && selMinute < i + minuteStep!) {
- minutes.push({
- value: selMinute + '',
- label: locale.minute
- ? selMinute + locale.minute + ''
- : pad(selMinute),
- })
- }
- }
- const cols = [
- { key: 'hours', props: { children: hours } },
- { key: 'minutes', props: { children: minutes } },
- ].concat(
- use12Hours
- ? [
- {
- key: 'ampm',
- props: {
- children: [
- { value: '0', label: locale.am },
- { value: '1', label: locale.pm },
- ],
- },
- },
- ]
- : [],
- )
- return { cols, selMinute }
- }
-
- clipDate(date: any) {
- const { mode } = this.props
- const minDate = this.getMinDate()
- const maxDate = this.getMaxDate()
- if (mode === DATETIME) {
- if (date < minDate) {
- return cloneDate(minDate)
- }
- if (date > maxDate) {
- return cloneDate(maxDate)
- }
- } else if (mode === DATE || mode === YEAR || mode === MONTH) {
- // compare-two-dates: https://stackoverflow.com/a/14629978/2190503
- if (+date + ONE_DAY <= minDate) {
- return cloneDate(minDate)
- }
- if (date >= +maxDate + ONE_DAY) {
- return cloneDate(maxDate)
- }
- } else if (mode === TIME) {
- const maxHour = maxDate.getHours()
- const maxMinutes = maxDate.getMinutes()
- const minHour = minDate.getHours()
- const minMinutes = minDate.getMinutes()
- const hour = date.getHours()
- const minutes = date.getMinutes()
- if (hour < minHour || (hour === minHour && minutes < minMinutes)) {
- return cloneDate(minDate)
- }
- if (hour > maxHour || (hour === maxHour && minutes > maxMinutes)) {
- return cloneDate(maxDate)
- }
- }
- return date
- }
-
- getValueCols() {
- const { mode, use12Hours } = this.props
- const date = this.getDate()
- let cols: any[] = []
- let value: any[] = []
-
- if (mode === YEAR) {
- return {
- cols: this.getDateData(),
- value: [date.getFullYear() + ''],
- }
- }
-
- if (mode === MONTH) {
- return {
- cols: this.getDateData(),
- value: [date.getFullYear() + '', date.getMonth() + ''],
- }
- }
-
- if (mode === DATETIME || mode === DATE) {
- cols = this.getDateData()
- value = [
- date.getFullYear() + '',
- date.getMonth() + '',
- date.getDate() + '',
- ]
- }
-
- if (mode === DATETIME || mode === TIME) {
- const time = this.getTimeData(date)
- cols = cols.concat(time.cols)
- const hour = date.getHours()
- let dtValue = [hour + '', time.selMinute + '']
- let nhour = hour
- if (use12Hours) {
- nhour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour
- dtValue = [nhour + '', time.selMinute + '', (hour >= 12 ? 1 : 0) + '']
- }
- value = value.concat(dtValue)
- }
- return {
- value,
- cols,
- }
- }
-
- render() {
- const { value, cols } = this.getValueCols()
- const { disabled, rootNativeProps, style, itemStyle } = this.props
-
- return (
-
- {cols.map((p) => (
-
- {p.props.children.map((item: any) => {
- return (
-
- {item.label}
-
- )
- })}
-
- ))}
-
- )
- }
-}
-
-export default DatePicker
diff --git a/components/date-picker/datepicker/DatePickerProps.tsx b/components/date-picker/datepicker/DatePickerProps.tsx
deleted file mode 100644
index afdc136bc..000000000
--- a/components/date-picker/datepicker/DatePickerProps.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { StyleProp, TextStyle, ViewStyle } from 'react-native'
-
-interface DatePickerProps {
- date?: any
- defaultDate?: any
- minDate?: any
- maxDate?: any
- mode?: string
- disabled?: boolean
- locale?: any
- minuteStep?: number
- formatMonth?: (month: number, date?: any) => any
- formatDay?: (day: number, date?: any) => any
- onDateChange?: (date: any) => void
- onValueChange?: (vals: any, index: number) => void
- itemStyle?: StyleProp
- style?: StyleProp
- onScrollChange?: (date: any, vals: any, index: number) => void
- rootNativeProps?: {}
- use12Hours?: boolean
-}
-
-export default DatePickerProps
diff --git a/components/date-picker/datepicker/Popup.tsx b/components/date-picker/datepicker/Popup.tsx
deleted file mode 100644
index c27a95fa7..000000000
--- a/components/date-picker/datepicker/Popup.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from 'react'
-import PopupPicker from '../../picker/Popup'
-import { PopupPickerProps } from '../../picker/PopupPickerTypes'
-import DatePickerProps from './DatePickerProps'
-
-export interface PopupDatePickerProps extends PopupPickerProps {
- datePicker: React.ReactElement
- onChange?: (date?: any) => void
- date?: any
-}
-
-class PopupDatePicker extends React.Component {
- static defaultProps = {
- pickerValueProp: 'date',
- pickerValueChangeProp: 'onDateChange',
- }
-
- onOk = (v: any) => {
- const { onChange, onOk } = this.props
- if (onChange) {
- onChange(v)
- }
- if (onOk) {
- onOk(v)
- }
- }
-
- render() {
- return (
-
- )
- }
-}
-
-export default PopupDatePicker
diff --git a/components/date-picker/datepicker/PopupStyles.tsx b/components/date-picker/datepicker/PopupStyles.tsx
deleted file mode 100644
index 702919b45..000000000
--- a/components/date-picker/datepicker/PopupStyles.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '../../picker/PopupStyles'
diff --git a/components/date-picker/datepicker/index.tsx b/components/date-picker/datepicker/index.tsx
deleted file mode 100644
index 1827e7506..000000000
--- a/components/date-picker/datepicker/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './DatePicker'
diff --git a/components/date-picker/datepicker/locale/en_US.tsx b/components/date-picker/datepicker/locale/en_US.tsx
deleted file mode 100644
index 42c9b02df..000000000
--- a/components/date-picker/datepicker/locale/en_US.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-export default {
- year: '',
- month: '',
- day: '',
- hour: '',
- minute: '',
- am: 'AM',
- pm: 'PM',
-}
diff --git a/components/date-picker/datepicker/locale/es_ES.tsx b/components/date-picker/datepicker/locale/es_ES.tsx
deleted file mode 100644
index 42c9b02df..000000000
--- a/components/date-picker/datepicker/locale/es_ES.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-export default {
- year: '',
- month: '',
- day: '',
- hour: '',
- minute: '',
- am: 'AM',
- pm: 'PM',
-}
diff --git a/components/date-picker/datepicker/locale/fa_IR.tsx b/components/date-picker/datepicker/locale/fa_IR.tsx
deleted file mode 100644
index 9809570b1..000000000
--- a/components/date-picker/datepicker/locale/fa_IR.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-export default {
- year: 'سال',
- month: 'ماه',
- day: 'روز',
- hour: 'ساعت',
- minute: 'دقیقه',
- am: 'صبح',
- pm: 'بعد از ظهر',
-}
diff --git a/components/date-picker/datepicker/locale/ko_KR.tsx b/components/date-picker/datepicker/locale/ko_KR.tsx
new file mode 100644
index 000000000..668fd5683
--- /dev/null
+++ b/components/date-picker/datepicker/locale/ko_KR.tsx
@@ -0,0 +1,9 @@
+export default {
+ year: '년',
+ month: '월',
+ day: '일',
+ hour: '시간',
+ minute: '분',
+ am: '오전',
+ pm: '오후',
+}
diff --git a/components/date-picker/datepicker/locale/pt_BR.tsx b/components/date-picker/datepicker/locale/pt_BR.tsx
deleted file mode 100644
index 9266e1468..000000000
--- a/components/date-picker/datepicker/locale/pt_BR.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-export default {
- year: 'Ano',
- month: 'Mês',
- day: 'Dia',
- hour: 'Hora',
- minute: 'Minuto',
- am: 'AM',
- pm: 'PM',
-}
diff --git a/components/date-picker/datepicker/locale/ru_RU.tsx b/components/date-picker/datepicker/locale/ru_RU.tsx
deleted file mode 100644
index 42c9b02df..000000000
--- a/components/date-picker/datepicker/locale/ru_RU.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-export default {
- year: '',
- month: '',
- day: '',
- hour: '',
- minute: '',
- am: 'AM',
- pm: 'PM',
-}
diff --git a/components/date-picker/datepicker/locale/sv_SE.tsx b/components/date-picker/datepicker/locale/sv_SE.tsx
deleted file mode 100644
index 42c9b02df..000000000
--- a/components/date-picker/datepicker/locale/sv_SE.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-export default {
- year: '',
- month: '',
- day: '',
- hour: '',
- minute: '',
- am: 'AM',
- pm: 'PM',
-}
diff --git a/components/date-picker/datepicker/locale/zh_CN.tsx b/components/date-picker/datepicker/locale/zh_CN.tsx
deleted file mode 100644
index 442b57afa..000000000
--- a/components/date-picker/datepicker/locale/zh_CN.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-export default {
- year: '年',
- month: '月',
- day: '日',
- hour: '时',
- minute: '分',
- am: '上午',
- pm: '下午',
-}
diff --git a/components/date-picker/index.en-US.md b/components/date-picker/index.en-US.md
index 17cfa8b90..a3d2353a5 100644
--- a/components/date-picker/index.en-US.md
+++ b/components/date-picker/index.en-US.md
@@ -7,31 +7,57 @@ title: DatePicker
Used to select a date or time.
### Rules
-- A maximum of five independent rollers are shown, each of which represents a different value.
+- At most accurate to seconds.
## API
+```ts
+type Precision =
+ | 'week'
+ | 'week-day'
+ | 'year'
+ | 'month'
+ | 'day'
+ | 'hour'
+ | 'minute'
+ | 'second'
+
+type DatePickerFilter = Partial<
+ Record<
+ Precision,
+ (
+ val: number,
+ extend: {
+ date: Date
+ }
+ ) => boolean
+ >
+>
+```
+
+Properties | Descrition | Type | Default | Version
+-----------|------------|------|--------|--------
+| precision | Precision | `Precision` | `day` |`5.1.0`|
+| value | the currently selected value | Date | - ||
+| defaultValue | the default selected value | Date | - ||
+| minDate | minimum date | Date | 2000-1-1 ||
+| maxDate | maximum date | Date | 2030-1-1 ||
+| onChange | change handler | `(value: Date) => void` | - ||
+| onValueChange | fire when picker col change | `(value: Date, index: number) => void` | - ||
+| renderLabel | The function to custom rendering the label shown on a column. `type` means any value in `precision`, `data` means the default number | `(type:Precision / 'now', data: number) => ReactNode` | - ||
+| locale | international, can override the configuration of the global [LocaleProvider](/components/locale-provider) | Object: Object: {okText, dismissText, extra, `DatePickerLocale:{ year,month,day,hour,minute,am,pm }`} | - |
+| filter | Filter available time | `DatePickerFilter` | - | `5.1.0` |
+
+
+In addition, the following attributes of `Picker` are supported: `onPickerChange` `onVisibleChange` `style` `styles` `itemStyle` `itemHeight` `numberOfLines` `title` `okText` `dismissText` `visible` `children` `renderMaskTop` `renderMaskBottom`
+
+### Children
+Same as [Picker](/components/picker/#Children), except type `format` is different:
+
Properties | Descrition | Type | Default
------------|------------|------|--------
-| mode | mode value, can be a `date` or `time` or `datetime` or `year` or `month` | String | `date` |
-| value | the currently selected value | Date | - |
-| defaultDate | the default selected value | Date | - |
-| minDate | minimum date | Date | 2000-1-1 |
-| maxDate | maximum date | Date | 2030-1-1 |
-| minuteStep | The amount of time, in minutes, between each minute item. | Number | 1 |
-| locale | international, can override the configuration of the global `[LocaleProvider](https://mobile.ant.design/components/locale-provider)` | Object: {DatePickerLocale: {year, month, day, hour, minute, am?, pm?}, okText, dismissText} | - |
-| disabled | set disabled | Boolean | false |
-| onChange | change handler | (date: Object): void | - |
-| onValueChange | fire when picker col change | (vals: any, index: number) => void | - |
-| format | format the selected value | `(value: Date) => date string` / `format string`(corresponding mode under the format are: `YYYY-MM-DD` or `HH:mm` or `YYYY-MM-DD HH:mm`) | - |
-| title | title | string/React.ReactElement | - |
-| itemStyle | itemStyle | StyleProp;
- | - |
-| extra | the display text | String | `请选择` |
-| onOk | handler called when click ok | (val): void | - |
-| onDismiss | handler called when click cancel | (): void | - |
-
-Note: The date strings have different implementations in different browsers. For example, `new Date ('2017-1-1')` is an Invalid Date on Safari but is parsed properly on Chrome.
-
-Note: We suggest DatePicker's children to be `List.Item`, if not, you need to be a custom component which accept and handle `onClick` / `extra` / `chidlren` props, see [demo](/components/date-picker)
+----|-----|------|------
+| format | format the selected value |`(value: Date) => date string` | import [Day.js Format](https://day.js.org/docs/en/parse/string-format), precision:`YYYY-MM-DD`,`YYYY-MM-DD HH:mm:ss`|
+
+### Ref
+Same as `Picker`
\ No newline at end of file
diff --git a/components/date-picker/index.tsx b/components/date-picker/index.tsx
index 83edcffa7..ae7f1b350 100644
--- a/components/date-picker/index.tsx
+++ b/components/date-picker/index.tsx
@@ -1,71 +1,3 @@
-import React from 'react'
-import PickerStyles, { PickerStyle } from '../picker/style/index'
-import { WithTheme, WithThemeStyles } from '../style'
-import { getComponentLocale } from '../_util/getLocale'
-import AntDatePicker from './datepicker'
-import PopupDatePicker from './datepicker/Popup'
-import { DatePickerPropsType } from './PropsType'
-import { formatProps } from './utils'
-import { LocaleContext } from '../locale-provider'
+import DatePicker from './date-picker'
-export interface DatePickerProps
- extends DatePickerPropsType,
- WithThemeStyles {
- triggerTypes?: string
-}
-
-export default class DatePicker extends React.Component {
- static defaultProps = {
- mode: 'datetime',
- triggerType: 'onPress',
- minuteStep: 1,
- }
- static contextType = LocaleContext
- render() {
- const { children, value, defaultDate, itemStyle, ...restProps } = this.props
- const locale = getComponentLocale(
- this.props,
- (this as any).context,
- 'DatePicker',
- () => require('./locale/zh_CN'),
- )
-
- const { okText, dismissText, extra, DatePickerLocale } = locale
-
- const dataPicker = (
-
- )
-
- return (
-
- {(styles) => (
-
- {children &&
- React.isValidElement(children) &&
- React.cloneElement
- )}
-
- )
- }
-}
+export default DatePicker
diff --git a/components/date-picker/index.zh-CN.md b/components/date-picker/index.zh-CN.md
index 7cb049bfd..f62255e00 100644
--- a/components/date-picker/index.zh-CN.md
+++ b/components/date-picker/index.zh-CN.md
@@ -8,31 +8,57 @@ subtitle: 日期选择
用于选择日期或者时间。
### 规则
-- 最多展示 5 个独立滚轮,每个滚轮表示一个不同的值。
+- 最多精确到秒。
## API
+```ts
+type Precision =
+ | 'week'
+ | 'week-day'
+ | 'year'
+ | 'month'
+ | 'day'
+ | 'hour'
+ | 'minute'
+ | 'second'
+
+type DatePickerFilter = Partial<
+ Record<
+ Precision,
+ (
+ val: number,
+ extend: {
+ date: Date
+ }
+ ) => boolean
+ >
+>
+```
+
+属性 | 说明 | 类型 | 默认值 | 版本
+----|-----|------|------|------
+| precision | 精度 | `Precision` | `day` |`5.1.0`|
+| value | 当前选中时间 | Date | 无 ||
+| defaultValue | 默认选中时间 | Date | 无 ||
+| minDate | 最小可选日期 | Date | 2000-1-1 ||
+| maxDate | 最大可选日期 | Date | 2030-1-1 ||
+| onChange | 时间发生变化的回调函数 | `(value: Date) => void` | - ||
+| onValueChange | 每列 picker 改变时的回调 | `(value: Date, index: number) => void` | - ||
+| renderLabel | 自定义渲染每列展示的内容。其中 `type` 参数为 `precision` 中的任意值或 `now`,`data` 参数为默认渲染的数字 | `(type:Precision / 'now', data: number) => ReactNode` | - ||
+| locale | 国际化,可覆盖全局[LocaleProvider](/components/locale-provider-cn)的配置 | Object: {okText, dismissText, extra, `DatePickerLocale:{ year,month,day,hour,minute,am,pm }`} | - |
+| filter | 过滤可供选择的时间 | `DatePickerFilter` | - | `5.1.0` |
+
+
+此外还支持 Picker 的以下属性:`onPickerChange` `onVisibleChange` `style` `styles` `itemStyle` `itemHeight` `numberOfLines` `title` `okText` `dismissText` `visible` `children` `renderMaskTop` `renderMaskBottom`
+
+### Children
+同 [Picker](/components/picker-cn/#Children),其中`format`类型不同:
+
属性 | 说明 | 类型 | 默认值
----|-----|------|------
-| mode | 日期选择的类型, 可以是日期`date`,时间`time`,日期+时间`datetime`,年`year`,月`month` | String | `date` |
-| value | 当前选中时间 | Date | 无 |
-| defaultDate | 默认选中时间 | Date | 无 |
-| minDate | 最小可选日期 | Date | 2000-1-1 |
-| maxDate | 最大可选日期 | Date | 2030-1-1 |
-| minuteStep | 分钟数递增步长设置 | Number | 1 |
-| locale | 国际化,可覆盖全局`[LocaleProvider](https://mobile.ant.design/components/locale-provider)`的配置 | Object: {DatePickerLocale: {year, month, day, hour, minute, am?, pm?}, okText, dismissText } | - |
-| disabled | 是否不可用 | Boolean | false |
-| onChange | 时间发生变化的回调函数 | (date: Object): void | - |
-| onValueChange | 每列 picker 改变时的回调 | (vals: any, index: number) => void | - |
-| format | 格式化选中的值 | `(value: Date) => date string` / `format string`(对应 mode 下格式分别为:`YYYY-MM-DD`,`HH:mm`,`YYYY-MM-DD HH:mm`) | - |
-| title | 弹框的标题 | string/React.ReactElement | 无 |
-| itemStyle | itemStyle | StyleProp;
- | - |
-| extra | 显示文案 | String | `请选择` |
-| onOk | 点击选中时执行的回调 | (val): void | 无 |
-| onDismiss | 点击取消时执行的回调 | (): void | 无 |
-
-注意:日期字符串在不同浏览器有不同的实现,例如 `new Date('2017-1-1')` 在 Safari 上是 Invalid Date,而在 Chrome 上是能正常解析的。
-
-注意:`DatePicker` children 建议是 `List.Item`, 如果不是,需要是自定义组件(组件内需处理 `onClick` / `extra` / `children` 属性,详情请看 [demo](/components/date-picker-cn#demoTitle)
+| format | 格式化选中的值 |`(value: Date) => date string` | 引用 [Day.js Format](https://day.js.org/docs/en/parse/string-format),参数对应精度:`YYYY-MM-DD`,`YYYY-MM-DD HH:mm:ss`|
+
+### Ref
+同 `Picker`。
diff --git a/components/date-picker/locale/en_US.tsx b/components/date-picker/locale/en_US.tsx
index 68106d368..9fb4189d0 100644
--- a/components/date-picker/locale/en_US.tsx
+++ b/components/date-picker/locale/en_US.tsx
@@ -1,8 +1,7 @@
-import DatePickerLocale from '../datepicker/locale/en_US'
+import DatePickerLocale from '../../date-picker-view/locale/en_US'
+import PickerLocale from '../../picker/locale/en_US'
export default {
- okText: 'OK',
- dismissText: 'Cancel',
- extra: 'please select',
+ ...PickerLocale,
DatePickerLocale,
}
diff --git a/components/date-picker/locale/es_ES.tsx b/components/date-picker/locale/es_ES.tsx
index 8d8551d16..0c092bfb8 100644
--- a/components/date-picker/locale/es_ES.tsx
+++ b/components/date-picker/locale/es_ES.tsx
@@ -1,8 +1,7 @@
-import DatePickerLocale from '../datepicker/locale/es_ES'
+import DatePickerLocale from '../../date-picker-view/locale/es_ES'
+import PickerLocale from '../../picker/locale/es_ES'
export default {
- okText: 'OK',
- dismissText: 'Cancelar',
- extra: 'Seleccione',
+ ...PickerLocale,
DatePickerLocale,
}
diff --git a/components/date-picker/locale/fa_IR.tsx b/components/date-picker/locale/fa_IR.tsx
index 516e2692a..2f8dd4308 100644
--- a/components/date-picker/locale/fa_IR.tsx
+++ b/components/date-picker/locale/fa_IR.tsx
@@ -1,8 +1,7 @@
-import DatePickerLocale from '../datepicker/locale/fa_IR'
+import DatePickerLocale from '../../date-picker-view/locale/fa_IR'
+import PickerLocale from '../../picker/locale/fa_IR'
export default {
- okText: 'تایید',
- dismissText: 'لغو',
- extra: 'لطفا انتخاب کنید',
+ ...PickerLocale,
DatePickerLocale,
}
diff --git a/components/date-picker/locale/id_ID.tsx b/components/date-picker/locale/id_ID.tsx
new file mode 100644
index 000000000..76d1c06a1
--- /dev/null
+++ b/components/date-picker/locale/id_ID.tsx
@@ -0,0 +1,7 @@
+import DatePickerLocale from '../../date-picker-view/locale/id_ID'
+import PickerLocale from '../../picker/locale/id_ID'
+
+export default {
+ ...PickerLocale,
+ DatePickerLocale,
+}
diff --git a/components/date-picker/locale/ko_KR.tsx b/components/date-picker/locale/ko_KR.tsx
new file mode 100644
index 000000000..69a87e0de
--- /dev/null
+++ b/components/date-picker/locale/ko_KR.tsx
@@ -0,0 +1,7 @@
+import DatePickerLocale from '../../date-picker-view/locale/ko_KR'
+import PickerLocale from '../../picker/locale/ko_KR'
+
+export default {
+ ...PickerLocale,
+ DatePickerLocale,
+}
diff --git a/components/date-picker/locale/pt_BR.tsx b/components/date-picker/locale/pt_BR.tsx
index 4161e555e..6f0c4d4a6 100644
--- a/components/date-picker/locale/pt_BR.tsx
+++ b/components/date-picker/locale/pt_BR.tsx
@@ -1,8 +1,7 @@
-import DatePickerLocale from '../datepicker/locale/pt_BR'
+import DatePickerLocale from '../../date-picker-view/locale/pt_BR'
+import PickerLocale from '../../picker/locale/pt_BR'
export default {
- okText: 'OK',
- dismissText: 'Cancelar',
- extra: 'Selecione',
+ ...PickerLocale,
DatePickerLocale,
}
diff --git a/components/date-picker/locale/ru_RU.tsx b/components/date-picker/locale/ru_RU.tsx
index 9df3bd696..c0d8425ff 100644
--- a/components/date-picker/locale/ru_RU.tsx
+++ b/components/date-picker/locale/ru_RU.tsx
@@ -1,8 +1,7 @@
-import DatePickerLocale from '../datepicker/locale/en_US'
+import DatePickerLocale from '../../date-picker-view/locale/ru_RU'
+import PickerLocale from '../../picker/locale/ru_RU'
export default {
- okText: 'Ок',
- dismissText: 'Отмена',
- extra: '',
+ ...PickerLocale,
DatePickerLocale,
}
diff --git a/components/date-picker/locale/sv_SE.tsx b/components/date-picker/locale/sv_SE.tsx
index 2ee1f27d3..fcd011120 100644
--- a/components/date-picker/locale/sv_SE.tsx
+++ b/components/date-picker/locale/sv_SE.tsx
@@ -1,8 +1,7 @@
-import DatePickerLocale from '../datepicker/locale/en_US'
+import DatePickerLocale from '../../date-picker-view/locale/sv_SE'
+import PickerLocale from '../../picker/locale/sv_SE'
export default {
- okText: 'Ok',
- dismissText: 'Avbryt',
- extra: 'vänligen välj',
+ ...PickerLocale,
DatePickerLocale,
}
diff --git a/components/date-picker/locale/zh_CN.tsx b/components/date-picker/locale/zh_CN.tsx
index 76c0b536b..ca81ee9e0 100644
--- a/components/date-picker/locale/zh_CN.tsx
+++ b/components/date-picker/locale/zh_CN.tsx
@@ -1,8 +1,7 @@
-import DatePickerLocale from '../datepicker/locale/zh_CN'
+import DatePickerLocale from '../../date-picker-view/locale/zh_CN'
+import PickerLocale from '../../picker/locale/zh_CN'
export default {
- okText: '确定',
- dismissText: '取消',
- extra: '请选择',
+ ...PickerLocale,
DatePickerLocale,
}
diff --git a/components/date-picker/util.ts b/components/date-picker/util.ts
new file mode 100644
index 000000000..7a209410a
--- /dev/null
+++ b/components/date-picker/util.ts
@@ -0,0 +1,5 @@
+export const TILL_NOW = 'TILL_NOW'
+
+export type PickerDate = Date & {
+ tillNow?: boolean
+}
diff --git a/components/date-picker/utils.tsx b/components/date-picker/utils.tsx
deleted file mode 100644
index 2bfe17bc8..000000000
--- a/components/date-picker/utils.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-function formatIt(date: Date, form: string) {
- const pad = (n: number) => (n < 10 ? `0${n}` : n)
- const dateStr = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(
- date.getDate(),
- )}`
- const timeStr = `${pad(date.getHours())}:${pad(date.getMinutes())}`
- if (form === 'YYYY-MM-DD') {
- return dateStr
- }
- if (form === 'HH:mm') {
- return timeStr
- }
- return `${dateStr} ${timeStr}`
-}
-
-export function formatFn(instance: any, value: Date) {
- const formatsEnum = {
- date: 'YYYY-MM-DD',
- time: 'HH:mm',
- datetime: 'YYYY-MM-DD HH:mm',
- }
- const { format } = instance.props
- const type = typeof format
- if (type === 'string') {
- return formatIt(value, format)
- }
- if (type === 'function') {
- return format(value)
- }
- return formatIt(value, (formatsEnum as any)[instance.props.mode])
-}
-
-export function formatProps(props: any, value: Date) {
- const formatsEnum = {
- date: 'YYYY-MM-DD',
- time: 'HH:mm',
- datetime: 'YYYY-MM-DD HH:mm',
- }
- const { format } = props
- const type = typeof format
- if (type === 'string') {
- return formatIt(value, format)
- }
- if (type === 'function') {
- return format(value)
- }
- return formatIt(value, (formatsEnum as any)[props.mode])
-}
diff --git a/components/grid/__tests__/__snapshots__/demo.test.js.snap b/components/grid/__tests__/__snapshots__/demo.test.js.snap
index 7f80f6d0b..01ff63f62 100644
--- a/components/grid/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/grid/__tests__/__snapshots__/demo.test.js.snap
@@ -919,6 +919,7 @@ exports[`renders ./components/grid/demo/basic.tsx correctly 1`] = `
automaticallyAdjustContentInsets={false}
autoplay={false}
autoplayInterval={3000}
+ collapsable={false}
contentOffset={
Object {
"x": 0,
@@ -931,6 +932,8 @@ exports[`renders ./components/grid/demo/basic.tsx correctly 1`] = `
dots={true}
horizontal={true}
infinite={false}
+ onGestureHandlerEvent={[Function]}
+ onGestureHandlerStateChange={[Function]}
onMomentumScrollEnd={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
diff --git a/components/image-picker/CameraRollPicker.tsx b/components/image-picker/CameraRollPicker.tsx
deleted file mode 100644
index 07929ee83..000000000
--- a/components/image-picker/CameraRollPicker.tsx
+++ /dev/null
@@ -1,208 +0,0 @@
-import { CameraRoll } from '@react-native-camera-roll/camera-roll'
-import React, { Component } from 'react'
-import {
- GetPhotosParamType,
- Platform,
- StyleSheet,
- View,
- ViewStyle,
-} from 'react-native'
-import ListView from '../list-view'
-import ImageItem from './ImageItem'
-
-export interface CameraRollPickerStyle {
- wrapper: ViewStyle
- row: ViewStyle
- marker: ViewStyle
- spinner: ViewStyle
-}
-const styles = StyleSheet.create({
- wrapper: {
- flex: 1,
- },
- row: {
- flexDirection: 'row',
- flex: 1,
- },
- marker: {
- position: 'absolute',
- top: 5,
- backgroundColor: 'transparent',
- },
- spinner: {},
-})
-
-export interface CameraRollPickerProps extends GetPhotosParamType {
- maximum: number
- selectSingleItem?: boolean
- imagesPerRow: number
- imageMargin: number
- containerWidth?: number
- callback?: (...args: any[]) => any
- selected?: any[]
- selectedMarker?: React.ReactElement
- backgroundColor?: string
-}
-export type CameraRollPickerState = {
- selected: any
- images: any[]
-}
-class CameraRollPicker extends Component<
- CameraRollPickerProps,
- CameraRollPickerState
-> {
- static defaultProps = {
- groupTypes: 'SavedPhotos',
- maximum: 15,
- imagesPerRow: 6,
- imageMargin: 4,
- first: 50,
- selectSingleItem: false,
- assetType: 'Photos',
- backgroundColor: 'white',
- selected: [],
- callback: function (selectedImages: any, currentImage: any) {
- // tslint:disable-next-line:no-console
- console.log(currentImage)
- // tslint:disable-next-line:no-console
- console.log(selectedImages)
- },
- }
- after: string | undefined
- constructor(props: CameraRollPickerProps) {
- super(props)
- this.state = {
- images: [],
- selected: this.props.selected,
- }
- }
-
- UNSAFE_componentWillReceiveProps(nextProps: CameraRollPickerProps) {
- this.setState({
- selected: nextProps.selected,
- })
- }
-
- onFetch = async (_ = 1, startFetch: any, abortFetch: () => void) => {
- try {
- const { assetType, groupTypes, first, groupName, mimeTypes } = this.props
-
- const params: GetPhotosParamType = {
- first,
- after: this.after,
- assetType: assetType,
- groupName,
- mimeTypes,
- }
- if (Platform.OS !== 'android') {
- params.groupTypes = groupTypes
- }
- const res = await CameraRoll.getPhotos(params)
- if (res) {
- const data = res.edges
- if (res.page_info) {
- this.after = res.page_info.has_next_page
- ? res.page_info.end_cursor
- : ''
- }
- startFetch(data, first)
- }
- } catch (err) {
- if (__DEV__) {
- // tslint:disable-next-line:no-console
- console.error(err)
- }
- abortFetch() // manually stop the refresh or pagination if it encounters network error
- }
- }
- render() {
- const { imageMargin, backgroundColor, imagesPerRow } = this.props
-
- return (
-
- this._renderImage(item)}
- />
-
- )
- }
- _renderImage = (item: any) => {
- const { selected } = this.state
- const { imageMargin, selectedMarker, imagesPerRow, containerWidth } =
- this.props
- const uri = item.node.image.uri
- const isSelected =
- this._arrayObjectIndexOf(selected, 'uri', uri) >= 0 ? true : false
- return (
-
- )
- }
-
- _selectImage(image: { uri: any }) {
- const { maximum, callback, selectSingleItem } = this.props
- const selected = this.state.selected
- const index = this._arrayObjectIndexOf(selected, 'uri', image.uri)
- if (index >= 0) {
- selected.splice(index, 1)
- } else {
- if (selectSingleItem) {
- selected.splice(0, selected.length)
- }
- if (selected.length < maximum!) {
- selected.push(image)
- }
- }
- this.setState({
- selected: selected,
- })
- callback!(selected, image)
- }
- _nEveryRow(data: any, n: number) {
- const result = []
- let temp = []
- for (let i = 0; i < data.length; ++i) {
- if (i > 0 && i % n === 0) {
- result.push(temp)
- temp = []
- }
- temp.push(data[i])
- }
- if (temp.length > 0) {
- while (temp.length !== n) {
- temp.push(null)
- }
- result.push(temp)
- }
- return result
- }
- _arrayObjectIndexOf(array: any, property: string, value: any) {
- return array
- .map((o: any) => {
- return o[property]
- })
- .indexOf(value)
- }
-}
-
-export default CameraRollPicker
diff --git a/components/image-picker/ImageItem.tsx b/components/image-picker/ImageItem.tsx
deleted file mode 100644
index c60652c18..000000000
--- a/components/image-picker/ImageItem.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import React, { Component } from 'react'
-import {
- Dimensions,
- Image,
- ImageStyle,
- StyleSheet,
- TouchableOpacity,
-} from 'react-native'
-import Icon from '../icon'
-export type ImageItemProps = {
- item?: any
- selected?: boolean
- selectedMarker?: React.ReactElement
- imageMargin: number
- containerWidth?: number
- imagesPerRow: number
- onPress?: (...args: any[]) => any
-}
-class ImageItem extends Component {
- static defaultProps = {
- item: {},
- selected: false,
- }
- _imageSize: number
- constructor(props: ImageItemProps) {
- super(props)
- }
- UNSAFE_componentWillMount() {
- let { width } = Dimensions.get('window')
- const { imageMargin, imagesPerRow, containerWidth } = this.props
- if (typeof containerWidth !== 'undefined') {
- width = containerWidth
- }
- this._imageSize = (width - (imagesPerRow + 1) * imageMargin) / imagesPerRow
- }
- render() {
- const { item, selected, selectedMarker, imageMargin } = this.props
- if (!item) {
- return null
- }
- const marker = selectedMarker ? (
- selectedMarker
- ) : (
-
- )
- const image = item.node.image
- return (
- this._handleClick(image)}>
-
- {selected ? marker : null}
-
- )
- }
- _handleClick(item: any) {
- if (this.props.onPress) {
- this.props.onPress(item)
- }
- }
-}
-const styles = StyleSheet.create<{
- marker: ImageStyle
-}>({
- marker: {
- position: 'absolute',
- top: 5,
- right: 5,
- backgroundColor: 'transparent',
- },
-})
-
-export default ImageItem
diff --git a/components/image-picker/ImageRoll.tsx b/components/image-picker/ImageRoll.tsx
deleted file mode 100644
index d7bbd1b48..000000000
--- a/components/image-picker/ImageRoll.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import React from 'react'
-import {
- Modal,
- StatusBar,
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
-} from 'react-native'
-import varibles from '../style/themes/default'
-import CameraRollPicker, { CameraRollPickerProps } from './CameraRollPicker'
-
-export interface ImageRollProps extends ImageRollTexts {
- onCancel: () => void
- onSelected: (imgObj: {}) => void
- cameraPickerProps?: CameraRollPickerProps
-}
-
-export interface ImageRollTexts {
- title?: React.ReactNode
- cancelText?: React.ReactNode
-}
-
-const styles = StyleSheet.create({
- statusBarBg: {
- height: 5 * 4,
- },
- naviBar: {
- flexDirection: 'row',
- justifyContent: 'center',
- alignItems: 'center',
- borderBottomWidth: 1,
- borderBottomColor: '#d9d9d9',
- height: 11 * 4,
- },
- barTitle: {
- flex: 1,
- textAlign: 'center',
- fontWeight: '500',
- marginLeft: 7 * 4,
- fontSize: 16,
- },
- rightBtn: {
- width: 14 * 4,
- color: varibles.brand_primary,
- fontSize: 16,
- },
-})
-
-export default class ImageRoll extends React.Component {
- static defaultProps = {
- title: '图片',
- cancelText: '取消',
- cameraPickerProps: {},
- }
- onSelected = (images: any[], _: any) => {
- this.props.onSelected(images[0])
- this.props.onCancel()
- }
- render() {
- const { title, cancelText, cameraPickerProps } = this.props
-
- return (
- {}}
- transparent={false}>
-
-
-
-
- {title}
-
- {cancelText}
-
-
-
-
-
- )
- }
-}
diff --git a/components/image-picker/PropsType.tsx b/components/image-picker/PropsType.tsx
deleted file mode 100644
index db1801407..000000000
--- a/components/image-picker/PropsType.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-// import React from 'react';
-
-export interface ImagePickerPropTypes {
- styles?: {}
- files?: Array<{}>
- onChange?: (files: Array<{}>, operationType: string, index?: number) => void
- onImageClick?: (index?: number, files?: Array<{}>) => void
- onAddImageClick?: () => void
- onFail?: (msg: string) => void
- selectable?: boolean
-}
diff --git a/components/image-picker/__tests__/__snapshots__/demo.test.js.snap b/components/image-picker/__tests__/__snapshots__/demo.test.js.snap
deleted file mode 100644
index 913b3579e..000000000
--- a/components/image-picker/__tests__/__snapshots__/demo.test.js.snap
+++ /dev/null
@@ -1,697 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders ./components/image-picker/demo/basic.tsx correctly 1`] = `
-
-
-
-
-
-
-
-
- ×
-
-
-
-
-
-
-
-
-
- ×
-
-
-
-
-
-
-
-
-
- ×
-
-
-
-
-
-
-
-
-
- ×
-
-
-
-
-
-
-
-
-
- ×
-
-
-
-
-
-
-
-
-
- ×
-
-
-
-
-
- +
-
-
-
-
-
-
-
- +
-
-
-
-
-`;
diff --git a/components/image-picker/__tests__/demo.test.js b/components/image-picker/__tests__/demo.test.js
deleted file mode 100644
index 27ee9039b..000000000
--- a/components/image-picker/__tests__/demo.test.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import rnDemoTest from '../../../tests/shared/demoTest'
-
-rnDemoTest('image-picker')
diff --git a/components/image-picker/demo/basic.md b/components/image-picker/demo/basic.md
deleted file mode 100644
index d0ef454c3..000000000
--- a/components/image-picker/demo/basic.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-order: 0
-title:
- zh-CN: 基本
- en-US: Basic
----
-
-[Demo Source Code](https://github.com/ant-design/ant-design-mobile-rn/blob/master/components/image-picker/demo/basic.tsx)
-
-````jsx
-// todos: include('./basic.tsx')
-````
diff --git a/components/image-picker/demo/basic.tsx b/components/image-picker/demo/basic.tsx
deleted file mode 100644
index 8332c6adb..000000000
--- a/components/image-picker/demo/basic.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import React from 'react'
-import { PermissionsAndroid, Platform, Text, View } from 'react-native'
-import { ImagePicker, WhiteSpace } from '../../'
-
-export default class ImagePickerExample extends React.Component {
- constructor(props: any) {
- super(props)
- this.state = {
- files: [
- {
- url: 'https://zos.alipayobjects.com/rmsportal/WCxfiPKoDDHwLBM.png',
- id: '2121',
- },
- {
- url: 'https://zos.alipayobjects.com/rmsportal/WCxfiPKoDDHwLBM.png',
- id: '2122',
- },
- {
- url: 'https://zos.alipayobjects.com/rmsportal/WCxfiPKoDDHwLBM.png',
- id: '2123',
- },
- {
- url: 'https://zos.alipayobjects.com/rmsportal/WCxfiPKoDDHwLBM.png',
- id: '2124',
- },
- {
- url: 'https://zos.alipayobjects.com/rmsportal/WCxfiPKoDDHwLBM.png',
- id: '2125',
- },
- {
- url: 'https://zos.alipayobjects.com/rmsportal/WCxfiPKoDDHwLBM.png',
- id: '2126',
- },
- ],
- files2: [],
- }
- }
-
- handleFileChange = (files: any) => {
- this.setState({
- files,
- })
- }
-
- handleFile2Change = (files2: any) => {
- this.setState({
- files2,
- })
- }
- async requestCameraPermission() {
- try {
- const granted = await PermissionsAndroid.request(
- PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
- {
- title: '需要访问相册',
- message: '需要访问相册',
- } as any,
- )
- if (granted === PermissionsAndroid.RESULTS.GRANTED) {
- this.setState({
- granted: true,
- })
- } else {
- this.setState({
- granted: false,
- })
- }
- } catch (err) {
- console.warn(err)
- }
- }
- async componentDidMount() {
- if (Platform.OS === 'android') {
- await this.requestCameraPermission()
- }
- }
-
- render() {
- if (Platform.OS === 'android' && !this.state.granted) {
- return 需要访问相册的权限
- }
- return (
-
-
-
-
-
- )
- }
-}
diff --git a/components/image-picker/index.en-US.md b/components/image-picker/index.en-US.md
deleted file mode 100644
index 7451d6ab0..000000000
--- a/components/image-picker/index.en-US.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-category: Components
-type: Data Entry
-title: ImagePicker
----
-
-Note: Just for selecting picture. Generally `ImagePicker` is used to select picture before uploading, but without the feature of uploading.
-
-If you have permission issues Please checkout https://github.com/ant-design/ant-design-mobile-rn/issues/90
-
-## API
-
-| Properties | Descrition | Type | Default |
-| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------- |
-| styles | Styles object for the various elements of the ImagePicker | Object | See `/components/image-picker/style/index.tsx` for the defaults |
-| files | Picture files array which includes `url`(required) in each object | Array | [] |
-| onChange | Callback is called when the value of `files` is changed. The `operationType` is one of `add` or `remove`(the third argument is the removed index). | (files: Object, operationType: string, index: number): void | |
-| onImageClick | Callback is called when the user clicks the selected picture | (index: number, files: Array): void | |
-| onAddImageClick | Callback is called when the selector button is clicked | (): void | |
-| onFail | Callback is called when the image selection is cancelled | (msg: string): void | |
-| selectable | whether to show selector button | boolean | true |
-| title | ImageRoll'title | string | 'Photos' |
-| cancelText | Cancel text | string | 'Cancel' |
-| cameraPickerProps | CameraPickerProps | CameraPickerProps | - |
-
-> Note: Only return assets-library type for RN, if you want to upload files, see https://github.com/facebook/react-native/issues/201
diff --git a/components/image-picker/index.tsx b/components/image-picker/index.tsx
deleted file mode 100644
index f987f3de7..000000000
--- a/components/image-picker/index.tsx
+++ /dev/null
@@ -1,165 +0,0 @@
-import React from 'react'
-import {
- Image,
- Text,
- TouchableOpacity,
- TouchableWithoutFeedback,
- View,
-} from 'react-native'
-import { WithTheme, WithThemeStyles } from '../style'
-import { CameraRollPickerProps } from './CameraRollPicker'
-import ImageRoll, { ImageRollTexts } from './ImageRoll'
-import { ImagePickerPropTypes } from './PropsType'
-import ImagePickerStyles, { ImagePickerStyle } from './style/index'
-
-export interface ImagePickerProps
- extends ImagePickerPropTypes,
- WithThemeStyles,
- ImageRollTexts {
- cameraPickerProps?: CameraRollPickerProps
-}
-
-export default class ImagePicker extends React.Component<
- ImagePickerProps,
- any
-> {
- static defaultProps = {
- onChange() {},
- onFail() {},
- files: [],
- selectable: true,
- }
-
- plusText: any
- plusWrap: any
-
- constructor(props: ImagePickerProps) {
- super(props)
- this.state = {
- visible: false,
- }
- }
-
- onPressIn = (styles: ReturnType) => () => {
- this.plusWrap.setNativeProps({
- style: [styles.item, styles.size, styles.plusWrapHighlight],
- })
- }
-
- onPressOut = (styles: ReturnType) => () => {
- this.plusWrap.setNativeProps({
- style: [styles.item, styles.size, styles.plusWrapNormal],
- })
- }
-
- showPicker = () => {
- if (this.props.onAddImageClick) {
- this.props.onAddImageClick()
- return
- }
- this.setState({
- visible: true,
- })
- }
-
- addImage(imageObj: any) {
- if (!imageObj.url) {
- imageObj.url = imageObj.uri
- delete imageObj.uri
- }
- const { files = [] } = this.props
- const newImages = files.concat(imageObj)
- if (this.props.onChange) {
- this.props.onChange(newImages, 'add')
- }
- }
-
- removeImage(idx: number): void {
- const newImages: any[] = []
- const { files = [] } = this.props
- files.forEach((image, index) => {
- if (index !== idx) {
- newImages.push(image)
- }
- })
- if (this.props.onChange) {
- this.props.onChange(newImages, 'remove', idx)
- }
- }
-
- hideImageRoll = () => {
- this.setState({
- visible: false,
- })
- if (this.props.onFail) {
- this.props.onFail('cancel image selection')
- }
- }
-
- onImageClick(index: number) {
- if (this.props.onImageClick) {
- this.props.onImageClick(index, this.props.files)
- }
- }
-
- render() {
- const { files = [], selectable, cameraPickerProps } = this.props
- return (
-
- {(styles) => {
- const filesView = files.map((item: any, index) => (
-
- this.onImageClick(index)}
- activeOpacity={0.6}>
-
-
- this.removeImage(index)}
- style={styles.closeWrap}
- activeOpacity={0.6}>
- ×
-
-
- ))
-
- const imageRollEl = (
- this.addImage(imgObj)}
- title={this.props.title}
- cancelText={this.props.cancelText}
- cameraPickerProps={cameraPickerProps}
- />
- )
- return (
-
- {filesView}
- {selectable && (
-
- (this.plusWrap = conponent)}
- style={[
- styles.item,
- styles.size,
- styles.plusWrap,
- styles.plusWrapNormal,
- ]}>
- +
-
-
- )}
- {this.state.visible ? imageRollEl : null}
-
- )
- }}
-
- )
- }
-}
diff --git a/components/image-picker/index.zh-CN.md b/components/image-picker/index.zh-CN.md
deleted file mode 100644
index 920519716..000000000
--- a/components/image-picker/index.zh-CN.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-category: Components
-type: Data Entry
-title: ImagePicker
-subtitle: 图片选择器
----
-
-注意:只是图片选择器,一般用于上传图片前的文件选择操作,但不包括图片上传的功能
-
-
-权限相关的问题可以移步至:https://github.com/ant-design/ant-design-mobile-rn/issues/90 查看
-
-## API
-
-| 属性 | 说明 | 类型 | 默认值 |
-| ----------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | -------- |
-| files | 图片文件数组,元素为对象,包含属性 url(必选, 可能还有id, orientation, 以及业务需要的其它属性 | Array | [] |
-| onChange | files 值发生变化触发的回调函数, operationType 操作类型有添加,移除,如果是移除操作,则第三个参数代表的是移除图片的索引 | (files: Object, operationType: string, index: number): void | |
-| onImageClick | 点击图片触发的回调 | (index: number, files: Object): void | |
-| onAddImageClick | 自定义选择图片的方法 | (): void | |
-| onFail | 取消回调 | (msg: string): void | |
-| selectable | 是否显示添加按钮 | boolean | true |
-| title | ImageRoll 标题 | string | 'Photos' |
-| cancelText | 取消按钮 | string | 'Cancel' |
-| cameraPickerProps | CameraPickerProps | CameraPickerProps | - |
-
-
-> 注: RN 版本回传 assets-library (性能考虑),需要使用 native 模块进行上传,可参考 https://github.com/facebook/react-native/issues/201
diff --git a/components/image-picker/style/index.tsx b/components/image-picker/style/index.tsx
deleted file mode 100644
index 4cbbc3924..000000000
--- a/components/image-picker/style/index.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from 'react-native'
-import varibles from '../../style/themes/default'
-
-export interface ImagePickerStyle {
- container: ViewStyle
- size: ImageStyle
- item: ViewStyle
- image: ImageStyle
- closeWrap: ViewStyle
- closeText: TextStyle
- plusWrap: ViewStyle
- plusWrapNormal: ViewStyle
- plusWrapHighlight: ViewStyle
- plusText: TextStyle
-}
-
-export default () =>
- StyleSheet.create({
- container: {
- flexWrap: 'wrap',
- flexDirection: 'row',
- },
- size: {
- width: 80,
- height: 80,
- },
- item: {
- marginRight: varibles.h_spacing_sm,
- marginBottom: varibles.v_spacing_sm,
- overflow: 'hidden',
- },
- image: {
- overflow: 'hidden',
- borderRadius: varibles.radius_sm,
- },
- closeWrap: {
- width: 16,
- height: 16,
- backgroundColor: '#999',
- borderRadius: 8,
- position: 'absolute',
- top: 4,
- right: 4,
- justifyContent: 'center',
- alignItems: 'center',
- overflow: 'hidden',
- },
- closeText: {
- color: varibles.color_text_base_inverse,
- backgroundColor: 'transparent',
- fontSize: 20,
- height: 20,
- marginTop: -8,
- fontWeight: '300',
- },
- plusWrap: {
- borderRadius: varibles.radius_sm,
- borderWidth: 1,
- justifyContent: 'center',
- alignItems: 'center',
- },
- plusWrapNormal: {
- backgroundColor: varibles.fill_base,
- borderColor: varibles.border_color_base,
- },
- plusWrapHighlight: {
- backgroundColor: varibles.fill_tap,
- borderColor: varibles.border_color_base,
- },
- plusText: {
- fontSize: 64,
- backgroundColor: 'transparent',
- fontWeight: '100',
- color: varibles.color_text_caption,
- },
- })
diff --git a/components/index.tsx b/components/index.tsx
index 9173c2962..9a4954ffc 100644
--- a/components/index.tsx
+++ b/components/index.tsx
@@ -12,7 +12,6 @@ export { default as Drawer } from './drawer/index'
export { default as Flex } from './flex/index'
export { default as Grid } from './grid/index'
export { default as Icon } from './icon/index'
-export { default as ImagePicker } from './image-picker/index'
export { default as InputItem } from './input-item/index'
export { default as ListView } from './list-view/index'
export { default as List } from './list/index'
diff --git a/components/input-item/locale/ko_KR.tsx b/components/input-item/locale/ko_KR.tsx
new file mode 100644
index 000000000..7382b0e18
--- /dev/null
+++ b/components/input-item/locale/ko_KR.tsx
@@ -0,0 +1,5 @@
+export default {
+ confirmLabel: '확인',
+ backspaceLabel: '지우기',
+ cancelKeyboardLabel: '키보드 숨기기',
+}
diff --git a/components/list-view/locale/ko_KR.tsx b/components/list-view/locale/ko_KR.tsx
new file mode 100644
index 000000000..53b1fe21a
--- /dev/null
+++ b/components/list-view/locale/ko_KR.tsx
@@ -0,0 +1,8 @@
+export default {
+ done: '완료',
+ loading: '로딩중',
+ refreshableTitlePull: '당겨서 새로고침',
+ refreshableTitleRelease: '놓아서 새로고침',
+ refreshableTitleRefreshing: '로딩중...',
+ noData: '데이터 없음',
+}
diff --git a/components/locale-provider/__tests__/__snapshots__/demo.test.js.snap b/components/locale-provider/__tests__/__snapshots__/demo.test.js.snap
index 3651321d0..ed4c93550 100644
--- a/components/locale-provider/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/locale-provider/__tests__/__snapshots__/demo.test.js.snap
@@ -12,29 +12,46 @@ exports[`renders ./components/locale-provider/demo/basic.tsx correctly 1`] = `
]
}
>
-
+
@@ -42,91 +59,72 @@ exports[`renders ./components/locale-provider/demo/basic.tsx correctly 1`] = `
style={
Array [
Object {
- "alignItems": "center",
- "borderBottomColor": "#dddddd",
- "borderBottomWidth": 0.5,
"flex": 1,
- "flexDirection": "row",
- "minHeight": 44,
- "paddingRight": 15,
- "paddingVertical": 6,
+ "flexDirection": "column",
},
- false,
- false,
]
}
>
-
-
- Choose language
-
-
-
-
- English
-
-
+ Choose language
+
+
+
-
+ 请选择
+
+
+
@@ -397,29 +395,46 @@ exports[`renders ./components/locale-provider/demo/basic.tsx correctly 1`] = `
}
}
>
-
+
@@ -427,117 +442,115 @@ exports[`renders ./components/locale-provider/demo/basic.tsx correctly 1`] = `
style={
Array [
Object {
- "alignItems": "center",
- "borderBottomColor": "#dddddd",
- "borderBottomWidth": 0.5,
"flex": 1,
- "flexDirection": "row",
- "minHeight": 44,
- "paddingRight": 15,
- "paddingVertical": 6,
+ "flexDirection": "column",
},
- false,
- false,
]
}
>
-
-
- DatePicker
-
-
-
-
- please select
-
-
+ DatePicker
+
+
+
-
+ please select
+
+
+
-
+
@@ -545,91 +558,72 @@ exports[`renders ./components/locale-provider/demo/basic.tsx correctly 1`] = `
style={
Array [
Object {
- "alignItems": "center",
- "borderBottomColor": "#dddddd",
- "borderBottomWidth": 0.5,
"flex": 1,
- "flexDirection": "row",
- "minHeight": 44,
- "paddingRight": 15,
- "paddingVertical": 6,
+ "flexDirection": "column",
},
- false,
- false,
]
}
>
-
-
- Picker
-
-
-
-
- please select
-
-
+ Picker
+
+
+
-
+ please select
+
+
+
diff --git a/components/locale-provider/demo/basic.tsx b/components/locale-provider/demo/basic.tsx
index 76f8db5a1..4d8bc360f 100644
--- a/components/locale-provider/demo/basic.tsx
+++ b/components/locale-provider/demo/basic.tsx
@@ -12,8 +12,11 @@ import {
} from '../../'
import enUS from '../en_US'
import esES from '../es_ES'
+import faIR from '../fa_IR'
+import koKR from '../ko_KR'
import ptBR from '../pt_BR'
import ruRU from '../ru_RU'
+import svSE from '../sv_SE'
import zhCN from '../zh_CN'
const maxDate = new Date(2018, 11, 3, 22, 0)
@@ -105,6 +108,21 @@ export default class LocaleProviderExample extends React.Component {
label: 'Português - BR',
language: ptBR,
},
+ {
+ value: 'Sverige',
+ label: 'Sverige',
+ language: svSE,
+ },
+ {
+ value: 'Persian',
+ label: 'Persian',
+ language: faIR,
+ },
+ {
+ value: '한국',
+ label: '한국',
+ language: koKR,
+ },
]
const currentLocale = languages.find(
(item) => item.value === locale,
diff --git a/components/locale-provider/ko_KR.tsx b/components/locale-provider/ko_KR.tsx
new file mode 100644
index 000000000..89ecc7e75
--- /dev/null
+++ b/components/locale-provider/ko_KR.tsx
@@ -0,0 +1,20 @@
+import DatePickerView from '../date-picker-view/locale/ko_KR'
+import DatePicker from '../date-picker/locale/ko_KR'
+import InputItem from '../input-item/locale/ko_KR'
+import ListView from '../list-view/locale/ko_KR'
+import Modal from '../modal/locale/ko_KR'
+import Pagination from '../pagination/locale/ko_KR'
+import Picker from '../picker/locale/ko_KR'
+import SearchBar from '../search-bar/locale/ko_KR'
+
+export default {
+ locale: 'ko_KR',
+ DatePicker,
+ DatePickerView,
+ InputItem,
+ Modal,
+ Pagination,
+ Picker,
+ SearchBar,
+ ListView,
+}
diff --git a/components/locale-provider/sv_SE.tsx b/components/locale-provider/sv_SE.tsx
index c92ce4ae9..b5fcc856d 100644
--- a/components/locale-provider/sv_SE.tsx
+++ b/components/locale-provider/sv_SE.tsx
@@ -1,11 +1,11 @@
-import DatePickerView from '../date-picker-view/locale/sv_Se'
-import DatePicker from '../date-picker/locale/sv_Se'
-import InputItem from '../input-item/locale/sv_Se'
-import ListView from '../list-view/locale/sv_Se'
-import Modal from '../modal/locale/sv_Se'
-import Pagination from '../pagination/locale/sv_Se'
-import Picker from '../picker/locale/sv_Se'
-import SearchBar from '../search-bar/locale/sv_Se'
+import DatePickerView from '../date-picker-view/locale/sv_SE'
+import DatePicker from '../date-picker/locale/sv_SE'
+import InputItem from '../input-item/locale/sv_SE'
+import ListView from '../list-view/locale/sv_SE'
+import Modal from '../modal/locale/sv_SE'
+import Pagination from '../pagination/locale/sv_SE'
+import Picker from '../picker/locale/sv_SE'
+import SearchBar from '../search-bar/locale/sv_SE'
export default {
locale: 'sv',
diff --git a/components/modal/locale/ko_KR.tsx b/components/modal/locale/ko_KR.tsx
new file mode 100644
index 000000000..ab43dfe84
--- /dev/null
+++ b/components/modal/locale/ko_KR.tsx
@@ -0,0 +1,5 @@
+export default {
+ okText: '확인',
+ cancelText: '취소',
+ buttonText: '버튼',
+}
diff --git a/components/pagination/locale/ko_KR.tsx b/components/pagination/locale/ko_KR.tsx
new file mode 100644
index 000000000..4a999d31e
--- /dev/null
+++ b/components/pagination/locale/ko_KR.tsx
@@ -0,0 +1,4 @@
+export default {
+ prevText: '이전',
+ nextText: '다음',
+}
diff --git a/components/picker-view/PickerView.tsx b/components/picker-view/PickerView.tsx
deleted file mode 100644
index 173195b83..000000000
--- a/components/picker-view/PickerView.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import React from 'react'
-import { StyleProp, TextStyle, ViewStyle } from 'react-native'
-import RMCCascader from '../picker/cascader'
-import MultiPicker from '../picker/MultiPicker'
-import RMCPicker from '../picker/Picker'
-import { PickerData } from '../picker/PropsType'
-
-function getDefaultProps() {
- return {
- cols: 3,
- cascade: true,
- value: [],
- onChange() {},
- }
-}
-
-export interface PickerViewProps {
- cols?: number
- cascade?: boolean
- value?: any[]
- data?: PickerData[] | PickerData[][]
- styles?: any
- onChange?: (value?: any) => void
- onScrollChange?: (value?: any) => void
- indicatorStyle?: StyleProp
- itemStyle?: StyleProp
-}
-
-export default class PickerView extends React.Component {
- static defaultProps = getDefaultProps()
-
- getCol = () => {
- const { data, indicatorStyle, itemStyle } = this.props
- return (data as PickerData[][]).map((col, index) => {
- return (
-
- {col.map((item) => {
- return (
-
- {item.label}
-
- )
- })}
-
- )
- })
- }
- render() {
- // tslint:disable-next-line:no-this-assignment
- const { props } = this
- let picker
- if (props.cascade) {
- picker = (
-
- )
- } else {
- picker = (
-
- {this.getCol()}
-
- )
- }
- return picker
- }
-}
diff --git a/components/picker-view/PropsType.tsx b/components/picker-view/PropsType.tsx
new file mode 100644
index 000000000..e6dd6e73a
--- /dev/null
+++ b/components/picker-view/PropsType.tsx
@@ -0,0 +1,38 @@
+import type { ReactNode } from 'react'
+import { StyleProp, TextStyle, ViewStyle } from 'react-native'
+
+export type PickerValue = string | number
+
+export type PickerValueExtend = {
+ columns: PickerColumn[]
+ items: (PickerColumnItem | undefined)[]
+}
+
+export type PickerColumnItem = {
+ label: string | ReactNode
+ value: string | number
+ key?: string | number
+ children?: PickerColumnItem[]
+}
+
+export type PickerColumn = PickerColumnItem[]
+// type PickerColumn = (string | PickerColumnItem)[] // TODO: support string type
+
+export interface PickerViewPropsType {
+ value?: PickerValue[]
+ defaultValue?: PickerValue[]
+ data: PickerColumn | PickerColumn[]
+ cols?: number
+ cascade?: boolean
+ loading?: boolean
+ loadingContent?: ReactNode
+ numberOfLines?: number
+ style?: StyleProp
+ itemStyle?: StyleProp
+ itemHeight?: number
+ indicatorStyle?: StyleProp
+ onChange?: (value: PickerValue[], extend: PickerValueExtend) => void
+ renderLabel?: (item: PickerColumnItem, index: number) => ReactNode
+ renderMaskTop?: () => ReactNode
+ renderMaskBottom?: () => ReactNode
+}
diff --git a/components/picker-view/Wheel.tsx b/components/picker-view/Wheel.tsx
new file mode 100644
index 000000000..17a6e0af3
--- /dev/null
+++ b/components/picker-view/Wheel.tsx
@@ -0,0 +1,200 @@
+// 需要重构成translate3d
+import type { ReactNode } from 'react'
+import React from 'react'
+import {
+ NativeScrollEvent,
+ NativeSyntheticEvent,
+ Platform,
+ View,
+} from 'react-native'
+import { ScrollView } from 'react-native-gesture-handler'
+
+type ColumnItem = {
+ label: string | ReactNode
+ value: string | number
+}
+type Value = string | number
+
+type Props = {
+ index: number
+ column: ColumnItem[]
+ value?: Value
+ onSelect: (value: Value, index: number) => void
+ renderLabel: (item: ColumnItem, index: number) => ReactNode
+ itemHeight: number
+ wheelHeight: number
+}
+
+class Wheel extends React.Component {
+ scrollerRef: any
+
+ scrollBuffer: number
+
+ selectValue: any
+ isScrolling: boolean
+
+ componentDidMount() {
+ const { value, column, itemHeight } = this.props
+ setTimeout(() => {
+ this.scrollTo(this.getSelectIndex(column, value) * itemHeight)
+ })
+ }
+
+ shouldComponentUpdate(nextProps: Props) {
+ const { value, column, itemHeight, wheelHeight } = nextProps
+ if (
+ value !== this.props.value ||
+ itemHeight !== this.props.itemHeight ||
+ wheelHeight !== this.props.wheelHeight
+ ) {
+ this.scrollTo(this.getSelectIndex(column, value) * itemHeight)
+ return true
+ }
+ return column !== this.props.column
+ }
+
+ componentDidUpdate(prevProps: Props) {
+ const { column } = prevProps
+ if (column !== this.props.column) {
+ this.scrollTo(
+ this.getSelectIndex(this.props.column, this.props.value) *
+ this.props.itemHeight,
+ )
+ }
+ }
+
+ scrollTo = (y: any) => {
+ this.scrollerRef?.scrollTo?.({
+ y,
+ animated: false,
+ })
+ }
+
+ getSelectIndex(column: ColumnItem[], value?: Value) {
+ return Math.max(
+ column.findIndex((item) => item.value === value),
+ 0,
+ )
+ }
+
+ handleSelect = () => {
+ if (this.props.value !== this.selectValue && this.props.onSelect) {
+ this.props.onSelect(this.selectValue, this.props.index)
+ }
+
+ setTimeout(() => {
+ this.scrollTo(
+ this.getSelectIndex(this.props.column, this.props.value) *
+ this.props.itemHeight,
+ )
+ })
+ }
+
+ onScrollEndForWeb = (e: NativeSyntheticEvent) => {
+ e.persist?.()
+ let contentOffset = e.nativeEvent.contentOffset
+ // android/web hack
+ if (!contentOffset) {
+ //@ts-ignore
+ const { position } = e.nativeEvent
+ contentOffset = {
+ x: 0,
+ y: position * this.props.itemHeight,
+ }
+ }
+
+ const selectIndex = Math.round(contentOffset.y / this.props.itemHeight)
+ if (this.props.column[selectIndex]) {
+ this.selectValue = this.props.column[selectIndex].value
+ if (!this.isScrolling) {
+ this.updateSelectThrottle()
+ }
+ }
+ }
+
+ /*** for web event ***/
+ onTouchStartForWeb = () => {
+ this.isScrolling = true
+ }
+ onTouchEndForWeb = () => {
+ this.isScrolling = false
+ this.updateSelectThrottle()
+ }
+ onScrollForWeb = (e: NativeSyntheticEvent) => {
+ this.onScrollEndForWeb(e)
+ }
+ updateSelectThrottle = () => {
+ clearTimeout(this.scrollBuffer)
+ this.scrollBuffer = setTimeout(() => {
+ this.handleSelect()
+ }, 100) //idle time
+ }
+
+ onMomentumScrollBegin = () => {
+ this.isScrolling = true
+ }
+ onMomentumScrollEnd = (e: NativeSyntheticEvent) => {
+ e.persist?.()
+ if (this.isScrolling) {
+ let contentOffset = e.nativeEvent.contentOffset
+ // android/web hack
+ if (!contentOffset) {
+ //@ts-ignore
+ const { position } = e.nativeEvent
+ contentOffset = {
+ x: 0,
+ y: position * this.props.itemHeight,
+ }
+ }
+ const selectIndex = Math.round(contentOffset.y / this.props.itemHeight)
+ if (this.props.column[selectIndex]) {
+ this.selectValue = this.props.column[selectIndex].value
+ this.handleSelect()
+ }
+ }
+ this.isScrolling = false
+ }
+
+ renderItems = () => {
+ const { column, renderLabel, itemHeight, wheelHeight } = this.props
+ return (
+
+ {column.map((item, index) => renderLabel(item, index))}
+
+ )
+ }
+
+ render() {
+ return (
+ (this.scrollerRef = el)}
+ showsHorizontalScrollIndicator={false}
+ showsVerticalScrollIndicator={false}
+ pagingEnabled={false}
+ automaticallyAdjustContentInsets={false}
+ directionalLockEnabled={true}
+ decelerationRate="fast"
+ snapToInterval={this.props.itemHeight}
+ {...(Platform.OS === 'web'
+ ? {
+ onTouchStart: this.onTouchStartForWeb,
+ onTouchEnd: this.onTouchEndForWeb,
+ onScroll: this.onScrollForWeb,
+ scrollEventThrottle: 16,
+ }
+ : {
+ onMomentumScrollBegin: this.onMomentumScrollBegin,
+ onMomentumScrollEnd: this.onMomentumScrollEnd,
+ })}>
+ {this.renderItems()}
+
+ )
+ }
+}
+
+export default Wheel
diff --git a/components/picker-view/columns-extend.tsx b/components/picker-view/columns-extend.tsx
new file mode 100644
index 000000000..cb8f1f5a2
--- /dev/null
+++ b/components/picker-view/columns-extend.tsx
@@ -0,0 +1,86 @@
+import { PickerColumn, PickerValue } from './PropsType'
+
+export function getColumns(
+ d: PickerColumn | PickerColumn[],
+ val: PickerValue[],
+ cols: number,
+ cascade: boolean,
+): PickerColumn[] {
+ if (!d || d.length === 0) {
+ return []
+ }
+
+ if (!cascade) {
+ // when d is PickerColumn
+ if (!Array.isArray(d[0])) {
+ return [d as PickerColumn]
+ }
+ // when d is PickerColumn[]
+ return (d as PickerColumn[]).slice(0, cols!)
+ }
+
+ // cascade data
+ const columns = []
+ let currentOptions = (d as PickerColumn).slice()
+ let selected = val || []
+ let i = 0
+ while (i < cols!) {
+ columns.push(
+ currentOptions.map((option: any) => ({
+ label: option.label,
+ value: option.value,
+ })),
+ )
+ const x = selected[i]
+ const targetOptions =
+ currentOptions.find((option: any) => option.value === x) ||
+ currentOptions[0]
+ if (!targetOptions.children) {
+ break
+ }
+ currentOptions = targetOptions.children
+ i++
+ }
+ return columns
+}
+
+export function getValueExtend(
+ d: PickerColumn | PickerColumn[],
+ val: PickerValue[],
+ cols: number,
+ cascade: boolean,
+) {
+ if (!d || d.length === 0) {
+ return { nextValue: [], extend: [] }
+ }
+
+ if (!cascade) {
+ const columns = getColumns(d, val, cols, false).map(
+ (column: PickerColumn, index: number) =>
+ column.find((item) => item.value === val[index]) ?? column[0],
+ )
+
+ return { nextValue: columns.map((item) => item.value), extend: columns }
+ }
+
+ // cascade data
+ let currentOptions = (d as PickerColumn).slice()
+ const nextValue = []
+ const extend = []
+ let selected = val || []
+ let i = 0
+ while (i < cols!) {
+ const x = selected[i]
+ const targetOptions =
+ currentOptions.find((option: any) => option.value === x) ||
+ currentOptions[0]
+ nextValue[i] = targetOptions.value
+ extend[i] = targetOptions
+ if (!targetOptions.children) {
+ break
+ }
+ currentOptions = targetOptions.children
+ i++
+ }
+ return { nextValue, extend }
+}
diff --git a/components/picker-view/demo/basic.tsx b/components/picker-view/demo/basic.tsx
index 42ab146ba..6f2515745 100644
--- a/components/picker-view/demo/basic.tsx
+++ b/components/picker-view/demo/basic.tsx
@@ -1,26 +1,18 @@
import React from 'react'
+import { ScrollView, Text } from 'react-native'
import { PickerView } from '../../'
-const seasons = [
+const basicColumns = [
[
- {
- label: '2013',
- value: '2013',
- },
- {
- label: '2014',
- value: '2014',
- },
+ { label: '周一', value: 'Mon' },
+ { label: '周二', value: 'Tues' },
+ { label: '周三', value: 'Wed' },
+ { label: '周四', value: 'Thur' },
+ { label: '周五', value: 'Fri' },
],
[
- {
- label: '春',
- value: '春',
- },
- {
- label: '夏',
- value: '夏',
- },
+ { label: '上午', value: 'am' },
+ { label: '下午', value: 'pm' },
],
]
@@ -35,12 +27,31 @@ export default class PickerViewExample extends React.Component {
}
render() {
return (
-
+
+ 基础用法
+
+
+ 自定义高度
+
+
+ 受控模式
+
+
+
+
)
}
}
diff --git a/components/picker-view/index.en-US.md b/components/picker-view/index.en-US.md
index 6a79818b5..99f805932 100644
--- a/components/picker-view/index.en-US.md
+++ b/components/picker-view/index.en-US.md
@@ -8,13 +8,38 @@ PickerView's functions like Picker, but it is rendered directly in the area inst
## API
+### Props
+
+Properties | Descrition | Type | Default
+-----------|------------|------|--------
+| data | data source | `PickerColumn` / `PickerColumn[]` | - |
+| value | Selected options | `PickerValue[]` | - |
+| defaultValue | Default selected options | `PickerValue[]` | - |
+| cascade | whether cascade
child cascade get from `data[].children` | Boolean | `true` |
+| cols | col numbers | Number | `3` |
+| onChange | selected callback function, can use [rc-form](https://github.com/react-component/form) | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
+| renderLabel | The function to custom rendering the label shown on a column | `(item: PickerColumnItem, index: number) => ReactNode` | `(item) => item.label` |
+| loading | Should the Picker displays as loading state | Boolean | - |
+| loadingContent | The loading content displayed in loading state | ReactNode | `` |
+| indicatorStyle | style of default Indicator | Object | - |
+
+For the type definition of `PickerColumnItem` `PickerColumn` `PickerValue` `PickerValueExtend`, please refer to the document of [Picker](/components/picker/).
+
+### Custom Style
+
+Properties | Descrition | Type | Default
+-----------|------------|------|--------
+| style | style | `StyleProp` | - |
+| styles | inner component styles | interface `PickerViewStyle` | - |
+| itemStyle| style to apply to each of the item labels | `StyleProp` | - |
+| itemHeight | Height of option item, calculated by `numberOfLines` when without value; `itemStyle` was not allowed to set `{height}` | Number | - |
+| 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` |
+
+#### Mask View
+
+Support custom mask style, such as using the gradient component ``. Default is white translucency.
+
Properties | Descrition | Type | Default
-----------|------------|------|--------
-| data | data source | `Array<{value, label}>` / `Array>` | - |
-| value | the value, the format is `[value1, value2, value3]`, corresponds to the level value of the data source | Array | - |
-| cascade | whether cascade | Boolean | true |
-| cols | col numbers | Number | `3` |
-| onChange | selected callback function, can use [rc-form](https://github.com/react-component/form) | (val): void | - |
-| cascade | whether is cascade mode | Boolean | true |
-| itemStyle | style to apply to each of the item labels | Object | - |
-| indicatorStyle | style of indicator | Object | - |
+| renderMaskTop | The function to custom rendering the mask top half view | `()=> ReactNode` | `` |
+| renderMaskBottom | The function to custom rendering the mask bottom half view | `()=> ReactNode` | `` |
\ No newline at end of file
diff --git a/components/picker-view/index.tsx b/components/picker-view/index.tsx
index c259ec5be..fbd10f687 100644
--- a/components/picker-view/index.tsx
+++ b/components/picker-view/index.tsx
@@ -1,3 +1,69 @@
-import PickerView from './PickerView'
+import useMergedState from 'rc-util/lib/hooks/useMergedState'
+import React, {
+ forwardRef,
+ useCallback,
+ useImperativeHandle,
+ useMemo,
+} from 'react'
+import { mergeProps } from '../_util/with-default-props'
+import { WithThemeStyles } from '../style'
+import { PickerValue, PickerViewPropsType } from './PropsType'
+import { getColumns, getValueExtend } from './columns-extend'
+import RMCPickerView from './picker-view'
+import { PickerViewStyle } from './style'
+
+export interface PickerViewProps
+ extends PickerViewPropsType,
+ WithThemeStyles {}
+
+const defaultProps = {
+ cols: 3,
+ cascade: true,
+}
+
+const PickerView = forwardRef((props, ref) => {
+ const p = mergeProps(defaultProps, props)
+
+ const [innerValue, setInnerValue] = useMergedState([], {
+ value: p.value,
+ defaultValue: p.defaultValue,
+ })
+
+ useImperativeHandle(ref, () => ({
+ value: innerValue,
+ }))
+
+ const columns = useMemo(
+ () => getColumns(p.data, innerValue, p.cols, p.cascade),
+ [p.data, innerValue, p.cols, p.cascade],
+ )
+
+ const handleSelect = useCallback(
+ (val: PickerValue, index: number) => {
+ const value = innerValue?.slice?.(0) || []
+ value[index] = val
+ const { nextValue, extend } = getValueExtend(
+ p.data,
+ value,
+ p.cols,
+ p.cascade,
+ )
+ setInnerValue(nextValue)
+ p.onChange?.(nextValue, { items: extend, columns })
+ },
+ [p, columns, innerValue, setInnerValue],
+ )
+
+ return (
+
+ )
+})
+
+PickerView.displayName = 'AntmPickerView'
export default PickerView
diff --git a/components/picker-view/index.zh-CN.md b/components/picker-view/index.zh-CN.md
index 622b0d95c..ff5cfe1bc 100644
--- a/components/picker-view/index.zh-CN.md
+++ b/components/picker-view/index.zh-CN.md
@@ -9,12 +9,38 @@ PickerView 的功能类似于 Picker ,但它是直接渲染在区域中,而
## API
+### 属性
+
属性 | 说明 | 类型 | 默认值
----|-----|------|------
-| data | 数据源 | `Array<{value, label}>` / `Array>` | - |
-| value | 值, 格式是`[value1, value2, value3]`, 对应数据源的相应级层 value | Array | - |
-| cascade | 是否级联 | Boolean| true|
+| data | 数据源 | `PickerColumn` / `PickerColumn[]` | - |
+| value | 选中项 | `PickerValue[]` | - |
+| defaultValue | 默认选中项 | `PickerValue[]` | - |
+| cascade | 是否级联。
子级来自`data`参数内的`children`属性 | Boolean | `true` |
| cols | 列数 | Number | `3` |
-| onChange | 选中后的回调,可使用[rc-form](https://github.com/react-component/form) | (val): void | - |
-| itemStyle| 每列样式 | Object | - |
-| indicatorStyle | indicator 样式 | Object | - |
+| onChange | 选中后的回调,可使用[rc-form](https://github.com/react-component/form) | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
+| renderLabel | 自定义渲染每列展示的内容 | `(item: PickerColumnItem, index: number) => ReactNode` | `(item) => item.label` |
+| loading | 是否处于加载状态 | Boolean | - |
+| loadingContent | 加载状态下展示的内容 | ReactNode | `` |
+| indicatorStyle | 默认Indicator的样式 | Object | - |
+
+关于 `PickerColumnItem` `PickerColumn` `PickerValue` `PickerValueExtend` 的类型定义,请参考 [Picker](/components/picker-cn/) 的文档。
+
+### 自定义样式
+
+属性 | 说明 | 类型 | 默认值
+----|-----|------|------
+| style | 外部样式 | `StyleProp` | - |
+| styles | 内部组件样式集 | `PickerViewStyle` | - |
+| itemStyle| 每列样式 | `StyleProp` | - |
+| itemHeight | 每列固定高度,未设值时会根据`numberOfLines`动态计算;`itemStyle`属性设置`{height}`值是无效的 | Number | - |
+| numberOfLines | 允许每列显示行数 | Number | `1` |
+
+#### 遮罩层
+
+还支持自定义遮罩样式,如使用渐变组件``。当前默认为白色半透明。
+
+属性 | 说明 | 类型 | 默认值
+----|-----|------|------
+| renderMaskTop | 自定义渲染上半部分遮罩层 | `()=> ReactNode` | `` |
+| renderMaskBottom | 自定义渲染下半部分遮罩层 | `()=> ReactNode` | `` |
\ No newline at end of file
diff --git a/components/picker-view/picker-view.tsx b/components/picker-view/picker-view.tsx
new file mode 100644
index 000000000..6996fe750
--- /dev/null
+++ b/components/picker-view/picker-view.tsx
@@ -0,0 +1,167 @@
+import React from 'react'
+import { ActivityIndicator, LayoutChangeEvent, Text, View } from 'react-native'
+import { WithTheme, WithThemeStyles } from '../style'
+import {
+ PickerColumn,
+ PickerColumnItem,
+ PickerValue,
+ PickerViewPropsType,
+} from './PropsType'
+import Wheel from './Wheel'
+import pickerViewStyles, { PickerViewStyle } from './style/index'
+
+export type RMCPickerViewProps = Omit<
+ PickerViewPropsType,
+ 'data' | 'cols' | 'cascade' | 'onChange'
+> &
+ WithThemeStyles & {
+ columns: PickerColumn[]
+ handleSelect: (value: PickerValue, index: number) => void
+ }
+export default class RMCPickerView extends React.Component<
+ RMCPickerViewProps,
+ any
+> {
+ static defaultProps = {
+ value: [],
+ itemHeight: 0,
+ numberOfLines: 1,
+ renderMaskTop: () => (
+
+ ),
+ renderMaskBottom: () => (
+
+ ),
+ }
+
+ constructor(props: RMCPickerViewProps) {
+ super(props)
+ this.state = {
+ itemHeight: 0,
+ wheelHeight: 0,
+ }
+ }
+
+ wrapperMeasure = (e: LayoutChangeEvent) => {
+ const { height } = e.nativeEvent.layout
+ this.setState({ wheelHeight: height })
+ }
+
+ itemHeightMeasure = (e: LayoutChangeEvent) => {
+ const { height } = e.nativeEvent.layout
+ this.setState({ itemHeight: height })
+ }
+
+ renderLabel = (item: PickerColumnItem, index: number) => {
+ return (
+
+ {this.props.renderLabel?.(item, index) || (
+
+ {item.label}
+
+ )}
+
+ )
+ }
+
+ renderMask = (s: PickerViewStyle) => (
+
+ {this.props.renderMaskTop?.()}
+
+ {this.props.renderMaskBottom?.()}
+
+ )
+
+ render() {
+ const { wheelHeight } = this.state
+ const {
+ style,
+ styles,
+ columns,
+ value,
+ loading,
+ indicatorStyle,
+ numberOfLines,
+ handleSelect,
+ loadingContent,
+ } = this.props
+ const itemHeight = this.props.itemHeight || this.state.itemHeight
+ return (
+
+ {(s) =>
+ // {/* 计算中文占位符换行的高度后,items统一这个高度 */}
+ itemHeight === 0 ? (
+
+ {this.renderLabel(
+ {
+ value: 'layout',
+ label: <>{Array(numberOfLines).join('\n')} >,
+ },
+ 0,
+ )}
+
+ ) : (
+
+
+ {(loading || columns?.length === 0) && loading !== false
+ ? loadingContent || (
+ // TODO-luokun: loading样式优化
+
+
+
+ )
+ : itemHeight > 0 &&
+ wheelHeight > 0 &&
+ columns.map((column, index) => (
+
+ ))}
+
+ {/* mask */}
+ {this.renderMask(s)}
+
+ )
+ }
+
+ )
+ }
+}
diff --git a/components/picker-view/style/index.tsx b/components/picker-view/style/index.tsx
new file mode 100644
index 000000000..20d4bef47
--- /dev/null
+++ b/components/picker-view/style/index.tsx
@@ -0,0 +1,51 @@
+import { StyleSheet, ViewStyle } from 'react-native'
+
+export interface PickerViewStyle {
+ wrappper: ViewStyle
+ wheelWrapper: ViewStyle
+ mask: ViewStyle
+ maskTop: ViewStyle
+ maskMiddle: ViewStyle
+ maskBottom: ViewStyle
+}
+
+export default () =>
+ StyleSheet.create({
+ wrappper: {
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ overflow: 'hidden',
+ },
+ wheelWrapper: {
+ display: 'flex',
+ flexDirection: 'row',
+ backgroundColor: '#fff',
+ },
+ mask: {
+ position: 'absolute',
+ zIndex: 10000,
+ left: 0,
+ top: 0,
+ width: '100%',
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+
+ maskTop: {
+ flex: 1,
+ overflow: 'hidden',
+ },
+
+ maskMiddle: {
+ borderColor: '#eee',
+ borderTopWidth: 1,
+ borderBottomWidth: 1,
+ },
+
+ maskBottom: {
+ flex: 1,
+ overflow: 'hidden',
+ },
+ })
diff --git a/components/picker/MultiPicker.tsx b/components/picker/MultiPicker.tsx
deleted file mode 100644
index d7394306a..000000000
--- a/components/picker/MultiPicker.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react'
-import { View } from 'react-native'
-import MultiPickerMixin from './MultiPickerMixin'
-import MultiPickerProps from './MultiPickerProps'
-
-export interface MultiPickerProp {
- getValue: Function
-}
-
-const MultiPicker = (props: MultiPickerProp & MultiPickerProps) => {
- const { children, style } = props
- const selectedValue = props.getValue()
- const colElements = React.Children.map(children, (col: any, i) => {
- return React.cloneElement(col, {
- selectedValue: selectedValue[i],
- onValueChange: (...args: any[]) => props.onValueChange!(i, ...args),
- })
- })
- return {colElements}
-}
-
-export default MultiPickerMixin(MultiPicker)
diff --git a/components/picker/MultiPickerMixin.tsx b/components/picker/MultiPickerMixin.tsx
deleted file mode 100644
index b89d75250..000000000
--- a/components/picker/MultiPickerMixin.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react'
-import MultiPickerProps from './MultiPickerProps'
-
-export default function (ComposedComponent: any) {
- return class extends React.Component {
- static defaultProps = {
- prefixCls: 'rmc-multi-picker',
- onValueChange() {},
- }
-
- getValue = () => {
- const { children, selectedValue } = this.props
- if (selectedValue && selectedValue.length) {
- return selectedValue
- } else {
- if (!children) {
- return []
- }
- return React.Children.map(children, (c: any) => {
- const cc: any = React.Children.toArray(c.children || c.props.children)
- return cc && cc[0] && cc[0].props.value
- })
- }
- }
-
- onChange = (i: any, v: any, cb: any) => {
- const value = this.getValue().concat()
- value[i] = v
- if (cb) {
- cb(value, i)
- }
- }
-
- onValueChange = (i: any, v: any) => {
- this.onChange(i, v, this.props.onValueChange)
- }
-
- onScrollChange = (i: any, v: any) => {
- this.onChange(i, v, this.props.onScrollChange)
- }
-
- render() {
- return (
-
- )
- }
- }
-}
diff --git a/components/picker/MultiPickerProps.tsx b/components/picker/MultiPickerProps.tsx
deleted file mode 100644
index e8ecfc5bf..000000000
--- a/components/picker/MultiPickerProps.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { StyleProp, ViewStyle } from 'react-native'
-
-export interface PickerCol {
- key?: string
- props?: any
-}
-
-interface MultiPickerProps {
- selectedValue?: any[]
- rootNativeProps?: any
- onValueChange?: (v?: any, i?: number) => void
- children?: any
- style?: StyleProp
- onScrollChange?: (v?: any, i?: number) => void
-}
-
-export default MultiPickerProps
diff --git a/components/picker/NativePicker.android.tsx b/components/picker/NativePicker.android.tsx
deleted file mode 100644
index bbc2f3f67..000000000
--- a/components/picker/NativePicker.android.tsx
+++ /dev/null
@@ -1,174 +0,0 @@
-import React from 'react'
-import { PixelRatio, ScrollView, StyleSheet, Text, View } from 'react-native'
-import PickerMixin from './PickerMixin'
-import { PickerProps } from './PickerTypes'
-
-const ratio = PixelRatio.get()
-const styles = StyleSheet.create({
- indicator: {
- position: 'absolute',
- left: 0,
- width: '100%',
- borderColor: '#aaa',
- borderTopWidth: 1 / ratio,
- borderBottomWidth: 1 / ratio,
- } as any,
-
- selectedItemText: {
- fontSize: 20,
- fontWeight: 'bold',
- color: '#000',
- } as any,
-
- itemText: {
- fontSize: 20,
- color: '#aaa',
- textAlign: 'center',
- } as any,
-})
-
-export interface IPickerProp {
- select: Function
- doScrollingComplete: Function
-}
-
-class Picker extends React.Component {
- scrollBuffer: number
- scrollerRef: ScrollView | null
-
- state = {
- itemHeight: 0,
- }
-
- onItemLayout = (e: any) => {
- const { height } = e.nativeEvent.layout
- this.setState({ itemHeight: height }, () => {
- this.props.select(
- this.props.selectedValue,
- this.state.itemHeight,
- this.scrollTo,
- )
- })
- }
-
- componentDidUpdate() {
- this.props.select(
- this.props.selectedValue,
- this.state.itemHeight,
- this.scrollTo,
- )
- }
-
- componentWillUnmount() {
- this.clearScrollBuffer()
- }
-
- clearScrollBuffer() {
- if (this.scrollBuffer) {
- clearTimeout(this.scrollBuffer)
- }
- }
-
- scrollTo = (y: any) => {
- if (this.scrollerRef) {
- this.scrollerRef.scrollTo({
- y,
- animated: false,
- })
- }
- }
-
- fireValueChange = (selectedValue: any) => {
- if (
- this.props.selectedValue !== selectedValue &&
- this.props.onValueChange
- ) {
- this.props.onValueChange(selectedValue)
- }
- }
-
- onScroll = (e: any) => {
- const { y } = e.nativeEvent.contentOffset
- this.clearScrollBuffer()
- this.scrollBuffer = setTimeout(() => {
- this.clearScrollBuffer()
- this.props.doScrollingComplete(
- y,
- this.state.itemHeight,
- this.fireValueChange,
- )
- }, 50) as any
- }
-
- render() {
- const { itemHeight } = this.state
- const {
- children,
- itemStyle,
- selectedValue,
- style,
- numberOfLines = 1,
- } = this.props
- const items = React.Children.map(children, (item: any, index) => {
- const totalStyle = [styles.itemText]
- if (selectedValue === item.props.value) {
- totalStyle.push(styles.selectedItemText)
- }
- return (
-
-
- {item.props.label}
-
-
- )
- })
- return (
-
-
- {/* 计算中文占位符换行的高度后,items统一这个高度 */}
- {itemHeight === 0 && (
-
- {Array(numberOfLines).join('\n')}
-
- )}
- (this.scrollerRef = el)}
- onScroll={this.onScroll}
- showsVerticalScrollIndicator={false}
- overScrollMode="never"
- renderToHardwareTextureAndroid
- scrollEventThrottle={10}
- needsOffscreenAlphaCompositing
- collapsable
- horizontal={false}
- removeClippedSubviews>
-
- {items}
-
-
-
- )
- }
-}
-
-export default PickerMixin(Picker)
diff --git a/components/picker/NativePicker.ios.tsx b/components/picker/NativePicker.ios.tsx
deleted file mode 100644
index d9eb7606e..000000000
--- a/components/picker/NativePicker.ios.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { Picker as default } from '@react-native-picker/picker'
diff --git a/components/picker/NativePicker.tsx b/components/picker/NativePicker.tsx
deleted file mode 100644
index dae5f0123..000000000
--- a/components/picker/NativePicker.tsx
+++ /dev/null
@@ -1,2 +0,0 @@
-// hack for ts module resolution
-export { default } from './NativePicker.ios'
diff --git a/components/picker/Picker.tsx b/components/picker/Picker.tsx
index 02ffb13da..acb4be903 100644
--- a/components/picker/Picker.tsx
+++ b/components/picker/Picker.tsx
@@ -1,39 +1,180 @@
-import React from 'react'
-import NativePicker from './NativePicker'
-import { PickerProps } from './PickerTypes'
+import useMergedState from 'rc-util/lib/hooks/useMergedState'
+import React, {
+ forwardRef,
+ useCallback,
+ useContext,
+ useImperativeHandle,
+ useMemo,
+ useState,
+} from 'react'
+import { GestureHandlerRootView } from 'react-native-gesture-handler'
+import { Omit } from 'utility-types'
-const Item: any = NativePicker.Item
+import { getComponentLocale } from '../_util/getLocale'
+import { LocaleContext } from '../locale-provider'
+import { PickerColumn, PickerValue } from '../picker-view/PropsType'
+import RMCPickerView from '../picker-view/picker-view'
+import { useTheme } from '../style'
+import PopupPicker from './Popup'
+import { PickerPropsType } from './PropsType'
+import PickerStyles from './style'
-class Picker extends React.Component {
- static defaultProps = {
- children: [],
- }
+export type PickerActions = {
+ open: () => void
+ close: () => void
+ toggle: () => void
+ _updateExtra: () => void
+}
+export type PickerRef = PickerActions
- static Item: any = () => {}
+export interface RMCPickerProps
+ extends Omit {
+ value: PickerValue[]
+ columns: PickerColumn[]
+ handleSelect: (value: PickerValue, index: number) => void
+}
- getValue() {
- if ('selectedValue' in this.props) {
- return this.props.selectedValue
- }
- const children: any = React.Children.toArray(this.props.children)
- return children && children[0] && children[0].props.value
- }
+const RMCPicker = forwardRef((props, ref) => {
+ const {
+ onVisibleChange,
+ visible,
+ children,
+ okText,
+ dismissText,
+ onChange,
+ onOk,
+ onDismiss,
+ onClose,
+ title,
+ extra,
+ disabled,
+ format,
+ value,
+ columns,
+ handleSelect,
+ ...restProps
+ } = props
+
+ const [innerVisible, setInnerVisible] = useMergedState(false, {
+ value: visible,
+ onChange: (v) => onVisibleChange?.(v),
+ })
+ const [innerExtra, setInnerExtra] = useState(extra)
- shouldComponentUpdate(nextProps: any) {
- return (
- this.props.selectedValue !== nextProps.selectedValue ||
- this.props.children !== nextProps.children
+ const actions = useMemo(
+ () => ({
+ toggle: () => {
+ setInnerVisible(!innerVisible)
+ },
+ open: () => {
+ setInnerVisible(true)
+ },
+ close: () => {
+ setInnerVisible(false)
+ onClose?.()
+ },
+ // TODO: remove hack
+ _updateExtra: () => {
+ setInnerExtra(
+ format?.(
+ columns.reduce((cur: any[], next, index) => {
+ const labelValue = next.find(
+ (item) => item.value === value?.[index],
+ )
+ if (labelValue?.label) {
+ cur.push(labelValue.label)
+ }
+ return cur
+ }, []),
+ ),
+ )
+ },
+ }),
+ [columns, format, innerVisible, onClose, setInnerVisible, value],
+ )
+ useImperativeHandle(ref, () => actions)
+
+ const handleOk = useCallback(() => {
+ const extend = columns.map(
+ (column, index) =>
+ column.find((item) => item.value === value?.[index]) ?? column[0],
)
- }
+ const nextValue = extend.map((item) => item.value)
+ onOk?.(nextValue, { items: extend, columns })
+ onChange?.(nextValue, { items: extend, columns })
+ actions.close()
+ }, [actions, columns, onChange, onOk, value])
- render() {
- const children = React.Children.map(this.props.children, (c: any) => {
- return (
-
- )
- })
- return {children}
+ const handleDismiss = useCallback(() => {
+ onDismiss?.()
+ actions.close()
+ }, [actions, onDismiss])
+
+ const _locale = getComponentLocale(
+ props,
+ useContext(LocaleContext),
+ 'Picker',
+ () => require('./locale/zh_CN'),
+ )
+
+ const renderChildren = () => {
+ if (!children) {
+ return null
+ }
+ const child = children as any
+ const newChildProps = {
+ extra: innerExtra || extra || _locale.extra,
+ disabled,
+ } as any
+ if (!disabled) {
+ newChildProps[props.triggerType!] = (e: any) => {
+ if (child.props[props.triggerType!]) {
+ child.props[props.triggerType!](e)
+ }
+ actions.toggle()
+ }
+ }
+ return React.cloneElement(child as any, newChildProps)
}
+
+ const styles = useTheme({
+ styles: props.styles,
+ themeStyles: PickerStyles,
+ })
+
+ return (
+ <>
+
+ {/* TODO: 组件卸载是在visible更新fasle之后,需要前置 */}
+ {/* 否则会无效执行onPickerChange */}
+ {innerVisible && (
+
+
+
+ )}
+
+ {renderChildren()}
+ >
+ )
+})
+
+RMCPicker.displayName = 'Picker'
+RMCPicker.defaultProps = {
+ triggerType: 'onPress',
+ format: (labels: string[]) => labels.join(','),
}
-export default Picker
+export default RMCPicker
diff --git a/components/picker/PickerMixin.tsx b/components/picker/PickerMixin.tsx
deleted file mode 100644
index 0cb3cb04d..000000000
--- a/components/picker/PickerMixin.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/* tslint:disable:no-console */
-import React from 'react'
-import { PickerProps } from './PickerTypes'
-
-type ItemProps = {
- value: any
-}
-
-const Item = (_props: ItemProps) => null
-
-export default function (ComposedComponent: any) {
- return class extends React.Component {
- static Item = Item
-
- select = (value: any, itemHeight: any, scrollTo: any) => {
- const children: any = React.Children.toArray(this.props.children)
- for (let i = 0, len = children.length; i < len; i++) {
- if (children[i].props.value === value) {
- this.selectByIndex(i, itemHeight, scrollTo)
- return
- }
- }
- this.selectByIndex(0, itemHeight, scrollTo)
- }
-
- selectByIndex(index: number, itemHeight: any, zscrollTo: any) {
- if (
- index < 0 ||
- index >= React.Children.count(this.props.children) ||
- !itemHeight
- ) {
- return
- }
- zscrollTo(index * itemHeight)
- }
-
- computeChildIndex(top: any, itemHeight: any, childrenLength: number) {
- const index = Math.round(top / itemHeight)
- return Math.min(index, childrenLength - 1)
- }
-
- doScrollingComplete = (top: any, itemHeight: any, fireValueChange: any) => {
- const children = React.Children.toArray(this.props.children)
- const index = this.computeChildIndex(top, itemHeight, children.length)
- const child: any = children[index]
- if (child) {
- fireValueChange(child.props.value)
- } else if (console.warn) {
- console.warn('child not found', children, index)
- }
- }
-
- render() {
- return (
-
- )
- }
- }
-}
diff --git a/components/picker/PickerTypes.tsx b/components/picker/PickerTypes.tsx
deleted file mode 100644
index 9c441471f..000000000
--- a/components/picker/PickerTypes.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { StyleProp, TextStyle, ViewStyle } from 'react-native'
-
-export type PickerProps = {
- disabled?: boolean
- selectedValue?: any
- onValueChange?: (value: any) => void
- itemStyle?: StyleProp
- indicatorStyle?: StyleProp
- indicatorClassName?: string
- defaultSelectedValue?: any
- style?: StyleProp
- onScrollChange?: (value: any) => void
- noAnimate?: boolean
- numberOfLines?: number
-}
diff --git a/components/picker/Popup.tsx b/components/picker/Popup.tsx
index d15e7f481..f67e00f28 100644
--- a/components/picker/Popup.tsx
+++ b/components/picker/Popup.tsx
@@ -1,14 +1,20 @@
-import React from 'react'
+import React, { memo } from 'react'
import { Text, TouchableHighlight, View } from 'react-native'
import Modal from '../modal/ModalView'
-import PopupMixin from './PopupMixin'
+import { PopupPickerProps } from './PopupPickerTypes'
-const getModal = (
- props: any,
- visible: any,
- { getContent, hide, onDismiss, onOk }: any,
-) => {
- const { styles, title, okText, dismissText } = props
+const PopupPicker = memo((props: PopupPickerProps) => {
+ const {
+ styles,
+ title,
+ okText = 'Ok',
+ dismissText = 'Dismiss',
+ visible,
+ onDismiss,
+ onOk,
+ onClose,
+ children,
+ } = props
const titleEl =
typeof title === 'string' ? (
@@ -35,7 +41,7 @@ const getModal = (
wrapStyle={styles.modal}
style={styles.container}
visible={visible}
- onClose={hide}>
+ onClose={onClose}>
- {getContent()}
+ {children}
)
-}
-
-export default PopupMixin(getModal, {
- actionTextUnderlayColor: '#ddd',
- actionTextActiveOpacity: 1,
- triggerType: 'onPress',
- styles: {},
- WrapComponent: View as any,
})
+export default PopupPicker
diff --git a/components/picker/PopupMixin.tsx b/components/picker/PopupMixin.tsx
deleted file mode 100644
index e463a072c..000000000
--- a/components/picker/PopupMixin.tsx
+++ /dev/null
@@ -1,164 +0,0 @@
-import React from 'react'
-import { View } from 'react-native'
-import { PopupPickerProps } from './PopupPickerTypes'
-
-interface Args {
- getContent: any
- hide: any
- onDismiss: any
- onOk: any
-}
-
-export default function PopupMixin(
- getModal: (props: any, visible: any, args: Args) => React.ReactNode,
- platformProps: {
- actionTextUnderlayColor: string
- actionTextActiveOpacity: number
- triggerType: string
- styles: {}
- WrapComponent: View
- },
-) {
- return class extends React.Component {
- static defaultProps = {
- onVisibleChange(_: any) {},
- okText: 'Ok',
- dismissText: 'Dismiss',
- title: '',
- onOk(_: any) {},
- onDismiss() {},
- ...platformProps,
- }
-
- picker: any
-
- constructor(props: Readonly) {
- super(props)
-
- this.state = {
- pickerValue: 'value' in this.props ? this.props.value : null,
- visible: this.props.visible || false,
- }
- }
-
- UNSAFE_componentWillReceiveProps(nextProps: { value: any; visible: any }) {
- if ('value' in nextProps) {
- this.setState({
- pickerValue: nextProps.value,
- })
- }
- if ('visible' in nextProps) {
- this.setVisibleState(nextProps.visible)
- }
- }
-
- onPickerChange = (pickerValue: any) => {
- if (this.state.pickerValue !== pickerValue) {
- this.setState({
- pickerValue,
- })
- const { picker, pickerValueChangeProp } = this.props
- if (picker && picker.props[pickerValueChangeProp!]) {
- picker.props[pickerValueChangeProp!](pickerValue)
- }
- }
- }
-
- saveRef = (picker: any) => {
- this.picker = picker
- }
-
- setVisibleState(visible: any) {
- this.setState({
- visible,
- })
- if (!visible) {
- this.setState({
- pickerValue: null,
- })
- }
- }
-
- fireVisibleChange(visible: boolean) {
- if (this.state.visible !== visible) {
- if (!('visible' in this.props)) {
- this.setVisibleState(visible)
- }
- this.props.onVisibleChange!(visible)
- }
- }
-
- getRender() {
- const props = this.props
- const children = props.children
- if (!children) {
- return getModal(props, this.state.visible, {
- getContent: this.getContent,
- onOk: this.onOk,
- hide: this.hide,
- onDismiss: this.onDismiss,
- })
- }
- const { WrapComponent, disabled } = this.props
- const child = children
- const newChildProps = {}
- if (!disabled) {
- ;(newChildProps as any)[props.triggerType!] = this.onTriggerClick
- }
- return (
-
- {React.cloneElement(child as any, newChildProps)}
- {getModal(props, this.state.visible, {
- getContent: this.getContent,
- onOk: this.onOk,
- hide: this.hide,
- onDismiss: this.onDismiss,
- })}
-
- )
- }
-
- onTriggerClick = (e: any) => {
- const child: any = this.props.children
- const childProps = child.props || {}
- if (childProps[this.props.triggerType!]) {
- childProps[this.props.triggerType!](e)
- }
- this.fireVisibleChange(!this.state.visible)
- }
-
- onOk = () => {
- this.props.onOk!(this.picker && this.picker.getValue())
- this.fireVisibleChange(false)
- }
-
- getContent = () => {
- if (this.props.picker) {
- let { pickerValue } = this.state
- if (pickerValue === null) {
- pickerValue = this.props.value
- }
- return React.cloneElement(this.props.picker, {
- [this.props.pickerValueProp!]: pickerValue,
- [this.props.pickerValueChangeProp!]: this.onPickerChange,
- ref: this.saveRef,
- })
- } else {
- return this.props.content
- }
- }
-
- onDismiss = () => {
- this.props.onDismiss!()
- this.fireVisibleChange(false)
- }
-
- hide = () => {
- this.fireVisibleChange(false)
- }
-
- render() {
- return this.getRender()
- }
- }
-}
diff --git a/components/picker/PopupPickerTypes.tsx b/components/picker/PopupPickerTypes.tsx
index 3920286f6..aaaa4a24b 100644
--- a/components/picker/PopupPickerTypes.tsx
+++ b/components/picker/PopupPickerTypes.tsx
@@ -1,27 +1,15 @@
-import React from 'react'
+import type { ReactNode } from 'react'
export type PopupPickerProps = {
- picker?: any
- value?: any
- triggerType?: string
- WrapComponent?: any
- dismissText?: string | React.ReactElement // React.ReactElement only for web
- okText?: string | React.ReactElement // React.ReactElement only for web
- title?: string | React.ReactElement // React.ReactElement only for web
- visible?: boolean
- disabled?: boolean
- onOk?: (value?: any) => void
- style?: any
- onVisibleChange?: (visible: boolean) => void
- content?: React.ReactElement | string
+ onOk?: () => void
onDismiss?: () => void
- styles?: any
+ onClose?: () => void
+ title?: ReactNode
+ visible?: boolean
+ okText?: ReactNode
+ dismissText?: ReactNode
+ children?: ReactNode
+ styles: any
actionTextUnderlayColor?: string
actionTextActiveOpacity?: number
- wrapStyle?: React.CSSProperties
- pickerValueProp?: string
- pickerValueChangeProp?: string
- transitionName?: string
- popupTransitionName?: string
- maskTransitionName?: string
}
diff --git a/components/picker/PopupStyles.tsx b/components/picker/PopupStyles.tsx
deleted file mode 100644
index de67f97da..000000000
--- a/components/picker/PopupStyles.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { StyleSheet } from 'react-native'
-
-const textStyle = {
- color: '#0ae',
- fontSize: 18,
- textAlign: 'center',
-}
-
-const styles = StyleSheet.create({
- modal: {
- flexDirection: 'column',
- justifyContent: 'flex-end',
- },
- container: {},
- header: {
- // flex:1, 0.39.0 needs to remove
- height: 44,
- alignItems: 'center',
- flexDirection: 'row',
- justifyContent: 'center',
- borderBottomWidth: 1,
- borderBottomColor: '#e7e7e7',
- },
- headerItem: {
- height: 44,
- alignItems: 'center',
- justifyContent: 'center',
- flex: 1,
- },
- actionText: textStyle,
- okText: {},
- dismissText: {},
- title: {
- ...textStyle,
- color: '#666',
- },
-})
-
-export default styles
diff --git a/components/picker/PropsType.tsx b/components/picker/PropsType.tsx
index 377de595f..e3e912c9b 100644
--- a/components/picker/PropsType.tsx
+++ b/components/picker/PropsType.tsx
@@ -1,22 +1,26 @@
-import { StyleProp, TextStyle, ViewStyle } from 'react-native'
+import type { ReactNode } from 'react'
import { Omit } from 'utility-types'
+import {
+ PickerValue,
+ PickerValueExtend,
+ PickerViewPropsType,
+} from '../picker-view/PropsType'
+import { PickerViewStyle } from '../picker-view/style'
+import { WithThemeStyles } from '../style'
import { PopupPickerProps } from './PopupPickerTypes'
-import { CascaderValue } from './cascader/CascaderTypes'
-export interface PickerData {
- value: string | number
- label: string
- children?: PickerData[]
-}
-export interface PickerPropsType extends Omit {
- data: PickerData[] | PickerData[][]
- cascade?: boolean
- value?: Array
- format?: (values: string[]) => string
- cols?: number
+import { PickerStyle } from './style'
+
+export interface PickerPropsType
+ extends PickerViewPropsType,
+ WithThemeStyles,
+ Omit {
+ onOk?: (value: PickerValue[], extend: PickerValueExtend) => void
+ onPickerChange?: (value: PickerValue[], index: number) => void
+ onVisibleChange?: (visible: boolean) => void
+ format?: (labels: string[]) => string
extra?: string
- onChange?: (date?: CascaderValue) => void
- onPickerChange?: (value: CascaderValue) => void
- itemStyle?: StyleProp
- indicatorStyle?: StyleProp
- numberOfLines?: number
+ triggerType?: string
+ disabled?: boolean
+ children?: ReactNode
+ locale?: { okText?: string; dismissText?: string; extra?: string }
}
diff --git a/components/picker/__tests__/__snapshots__/demo.test.js.snap b/components/picker/__tests__/__snapshots__/demo.test.js.snap
index a58301127..c6e28f8d5 100644
--- a/components/picker/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/picker/__tests__/__snapshots__/demo.test.js.snap
@@ -1,13 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/picker/demo/basic.tsx correctly 1`] = `
-
+
+ >
+ List Children
+
-
+
@@ -48,117 +68,115 @@ exports[`renders ./components/picker/demo/basic.tsx correctly 1`] = `
style={
Array [
Object {
- "alignItems": "center",
- "borderBottomColor": "#dddddd",
- "borderBottomWidth": 0.5,
"flex": 1,
- "flexDirection": "row",
- "minHeight": 44,
- "paddingRight": 15,
- "paddingVertical": 6,
+ "flexDirection": "column",
},
- false,
- false,
]
}
>
-
-
- 省市选择
-
-
-
-
- 请选择
-
-
+ 省市选择
+
+
+
-
+ 请选择
+
+
+
-
+
@@ -166,142 +184,121 @@ exports[`renders ./components/picker/demo/basic.tsx correctly 1`] = `
style={
Array [
Object {
- "alignItems": "center",
- "borderBottomColor": "#dddddd",
- "borderBottomWidth": 0.5,
"flex": 1,
- "flexDirection": "row",
- "minHeight": 44,
- "paddingRight": 15,
- "paddingVertical": 6,
+ "flexDirection": "column",
},
- false,
- false,
]
}
>
-
-
- 省市选择(异步加载)
-
-
-
-
- 请选择
-
-
+ 省市选择(异步加载)
+
+
+
-
+ 请选择
+
+
+
-
+
-
-
- Customized children
-
-
+
- 请选择
-
-
+ }
+ >
+ 请选择
+
+
+ visible 控制显示/隐藏
+
+
+
+
+
+ 选择
+
+
+
+
+ 未选择
+
+
`;
diff --git a/components/picker/__tests__/__snapshots__/index.test.js.snap b/components/picker/__tests__/__snapshots__/index.test.js.snap
index 6f0218b8d..23f73dac3 100644
--- a/components/picker/__tests__/__snapshots__/index.test.js.snap
+++ b/components/picker/__tests__/__snapshots__/index.test.js.snap
@@ -10,15 +10,13 @@ Array [
}
}
>
-
-
- 省市选择
-
-
+
+ 省市选择
+
,
-
-
-
-
+ />
-
-
-
-
+
-
-
-
-
+ >
+
+
+
+
+
diff --git a/components/picker/cascader/Cascader.tsx b/components/picker/cascader/Cascader.tsx
deleted file mode 100644
index 653e30f35..000000000
--- a/components/picker/cascader/Cascader.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import arrayTreeFilter from 'array-tree-filter'
-import React from 'react'
-import MultiPicker from '../MultiPicker'
-import Picker from '../Picker'
-import { CascaderDataItem, CascaderProps } from './CascaderTypes'
-
-class Cascader extends React.Component {
- static defaultProps = {
- cols: 3,
- data: [],
- disabled: false,
- }
-
- state = {
- value: this.getValue(
- this.props.data,
- this.props.defaultValue || this.props.value,
- ),
- }
-
- UNSAFE_componentWillReceiveProps(nextProps: { data: any; value: any }) {
- if ('value' in nextProps) {
- this.setState({
- value: this.getValue(nextProps.data, nextProps.value),
- })
- }
- }
-
- onValueChange = (value: any, index: any) => {
- const children = arrayTreeFilter(this.props.data, (c, level) => {
- return level <= index && c.value === value[level]
- })
- let data = children[index]
- let i
- for (
- i = index + 1;
- data && data.children && data.children.length && i < this.props.cols!;
- i++
- ) {
- data = data.children[0]
- value[i] = data.value
- }
- value.length = i
- if (!('value' in this.props)) {
- this.setState({
- value,
- })
- }
- if (this.props.onChange) {
- this.props.onChange(value)
- }
- }
-
- getValue(d: any, val: any) {
- let data = d || this.props.data
- const value = val || this.props.value || this.props.defaultValue
- let level = 0
- const nextValue = []
-
- if (value && value.length) {
- do {
- const index = (data as CascaderDataItem[]).findIndex(
- (item) => item.value === value[level],
- )
-
- if (index < 0) {
- break
- }
-
- nextValue[level] = value[level]
- level += 1
- data = data[index].children || []
- } while (data.length > 0)
- }
-
- for (let i = level; i < this.props.cols!; i++) {
- if (data && data.length) {
- nextValue[i] = data[0].value
- data = data[0].children
- } else {
- break
- }
- }
-
- return nextValue
- }
-
- getCols() {
- const {
- data,
- cols,
- disabled,
- pickerItemStyle,
- indicatorStyle,
- numberOfLines,
- } = this.props
- const value = this.state.value
- const childrenTree = arrayTreeFilter(data, (c, level) => {
- return c.value === value[level]
- }).map((c) => c.children)
-
- // in case the users data is async get when select change
- const needPad = cols! - childrenTree.length
- if (needPad > 0) {
- for (let i = 0; i < needPad; i++) {
- childrenTree.push([])
- }
- }
- childrenTree.length = cols! - 1
- childrenTree.unshift(data)
- return childrenTree.map((children: any[] = [], level) => (
-
- {children.map((item) => (
-
- {item.label}
-
- ))}
-
- ))
- }
-
- render() {
- const props = this.props
- const { rootNativeProps, style } = props
- const cols = this.getCols()
-
- return (
-
- {cols}
-
- )
- }
-}
-
-export default Cascader
diff --git a/components/picker/cascader/CascaderTypes.tsx b/components/picker/cascader/CascaderTypes.tsx
deleted file mode 100644
index 2f8948840..000000000
--- a/components/picker/cascader/CascaderTypes.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import * as React from 'react'
-import { StyleProp, TextStyle, ViewStyle } from 'react-native'
-
-export type CascaderOneValue = string | number
-export type CascaderValue = CascaderOneValue[]
-
-export interface CascaderDataItem {
- label: React.ReactNode
- value: CascaderOneValue
- children?: CascaderDataItem[]
-}
-
-export interface CascaderProps {
- defaultValue?: CascaderValue
- value?: CascaderValue
- onChange?: (value: CascaderValue) => void
- data: CascaderDataItem[]
- cols?: number
- disabled?: boolean
- rootNativeProps?: {}
- pickerItemStyle?: StyleProp
- indicatorStyle?: StyleProp
- style?: StyleProp
- onScrollChange?: (value: CascaderValue) => void
- numberOfLines?: number
-}
diff --git a/components/picker/cascader/Popup.tsx b/components/picker/cascader/Popup.tsx
deleted file mode 100644
index 9e98f3acc..000000000
--- a/components/picker/cascader/Popup.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react'
-import PopupPicker from '../Popup'
-import { PopupPickerProps } from '../PopupPickerTypes'
-import { CascaderProps, CascaderValue } from './CascaderTypes'
-
-export interface IPopupCascaderProps extends PopupPickerProps {
- cascader: React.ReactElement
- onChange?: (date?: CascaderValue) => void
- children?: React.ReactNode
-}
-
-class PopupCascader extends React.Component {
- static defaultProps = {
- pickerValueProp: 'value',
- pickerValueChangeProp: 'onChange',
- }
-
- onOk = (v: any) => {
- const { onChange, onOk } = this.props
- if (onChange) {
- onChange(v)
- }
- if (onOk) {
- onOk(v)
- }
- }
-
- render() {
- return (
-
- )
- }
-}
-
-export default PopupCascader
diff --git a/components/picker/cascader/PopupStyles.tsx b/components/picker/cascader/PopupStyles.tsx
deleted file mode 100644
index 9dac90392..000000000
--- a/components/picker/cascader/PopupStyles.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '../PopupStyles'
diff --git a/components/picker/cascader/index.tsx b/components/picker/cascader/index.tsx
deleted file mode 100644
index 6eaa35f2f..000000000
--- a/components/picker/cascader/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './Cascader'
diff --git a/components/picker/demo/basic.tsx b/components/picker/demo/basic.tsx
index 9fbc77ef1..5b1de50d9 100644
--- a/components/picker/demo/basic.tsx
+++ b/components/picker/demo/basic.tsx
@@ -1,8 +1,9 @@
const data = require('./data.json')
import { district } from 'antd-mobile-demo-data'
-import React from 'react'
+import React, { useState } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
-import { List, Picker } from '../../'
+import { Button, List, Picker } from '../../'
+import { PickerValue, PickerValueExtend } from '../../picker-view/PropsType'
const CustomChildren = (props: any) => (
@@ -21,6 +22,52 @@ const CustomChildren = (props: any) => (
)
+// visible用法
+function BasicDemo() {
+ const [visible, setVisible] = useState(false)
+ const [value, setValue] = useState([])
+ const [extend, setExtend] = useState()
+ return (
+
+
+
+ {/* extend渲染所选值 */}
+
+ {extend?.items?.map((item: any) => item.label).join(',') || ' 未选择'}
+
+
+ {/* visible控制显示/隐藏 */}
+ {
+ setVisible(false)
+ }}
+ visible={visible}
+ value={value}
+ onOk={(v: PickerValue[], ext: PickerValueExtend) => {
+ setValue(v)
+ setExtend(ext)
+ }}
+ />
+
+ )
+}
+
export default class PopupExample extends React.Component {
constructor(props: any) {
super(props)
@@ -40,11 +87,14 @@ export default class PopupExample extends React.Component {
onChange = (value: any) => {
this.setState({ value })
}
+
render() {
return (
-
+
+ List Children
{
Customized children
+ visible 控制显示/隐藏
+
)
}
diff --git a/components/picker/index.en-US.md b/components/picker/index.en-US.md
index bf8879b31..d2c36408a 100644
--- a/components/picker/index.en-US.md
+++ b/components/picker/index.en-US.md
@@ -12,24 +12,85 @@ Choose from a set of data, e.g. Country choice.
## API
+### Props
+
+```ts
+type PickerColumnItem = {
+ label: string | ReactNode
+ value: string | number
+ key?: string | number
+ children?: PickerColumnItem[]
+}
+
+type PickerColumn = PickerColumnItem[]
+
+type PickerValue = string | number
+
+type PickerValueExtend = {
+ columns: PickerColumn[]
+ items: (PickerColumnItem | undefined)[]
+}
+```
+
Properties | Descrition | Type | Default
-----------|------------|------|--------
-| data | data source | `Array<{value, label, children: Array}>` | - |
-| value | the value, the format is `[value1, value2, value3]`, corresponds to the level value of the data source | Array | - |
-| format | a function that formats the selected value | (labels: string[]): any | `(labels) => { return labels.join(','); } ` |
-| cols | col numbers | Number | `3` |
-| onChange | selected callback function, can use [rc-form](https://github.com/react-component/form) | (val): void | - |
-| onPickerChange | trigger on each column of selected data is changed | (val): void | - |
-| onVisibleChange | visible state change callback | (visible: bool): void | - |
-| itemStyle | style to apply to each of the item labels | Object | -|
-| 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 |
-| indicatorStyle | style of indicator | Object | - |
-| children| usually `List.Item` | Object | `List.Item` |
-| okText | ok text | String | `确定` |
+| data | data source | `PickerColumn` / `PickerColumn[]` | - |
+| value | Selected options | `PickerValue[]` | - |
+| defaultValue | Default selected options | `PickerValue[]` | - |
+| cascade | whether cascade
child cascade get from `data[].children` | Boolean | `true` |
+| cols | col numbers | Number | `3` |
+| onChange | selected callback function, can use [rc-form](https://github.com/react-component/form) | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
+| onPickerChange | trigger on each column of selected data is changed | `(value: PickerValue[], index: number) => void` | - |
+| onVisibleChange | visible state change callback | `(visible: bool): void` | - |
+| renderLabel | The function to custom rendering the label shown on a column | `(item: PickerColumnItem, index: number) => ReactNode` | `(item) => item.label` |
+| locale | international, can override the configuration of the global [LocaleProvider](/components/locale-provider) | Object: Object: {okText, dismissText, extra} | - |
+| title | title | ReactNode | - |
+| okText | ok text | String | `确定` |
| dismissText | dismiss text | String | `取消` |
-| onOk | handler called when click ok | (val): void | - |
+| onOk | handler called when click ok | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
| onDismiss | handler called when click cancel | (): void | - |
-| title | title | String | - |
-| extra | Picker's children is best to `List.Item`, if not, need to be a custom component (the `onClick`/`extra` props need to be handled in the component) | String | `请选择` |
-| disabled | set disabled | Boolean | false |
-| cascade | whether is cascade mode | Boolean | true |
+| visible | Whether to show or hide the Picker | Boolean | - |
+| loading | Should the Picker displays as loading state | Boolean | - |
+| loadingContent | The loading content displayed in loading state | ReactNode | - |
+| indicatorStyle | style of default Indicator | Object | - |
+
+### Custom Style
+
+Properties | Descrition | Type | Default
+-----------|------------|------|--------
+| style | style | `StyleProp` | - |
+| styles | inner component styles | interface `PickerViewStyle` | - |
+| itemStyle| style to apply to each of the item labels | `StyleProp` | - |
+| itemHeight | Height of option item, calculated by `numberOfLines` when without value; `itemStyle` was not allowed to set `{height}` | Number | - |
+| 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` |
+
+#### Mask View
+
+Support custom mask style, such as using the gradient component ``. Default is white translucency.
+
+Properties | Descrition | Type | Default
+-----------|------------|------|--------
+| renderMaskTop | The function to custom rendering the mask top half view | `()=> ReactNode` | `` |
+| renderMaskBottom | The function to custom rendering the mask bottom half view | `()=> ReactNode` | `` |
+
+
+### Children
+Picker's children is best to [List.Item](/components/list/#List.Item), if not, need to be a custom component (the `onClick`/`extra` props need to be handled in the component):
+
+Properties | Descrition | Type | Default
+-----------|------------|------|--------
+| children| usually `List.Item` | ReactNode | `List.Item` |
+| extra | Picker's children `extra` prop, display when no `value` | String | `请选择` |
+| format | a function that formats the selected value | (labels: string[]): any | `(labels) => { return labels.join(','); } ` |
+| triggerType | Press event name | String | `onPress` |
+| disabled | set disabled | Boolean | `false` |
+
+### PickerActions
+Properties | Descrition | Type
+-----------|------------|-----
+| close | Close Picker | `() => void` |
+| open | Open Picker | `() => void` |
+| toggle | Toggle the visible state of Picker | `() => void` |
+
+### Ref
+Same as PickerActions
diff --git a/components/picker/index.tsx b/components/picker/index.tsx
index d7ddf925d..de7ec2466 100644
--- a/components/picker/index.tsx
+++ b/components/picker/index.tsx
@@ -1,240 +1,97 @@
-import treeFilter from 'array-tree-filter'
-import React from 'react'
-import { getComponentLocale } from '../_util/getLocale'
-import { LocaleContext } from '../locale-provider'
-import { WithTheme, WithThemeStyles } from '../style'
-import MultiPicker from './MultiPicker'
-import RMCPicker from './Picker'
-import { PickerData, PickerPropsType } from './PropsType'
-import RMCCascader from './cascader'
-import RMCPopupCascader from './cascader/Popup'
-import PickerStyles, { PickerStyle } from './style'
+import React, {
+ forwardRef,
+ useCallback,
+ useEffect,
+ useImperativeHandle,
+ useMemo,
+ useRef,
+ useState,
+} from 'react'
+import { mergeProps } from '../_util/with-default-props'
+import { PickerValue } from '../picker-view/PropsType'
+import { getColumns, getValueExtend } from '../picker-view/columns-extend'
+import RMCPicker, { PickerRef } from './Picker'
+import { PickerPropsType } from './PropsType'
-export interface PickerProps
- extends PickerPropsType,
- WithThemeStyles {
- pickerPrefixCls?: string
- popupPrefixCls?: string
- children: React.ReactNode
-}
+export interface PickerProps extends PickerPropsType {}
-export function getDefaultProps() {
- const defaultFormat = (values: string[]) => {
- return values.join(',')
- }
- return {
- triggerType: 'onPress',
- prefixCls: 'am-picker',
- pickerPrefixCls: 'am-picker-col',
- popupPrefixCls: 'am-picker-popup',
- format: defaultFormat,
- cols: 3,
- cascade: true,
- title: '',
- }
+const defaultProps = {
+ defaultValue: [],
+ cols: 3,
+ cascade: true,
}
-export default class Picker extends React.Component {
- static contextType = LocaleContext
- static defaultProps = getDefaultProps()
- protected popupProps: {}
- private scrollValue: any
- getSel = () => {
- const value = this.props.value || []
- let treeChildren: PickerData[]
- const { data } = this.props
- if (this.props.cascade) {
- treeChildren = treeFilter(data as PickerData[], (c: any, level: any) => {
- return c.value === value[level]
- })
- } else {
- treeChildren = value.map((v, i) => {
- return (data as PickerData[][])[i].filter((d) => d.value === v)[0]
- })
- }
- return (
- this.props.format &&
- this.props.format(
- treeChildren.map((v) => {
- return v.label
- }),
- )
- )
- }
+const Picker = forwardRef((props, ref) => {
+ const p = mergeProps(defaultProps, props)
- getPickerCol = () => {
- const { data, itemStyle, indicatorStyle, numberOfLines } = this.props
+ const [innerValue, setInnerValue] = useState(
+ p.value === undefined ? p.defaultValue : p.value,
+ )
- return ((Array.isArray(data[0]) ? data : [data]) as PickerData[][]).map(
- (col, index) => {
- return (
-
- {col.map((item) => {
- return (
-
- {item.label}
-
- )
- })}
-
- )
- },
- )
- }
+ const pickerRef = React.useRef(null)
- onOk = (v: any) => {
- if (this.scrollValue !== undefined) {
- v = this.scrollValue
- }
- if (this.props.onChange) {
- this.props.onChange(v)
- }
- if (this.props.onOk) {
- this.props.onOk(v)
- }
- }
+ useImperativeHandle(ref, () => pickerRef.current as PickerRef)
- setScrollValue = (v: any) => {
- this.scrollValue = v
- }
+ const columns = useMemo(
+ () => getColumns(p.data, innerValue, p.cols, p.cascade),
+ [p.data, innerValue, p.cols, p.cascade],
+ )
- setCasecadeScrollValue = (v: any) => {
- // 级联情况下保证数据正确性,滚动过程中只有当最后一级变化时才变更数据
- if (v && this.scrollValue) {
- const length = this.scrollValue.length
- if (
- length === v.length &&
- this.scrollValue[length - 1] === v[length - 1]
- ) {
- return
- }
- }
- this.setScrollValue(v)
- }
+ const handleSelect = useCallback(
+ (val: PickerValue, index: number) => {
+ const value = innerValue?.slice?.(0) || []
+ value[index] = val
+ const { nextValue } = getValueExtend(p.data, value, p.cols, p.cascade)
+ setInnerValue(nextValue)
+ p.onPickerChange?.(nextValue, index)
+ },
+ [p, innerValue, setInnerValue],
+ )
- fixOnOk = (cascader: any) => {
- if (cascader && cascader.onOk !== this.onOk) {
- cascader.onOk = this.onOk
- cascader.forceUpdate()
- }
- }
+ // 记录value是否变化过
+ const isValueChanged = useRef(false)
- onPickerChange = (v: any) => {
- this.setScrollValue(v)
- if (this.props.onPickerChange) {
- this.props.onPickerChange(v)
- }
- }
+ const onVisibleChange = useCallback(
+ (visible) => {
+ p.onVisibleChange?.(visible)
+ if (!visible && p.value !== innerValue && isValueChanged.current) {
+ // 关闭时,如果选中值不同步,恢复为原选中值
+ setInnerValue(p.value || [])
+ }
+ },
+ [innerValue, p, setInnerValue],
+ )
- onVisibleChange = (visible: boolean) => {
- this.setScrollValue(undefined)
- if (this.props.onVisibleChange) {
- this.props.onVisibleChange(visible)
- }
- }
+ // for useEffect only on update
+ const isInitialMount = useRef(true)
- render() {
- const {
- children,
- value = [],
- popupPrefixCls,
- itemStyle,
- indicatorStyle,
- numberOfLines,
- okText,
- dismissText,
- extra,
- cascade,
- data,
- cols,
- onOk,
- ...restProps
- } = this.props
+ useEffect(() => {
+ if (isInitialMount.current) {
+ isInitialMount.current = false
+ // extra update initial
+ pickerRef.current?._updateExtra()
+ } else {
+ isValueChanged.current = true
+ setInnerValue(p.value || [])
+ // extra update after value update
+ setTimeout(() => {
+ pickerRef.current?._updateExtra()
+ })
+ }
+ }, [p.value])
- // tslint:disable-next-line:variable-name
- const _locale = getComponentLocale(
- this.props,
- (this as any).context,
- 'Picker',
- () => require('./locale/zh_CN'),
- )
+ return (
+
+ )
+})
- const { cascader, popupMoreProps }: { cascader: any; popupMoreProps: {} } =
- this.getCascade(
- cascade,
- data,
- cols,
- itemStyle,
- indicatorStyle,
- numberOfLines,
- )
- return (
-
- {(styles) => (
-
- {children &&
- typeof children !== 'string' &&
- React.isValidElement(children) &&
- React.cloneElement
- )}
-
- )
- }
+Picker.displayName = 'AntmPicker'
- getCascade = (
- cascade: boolean | undefined,
- data: PickerData[] | PickerData[][],
- cols: number | undefined,
- itemStyle: any,
- indicatorStyle: any,
- numberOfLines: number | undefined,
- ) => {
- let cascader: React.ReactNode
- let popupMoreProps = {}
- if (cascade) {
- cascader = (
-
- )
- } else {
- cascader = (
-
- {this.getPickerCol()}
-
- )
- popupMoreProps = {
- pickerValueProp: 'selectedValue',
- pickerValueChangeProp: 'onValueChange',
- }
- }
- return { cascader, popupMoreProps }
- }
-}
+export default Picker
diff --git a/components/picker/index.zh-CN.md b/components/picker/index.zh-CN.md
index 18dfdeccf..2f1793e07 100644
--- a/components/picker/index.zh-CN.md
+++ b/components/picker/index.zh-CN.md
@@ -13,25 +13,83 @@ subtitle: 选择器
## API
+### 属性
+
+```ts
+type PickerColumnItem = {
+ label: string | ReactNode
+ value: string | number
+ key?: string | number
+ children?: PickerColumnItem[]
+}
+
+type PickerColumn = PickerColumnItem[]
+
+type PickerValue = string | number
+
+type PickerValueExtend = {
+ columns: PickerColumn[]
+ items: (PickerColumnItem | undefined)[]
+}
+```
+
属性 | 说明 | 类型 | 默认值
----|-----|------|------
-| data | 数据源 | `Array<{value, label, children: Array}>` | - |
-| value | 值, 格式是`[value1, value2, value3]`, 对应数据源的相应级层value | Array | - |
-| format | 格式化选中值的函数 | (labels: string[]): any | `(labels) => { return labels.join(','); } ` |
-| cols | 列数 | Number | `3` |
-| onChange | 选中后的回调,可使用[rc-form](https://github.com/react-component/form) | (val): void | - |
-| onPickerChange | 每列数据选择变化后的回调函数 | (val): void | - |
-| onVisibleChange | 当显隐状态变化时回调函数 | (visible: bool): void | - |
-| itemStyle | 每列样式 | Object | - |
-| numberOfLines | 允许每列显示行数 | Number | 1 |
-| indicatorStyle | indicator 样式 | Object | - |
-| children| 通常是 `List.Item` | Object | `List.Item` |
-| okText | 选中的文案 | String | `确定` |
+| data | 数据源 | `PickerColumn` / `PickerColumn[]` | - |
+| value | 选中项 | `PickerValue[]` | - |
+| defaultValue | 默认选中项 | `PickerValue[]` | - |
+| cascade | 是否级联。
子级来自`data`参数内的`children` | Boolean | `true` |
+| cols | 列数 | Number | `3` |
+| onChange | 选中后的回调,可使用[rc-form](https://github.com/react-component/form) | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
+| onPickerChange | 每列数据选择变化后的回调函数 | `(value: PickerValue[], index: number) => void` | - |
+| onVisibleChange | 当显隐状态变化时回调函数 | `(visible: bool): void` | - |
+| renderLabel | 自定义渲染每列展示的内容 | `(item: PickerColumnItem, index: number) => ReactNode` | `(item) => item.label` |
+| locale | 国际化,可覆盖全局[LocaleProvider](/components/locale-provider-cn)的配置 | Object: {okText, dismissText, extra} | - |
+| title | 大标题 | ReactNode | - |
+| okText | 选中的文案 | String | `确定` |
| dismissText | 取消选中的文案 | String | `取消` |
-| onOk | 点击选中时执行的回调 | (val): void | 无 |
-| onDismiss | 点击取消时执行的回调 | (): void | 无 |
-| title | 大标题 | String | - |
-| extra | Picker children 建议是 `List.Item`, 如果不是,需要是自定义组件(组件内需处理`onClick`/`extra`属性) | String | `请选择` |
-| disabled | 是否不可用 | Boolean | false |
-| cascade | 是否联动 | Boolean | true |
+| onOk | 点击选中时执行的回调 | `(value: PickerValue[], extend: PickerValueExtend) => void` | - |
+| onDismiss | 点击取消时执行的回调 | (): void | - |
+| visible | 是否显示选择器 | Boolean | - |
+| loading | 是否处于加载状态 | Boolean | - |
+| loadingContent | 加载状态下展示的内容 | ReactNode | - |
+| indicatorStyle | 默认Indicator的样式 | Object | - |
+
+### 自定义样式
+
+属性 | 说明 | 类型 | 默认值
+----|-----|------|------
+| style | 外部样式 | `StyleProp` | - |
+| styles | 内部组件样式集 | `PickerViewStyle` | - |
+| itemStyle| 每列样式 | `StyleProp` | - |
+| itemHeight | 每列固定高度,未设值时会根据`numberOfLines`动态计算;`itemStyle`属性设置`{height}`值是无效的 | Number | - |
+| numberOfLines | 允许每列显示行数 | Number | `1` |
+
+#### 遮罩层
+还支持自定义遮罩样式,如使用渐变组件``。当前默认为白色半透明。
+
+属性 | 说明 | 类型 | 默认值
+----|-----|------|------
+| renderMaskTop | 自定义渲染上半部分遮罩层 | `()=> ReactNode` | `` |
+| renderMaskBottom | 自定义渲染下半部分遮罩层 | `()=> ReactNode` | `` |
+
+### Children
+通常是 [List.Item](/components/list-cn/#List.Item) ,以下属性也是围绕着`List.Item`展开:
+
+属性 | 说明 | 类型 | 默认值
+----|-----|------|------
+| children| Picker占位组件,通常是`List.Item` | ReactNode | `List.Item` |
+| extra | Picker children的`extra`属性,无选中项时展示 | String | `请选择` |
+| format | 格式化选中值的函数,用于回显在`extra`属性上 | (labels: string[]): any | `(labels) => { return labels.join(','); } ` |
+| triggerType | 按钮事件名称 | String | `onPress` |
+| disabled | 是否不可用 | Boolean | `false` |
+
+### PickerActions
+属性 | 说明 | 类型
+----|-----|------
+| close |关闭 Picker|`() => void`|
+| open |显示 Picker|`() => void`|
+| toggle|切换 Picker 的显示和隐藏状态|`() => void`|
+### Ref
+同 PickerActions
diff --git a/components/picker/locale/id_ID.tsx b/components/picker/locale/id_ID.tsx
new file mode 100644
index 000000000..db99d7ea2
--- /dev/null
+++ b/components/picker/locale/id_ID.tsx
@@ -0,0 +1,5 @@
+export default {
+ okText: 'Ok',
+ dismissText: 'Cancel',
+ extra: 'please select',
+}
diff --git a/components/picker/locale/ko_KR.tsx b/components/picker/locale/ko_KR.tsx
new file mode 100644
index 000000000..21c6edf98
--- /dev/null
+++ b/components/picker/locale/ko_KR.tsx
@@ -0,0 +1,5 @@
+export default {
+ okText: '확인',
+ dismissText: '취소',
+ extra: '선택해주세요',
+}
diff --git a/components/search-bar/locale/ko_KR.tsx b/components/search-bar/locale/ko_KR.tsx
new file mode 100644
index 000000000..771b2f8e1
--- /dev/null
+++ b/components/search-bar/locale/ko_KR.tsx
@@ -0,0 +1,3 @@
+export default {
+ cancelText: '취소',
+}
diff --git a/components/style/index.tsx b/components/style/index.tsx
index 1a8523bb3..2676cbec1 100644
--- a/components/style/index.tsx
+++ b/components/style/index.tsx
@@ -17,9 +17,33 @@ export const ThemeProvider = (props: ThemeProviderProps) => {
export interface UseThemeContextProps {
theme?: PartialTheme
}
-export const useTheme = (props: UseThemeContextProps = {}) => {
+export const useTheme = (props: any) => {
const theme = React.useContext(ThemeContext)
- return { ...theme, ...props.theme }
+ const { themeStyles, styles } = props
+
+ const stylesRef = React.useRef(undefined)
+ const cache = React.useRef(undefined)
+
+ const getStyles = React.useCallback(() => {
+ if (themeStyles && cache.current === undefined) {
+ cache.current = themeStyles(theme)
+ }
+
+ // TODO: check these styles has changed
+ if (styles && !shallowequal(stylesRef.current, styles)) {
+ stylesRef.current = styles
+ // merge styles from user defined
+ styles &&
+ Object.keys(styles).forEach((key) => {
+ if (cache.current[key]) {
+ cache.current[key] = [cache.current[key], styles[key]]
+ }
+ })
+ }
+
+ return cache.current || {}
+ }, [theme, themeStyles, styles])
+ return getStyles()
}
export interface WithThemeProps {
diff --git a/components/switch/PropsType.tsx b/components/switch/PropsType.tsx
index 1709ea1a7..c75252e3f 100644
--- a/components/switch/PropsType.tsx
+++ b/components/switch/PropsType.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-unused-vars */
import React from 'react'
import { ColorValue } from 'react-native'
@@ -15,6 +14,9 @@ export interface SwitchPropsType {
title?: string
checkedChildren?: string | React.ReactNode
unCheckedChildren?: string | React.ReactNode
- onChange?: (checked: boolean) => void
+ onChange?: (checked: boolean) => void | Promise
+ /**
+ * @deprecated
+ */
onPress?: (checked: boolean) => void
}
diff --git a/components/switch/Switch.tsx b/components/switch/Switch.tsx
index 88bf4053e..d88f844bd 100644
--- a/components/switch/Switch.tsx
+++ b/components/switch/Switch.tsx
@@ -1,13 +1,20 @@
import classNames from 'classnames'
import useMergedState from 'rc-util/lib/hooks/useMergedState'
import * as React from 'react'
-import { Animated, Easing, StyleProp, View, ViewStyle } from 'react-native'
+import {
+ Animated,
+ Easing,
+ Pressable,
+ StyleProp,
+ View,
+ ViewStyle,
+} from 'react-native'
+import devWarning from '../_util/devWarning'
+import { useAnimatedTiming } from '../_util/hooks/useAnimations'
+import { isPromise } from '../_util/isPromise'
import RNActivityIndicator from '../activity-indicator'
-import ButtonWave from '../button/ButtonWave'
import { WithTheme, WithThemeStyles } from '../style'
import AntmView from '../view/index'
-import devWarning from '../_util/devWarning'
-import { useAnimatedTiming } from '../_util/hooks/useAnimations'
import { SwitchPropsType } from './PropsType'
import SwitchStyles, { SwitchStyle } from './style/index'
@@ -21,6 +28,7 @@ export interface SwitchProps
const AnimatedView = Animated.createAnimatedComponent(AntmView)
const AntmSwitch = ({
prefixCls = 'switch',
+ style,
checked,
defaultChecked,
disabled,
@@ -40,29 +48,38 @@ const AntmSwitch = ({
'Switch',
'`value` is not a valid prop, do you mean `checked`?',
)
- // Compatible with old code : checked without onChange was alse onControlled
- const checkedRef = React.useRef()
- if (checkedRef.current === undefined) {
- checkedRef.current = checked ?? defaultChecked
- }
const [innerChecked, setInnerChecked] = useMergedState(false, {
- value: checkedRef.current,
+ value: checked,
defaultValue: defaultChecked,
+ onChange: onChange,
})
+ const [innerLoading, setInnerLoading] = useMergedState(false, {
+ value: loading,
+ })
+
+ const PADDING = 11 // switch旁白最低宽度
+ const TRACK_PADDING = 5 // switch轨道按压变形宽度
+ const BORDER_WIDTH = 2 // switch轨道边框宽度
+
+ // switch height measure
+ const [itemHeight, setHeight] = React.useState(31)
+ const wrapperMeasure = React.useCallback((e) => {
+ setHeight(e.nativeEvent.layout.height)
+ }, [])
//disabled when loading
- disabled = disabled || loading
+ disabled = disabled || innerLoading
// animate1
const [animatedValue, animate] = useAnimatedTiming()
const transitionMargin = {
marginLeft: animatedValue.interpolate({
inputRange: [0, 1],
- outputRange: [25, 7],
+ outputRange: [itemHeight - BORDER_WIDTH, BORDER_WIDTH],
}),
marginRight: animatedValue.interpolate({
inputRange: [0, 1],
- outputRange: [7, 25],
+ outputRange: [BORDER_WIDTH, itemHeight - BORDER_WIDTH],
}),
}
@@ -71,48 +88,59 @@ const AntmSwitch = ({
const transitionWidth = {
width: animatedValue2.interpolate({
inputRange: [0, 1],
- outputRange: [22, 28],
+ outputRange: [
+ itemHeight - BORDER_WIDTH * 2,
+ itemHeight - BORDER_WIDTH * 2 + TRACK_PADDING,
+ ],
}),
left: !innerChecked
? animatedValue.interpolate({
inputRange: [0, 1],
- outputRange: [0, 10],
+ outputRange: [BORDER_WIDTH, PADDING],
})
: undefined,
right: innerChecked
? animatedValue.interpolate({
inputRange: [0, 1],
- outputRange: [10, 0],
+ outputRange: [PADDING, BORDER_WIDTH],
})
: 0,
}
//initial animate
React.useEffect(() => {
- if (checkedRef.current) {
+ if (innerChecked) {
animate({})
animate2({ toValue: 0 })
} else {
animate({ toValue: 0 })
}
- }, [animate, animate2, checkedRef])
+ }, [animate, animate2, innerChecked, itemHeight])
- function triggerChange(newChecked: boolean) {
+ async function triggerChange(newChecked: boolean) {
if (!disabled) {
- checkedRef.current = newChecked
setInnerChecked(newChecked)
- onChange?.(newChecked)
+ const result = onChange?.(newChecked)
+ if (isPromise(result)) {
+ setInnerLoading(true)
+ try {
+ await result
+ setInnerLoading(false)
+ } catch (e) {
+ setInnerLoading(false)
+ throw e
+ }
+ }
return newChecked
}
return innerChecked
}
- function onInternalClick() {
- const ret = triggerChange(!innerChecked)
+ async function onInternalClick() {
+ const ret = await triggerChange(!innerChecked)
// [Legacy] trigger onClick with value
onPress?.(ret)
- animate({ toValue: ret ? 1 : 0 })
}
function onPressIn() {
@@ -134,6 +162,7 @@ const AntmSwitch = ({
})
.split(' ')
.map((a) => styles[a])
+ .concat([style])
const ant_switch_inner = classNames(`${prefixCls}_inner`, {
[`${prefixCls}_inner_checked`]: innerChecked,
@@ -149,6 +178,13 @@ const AntmSwitch = ({
})
.split(' ')
.map((a) => styles[a])
+ .concat([
+ {
+ width: itemHeight - BORDER_WIDTH * 2,
+ height: itemHeight - BORDER_WIDTH * 2,
+ borderRadius: itemHeight - BORDER_WIDTH * 2,
+ },
+ ])
// color props
const Color = innerChecked
@@ -170,51 +206,44 @@ const AntmSwitch = ({
const accessibilityState = {
checked: innerChecked,
disabled,
- busy: loading,
+ busy: innerLoading,
}
return (
-
-
-
-
- {loading && (
-
- )}
-
-
- {innerChecked ? checkedChildren : unCheckedChildren}
-
-
-
-
+ {...restProps}
+ disabled={disabled}
+ onPressIn={onPressIn}
+ onPressOut={onPressOut}
+ onPress={onInternalClick}>
+
+
+ {innerLoading && (
+
+ )}
+
+
+ {innerChecked ? checkedChildren : unCheckedChildren}
+
+
+
)
}}
diff --git a/components/switch/__tests__/__snapshots__/demo.test.js.snap b/components/switch/__tests__/__snapshots__/demo.test.js.snap
index fc8c9bfde..6d6f09cc5 100644
--- a/components/switch/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/switch/__tests__/__snapshots__/demo.test.js.snap
@@ -101,103 +101,90 @@ exports[`renders ./components/switch/demo/basic.tsx correctly 1`] = `
accessibilityRole="switch"
accessibilityState={
Object {
- "busy": undefined,
+ "busy": false,
"checked": false,
- "disabled": undefined,
+ "disabled": false,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
-
-
+
-
+ }
+ />
@@ -319,107 +306,94 @@ exports[`renders ./components/switch/demo/basic.tsx correctly 1`] = `
accessibilityRole="switch"
accessibilityState={
Object {
- "busy": undefined,
+ "busy": false,
"checked": false,
"disabled": true,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
-
-
+
-
+ }
+ />
@@ -626,105 +600,92 @@ exports[`renders ./components/switch/demo/basic.tsx correctly 1`] = `
accessibilityRole="switch"
accessibilityState={
Object {
- "busy": undefined,
+ "busy": false,
"checked": true,
- "disabled": undefined,
+ "disabled": false,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
-
-
+
- 开启
-
-
+ }
+ >
+ 开
+
@@ -800,105 +761,92 @@ exports[`renders ./components/switch/demo/basic.tsx correctly 1`] = `
accessibilityRole="switch"
accessibilityState={
Object {
- "busy": undefined,
+ "busy": false,
"checked": false,
- "disabled": undefined,
+ "disabled": false,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
-
-
+
- 0
-
-
+ }
+ >
+ 0
+
@@ -974,119 +922,106 @@ exports[`renders ./components/switch/demo/basic.tsx correctly 1`] = `
accessibilityRole="switch"
accessibilityState={
Object {
- "busy": undefined,
+ "busy": false,
"checked": true,
- "disabled": undefined,
+ "disabled": false,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
-
+
-
+
-
-
-
-
+
+
@@ -1214,125 +1149,111 @@ exports[`renders ./components/switch/demo/basic.tsx correctly 1`] = `
"disabled": true,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
+
-
-
-
-
+
-
+
@@ -1413,125 +1334,111 @@ exports[`renders ./components/switch/demo/basic.tsx correctly 1`] = `
"disabled": true,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
+
-
-
-
-
+
-
+
@@ -1653,107 +1560,299 @@ exports[`renders ./components/switch/demo/basic.tsx correctly 1`] = `
accessibilityRole="switch"
accessibilityState={
Object {
- "busy": undefined,
+ "busy": false,
"checked": true,
- "disabled": undefined,
+ "disabled": false,
}
}
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 异步
+
+
+
+
+
+
+
-
-
+
+
+
-
+
-
+
-
+ }
+ />
diff --git a/components/switch/demo/basic.tsx b/components/switch/demo/basic.tsx
index a3d1b1b88..c803266d9 100644
--- a/components/switch/demo/basic.tsx
+++ b/components/switch/demo/basic.tsx
@@ -7,6 +7,7 @@ export default class SwitchExample extends React.Component {
super(props)
this.state = {
disabled: true,
+ checked: false,
}
}
@@ -15,6 +16,20 @@ export default class SwitchExample extends React.Component {
disabled: !this.state.disabled,
})
}
+
+ sleep1s = () => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, 1000)
+ })
+ }
+
+ onChangeAsync = async (val: boolean) => {
+ await this.sleep1s()
+ this.setState({
+ checked: val,
+ })
+ }
+
render() {
return (
@@ -36,8 +51,8 @@ export default class SwitchExample extends React.Component {
}
@@ -66,6 +81,17 @@ export default class SwitchExample extends React.Component {
color="red"
+
+
+ }>
+ onChange 返回 Promise
+
+
)
}
diff --git a/components/switch/index.en-US.md b/components/switch/index.en-US.md
index 10c96be68..29e1833a6 100644
--- a/components/switch/index.en-US.md
+++ b/components/switch/index.en-US.md
@@ -7,14 +7,17 @@ title: Switch
Select between two status, e.g. Select On or Off.
### Rules
-- Used in `List` only.
-- There is no need to add extra text to describe the value of `Switch` .
+- This is a **controlled component** that requires an `onChange` callback that updates the `checked` prop in order for the component to reflect user actions.
## API
Properties | Descrition | Type | Default
-----------|------------|------|--------
| checked | Whether is checked by default | Boolean | false |
+| defaultChecked | Whether to open initially | Boolean | false |
| disabled | whether is disabled | Boolean | false |
+| loading | Loading status | Boolean | false |
+| onChange | The callback function when changing, when the Promise is returned, the loading status will be displayed automatically | `(val: boolean) => void \| Promise` | - |
| color | Background color when the switch is turned on. | String | #4dd865 |
-| onChange | The callback function that is triggered when the selected state changes. | (checked: bool): void | - |
+| checkedChildren | Selected content | ReactNode | - |
+| unCheckedChildren | Non-selected content | ReactNode | - |
\ No newline at end of file
diff --git a/components/switch/index.zh-CN.md b/components/switch/index.zh-CN.md
index d6327d0a6..e4997856b 100644
--- a/components/switch/index.zh-CN.md
+++ b/components/switch/index.zh-CN.md
@@ -8,18 +8,17 @@ subtitle: 滑动开关
在两个互斥对象进行选择,eg:选择开或关。
### 规则
-- 只在 List 中使用。
-- 避免增加额外的文案来描述当前 Switch 的值。
+- 这是一个“受控组件”。你必须使用`onChange`回调来更新`checked`属性以响应用户的操作。
## API
属性 | 说明 | 类型 | 默认值
----|-----|------|------
| checked | 是否默认选中 | Boolean | false |
-| checkedChildren | 选中时的内容 | String \| ReactNode | 无 |
+| defaultChecked | 初始是否打开 | Boolean | false |
| disabled | 是否不可修改 | Boolean | false |
-| loading | 加载中的开关
-| unCheckedChildren | 非选中时的内容 | String \| ReactNode | 无 |
-| onChange | change 事件触发的回调函数 | (checked: bool): void | 无 |
-| color | 开关打开后的颜色 | String | #4dd865 |
-| onPress | click事件触发的回调函数,当switch为disabled时,入参的值始终是默认传入的checked值。 | (checked: bool): void | 无 |
+| loading | 加载中的开关 | Boolean | false |
+| onChange | 变化时的回调函数,当返回 Promise 时,会自动显示加载状态 | `(val: boolean) => void \| Promise` | 无 |
+| color | 开关打开后的颜色 | String | `#4dd865` |
+| checkedChildren | 选中时的内容 | ReactNode | 无 |
+| unCheckedChildren | 非选中时的内容 | ReactNode | 无 |
\ No newline at end of file
diff --git a/components/switch/style/index.tsx b/components/switch/style/index.tsx
index 3f95edaee..923b55805 100644
--- a/components/switch/style/index.tsx
+++ b/components/switch/style/index.tsx
@@ -20,36 +20,48 @@ export default (theme: Theme) =>
StyleSheet.create({
switch: {
position: 'relative',
- minWidth: 50,
- minHeight: 25,
+ width: 55,
+ height: 31,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
padding: 0,
- borderRadius: 20,
- borderWidth: 1,
- borderColor: 'transparent',
- overflow: 'hidden',
+ borderRadius: 31,
},
// handle
switch_handle: {
position: 'absolute',
- width: 22,
- height: 22,
- borderRadius: 999,
+ width: 27,
+ height: 27,
+ borderRadius: 27,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
backgroundColor: '#ffffff',
+ shadowColor: 'rgb(0, 35, 11)',
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.2,
+ shadowRadius: 10,
+ elevation: 10,
},
// inner
switch_inner: {
color: '#fff',
fontSize: 12,
+ flex: 1,
+ textAlign: 'center',
+ alignItems: 'center',
+ justifyContent: 'center',
},
switch_inner_checked: {
marginLeft: 7,
- marginRight: 25,
+ marginRight: 27,
},
switch_inner_unchecked: {
- marginLeft: 25,
+ marginLeft: 27,
marginRight: 7,
},
// checked
diff --git a/components/toast/__tests__/__snapshots__/demo.test.js.snap b/components/toast/__tests__/__snapshots__/demo.test.js.snap
index 76c790638..1af00ead5 100644
--- a/components/toast/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/toast/__tests__/__snapshots__/demo.test.js.snap
@@ -96,103 +96,90 @@ exports[`renders ./components/toast/demo/basic.tsx correctly 1`] = `
accessibilityRole="switch"
accessibilityState={
Object {
- "busy": undefined,
+ "busy": false,
"checked": true,
- "disabled": undefined,
+ "disabled": false,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
-
-
+
-
+ }
+ />
@@ -270,103 +257,90 @@ exports[`renders ./components/toast/demo/basic.tsx correctly 1`] = `
accessibilityRole="switch"
accessibilityState={
Object {
- "busy": undefined,
+ "busy": false,
"checked": true,
- "disabled": undefined,
+ "disabled": false,
}
}
- style={
- Array [
- Object {
- "alignItems": "center",
- "borderColor": "transparent",
- "borderRadius": 20,
- "borderWidth": 1,
- "display": "flex",
- "flexDirection": "row",
- "minHeight": 25,
- "minWidth": 50,
- "overflow": "hidden",
- "padding": 0,
- "position": "relative",
- },
- Object {
- "padding": 1,
- },
- ]
- }
+ accessible={true}
+ collapsable={false}
+ focusable={true}
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onResponderGrant={[Function]}
+ onResponderMove={[Function]}
+ onResponderRelease={[Function]}
+ onResponderTerminate={[Function]}
+ onResponderTerminationRequest={[Function]}
+ onStartShouldSetResponder={[Function]}
>
-
-
+
-
+ }
+ />
diff --git a/components/view/index.tsx b/components/view/index.tsx
index 237ae5305..972e33bc3 100644
--- a/components/view/index.tsx
+++ b/components/view/index.tsx
@@ -43,6 +43,22 @@ class AntmView extends React.PureComponent {
}
}
+ if (
+ React.isValidElement(children) &&
+ String(children.type) === 'Symbol(react.fragment)'
+ ) {
+ return (
+
+ {React.Children.map(children.props.children, (child) => {
+ if (React.isValidElement(child)) {
+ return child
+ }
+ return {child}
+ })}
+
+ )
+ }
+
return
}
}
diff --git a/docs/react/upgrade-notes.en-US.md b/docs/react/upgrade-notes.en-US.md
index 7f22a0853..c7bf502a2 100644
--- a/docs/react/upgrade-notes.en-US.md
+++ b/docs/react/upgrade-notes.en-US.md
@@ -5,6 +5,26 @@ title: Upgrade
Here list some of main incompatible changes and recommended changes in the upgrade. See [Changelog](/changelog) for all changes.
+### 5.1.0
+
+> 安装 peer 依赖
+
+```bash
+npm install @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
+```
+
+or
+
+```bash
+yarn add @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
+```
+
+> On the root of the Project, the `App.js/ App.tsx` file probably need import
+
+```js
+import 'react-native-gesture-handler';
+```
+
### 5.0.3
> Installing peer dependencies
diff --git a/docs/react/upgrade-notes.zh-CN.md b/docs/react/upgrade-notes.zh-CN.md
index 9b428be56..1a9711e68 100644
--- a/docs/react/upgrade-notes.zh-CN.md
+++ b/docs/react/upgrade-notes.zh-CN.md
@@ -5,6 +5,26 @@ title: 升级指南
此处着重列出升级中的不兼容变化和推荐改动。所有变动请见 [Changelog](/changelog)。
+### 5.1.0
+
+> 安装 peer 依赖
+
+```bash
+npm install @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
+```
+
+or
+
+```bash
+yarn add @react-native-community/segmented-control @react-native-community/slider react-native-gesture-handler
+```
+
+> 在项目的根目录下,入口文件(通常是App.js)文件中需要引入这句:
+
+```js
+import 'react-native-gesture-handler';
+```
+
### 5.0.3
> 安装 peer 依赖
diff --git a/example/.nvmrc b/example/.nvmrc
new file mode 100644
index 000000000..25bf17fc5
--- /dev/null
+++ b/example/.nvmrc
@@ -0,0 +1 @@
+18
\ No newline at end of file
diff --git a/example/App.js b/example/App.js
index 6d1b5a66f..67af603bf 100644
--- a/example/App.js
+++ b/example/App.js
@@ -3,6 +3,7 @@ import { useFonts } from 'expo-font'
import * as SplashScreen from 'expo-splash-screen'
import React, { useCallback } from 'react'
import { View } from 'react-native'
+import 'react-native-gesture-handler'
SplashScreen.preventAutoHideAsync()
diff --git a/example/app.json b/example/app.json
index 830b4857f..656ad6ec9 100644
--- a/example/app.json
+++ b/example/app.json
@@ -5,7 +5,7 @@
"name": "@ant-design/react-native",
"slug": "ant-design-mobile-rn",
"description": "基于蚂蚁金服移动设计规范的 React Native 组件库",
- "sdkVersion": "47.0.0",
+ "sdkVersion": "49.0.0",
"icon": "../rn-kitchen-sink/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png",
"splash": {
"image": "../rn-kitchen-sink/ios/KitchenSink/Images.xcassets/AppIcon.appiconset/ios-marketing-1024@1x.png"
diff --git a/example/index.js b/example/index.js
index 40906434b..a37ba27c6 100644
--- a/example/index.js
+++ b/example/index.js
@@ -1,3 +1,4 @@
+import '@expo/metro-runtime'
import { registerRootComponent } from 'expo'
import App from './App'
diff --git a/example/package.json b/example/package.json
index 837c796b9..4183ff649 100644
--- a/example/package.json
+++ b/example/package.json
@@ -19,16 +19,18 @@
"dependencies": {
"@ant-design/icons-react-native": "^2.3.2",
"@ant-design/react-native": "*",
- "expo": "^47.0.0",
+ "expo": "49",
"expo-font": "~11.0.1",
"expo-splash-screen": "~0.17.4",
"expo-updates": "~0.15.6",
"react": "18.1.0",
"react-dom": "18.1.0",
- "react-native": "0.70.5",
+ "react-native": "0.70.8",
+ "react-native-gesture-handler": "~2.12.0",
"react-native-reanimated": "~2.12.0"
},
"devDependencies": {
- "babel-preset-expo": "9.0.2"
+ "@expo/metro-runtime": "^2.2.15",
+ "babel-preset-expo": "~9.2.1"
}
}
diff --git a/example/tsconfig.json b/example/tsconfig.json
new file mode 100644
index 000000000..0e6371f6f
--- /dev/null
+++ b/example/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "compilerOptions": {},
+ "extends": "expo/tsconfig.base"
+}
diff --git a/package.json b/package.json
index d04e92ff8..637544b3a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@ant-design/react-native",
- "version": "5.0.5",
+ "version": "5.1.0",
"description": "基于蚂蚁金服移动设计规范的 React Native 组件库",
"keywords": [
"ant",
@@ -34,13 +34,15 @@
"@ant-design/icons-react-native": "^2.3.1",
"@bang88/react-native-ultimate-listview": "^4.1.0",
"@types/shallowequal": "^1.1.1",
- "array-tree-filter": "~2.1.0",
"babel-runtime": "^6.x",
"classnames": "^2.2.1",
+ "dayjs": "^1.11.7",
+ "lodash.assignwith": "^4.2.0",
"normalize-css-color": "^1.0.2",
"rc-util": "^4.21.1",
"react-native-codegen": "^0.0.7",
"react-native-collapsible": "^1.6.0",
+ "react-native-gesture-handler": "~2.2.1",
"react-native-modal-popover": "^2.0.1",
"shallowequal": "^1.1.0",
"utility-types": "^3.10.0"
@@ -49,12 +51,9 @@
"@ant-design/tools": "^13.4.1-beta.0",
"@babel/core": "^7.12.9",
"@babel/runtime": "^7.12.5",
- "@react-native-camera-roll/camera-roll": "^5.1.0",
"@react-native-community/eslint-config": "^2.0.0",
- "@react-native-community/masked-view": "^0.1.9",
"@react-native-community/segmented-control": "^2.2.2",
"@react-native-community/slider": "^3.0.3",
- "@react-native-picker/picker": "^2.4.8",
"@react-navigation/native": "^6.1.1",
"@react-navigation/stack": "^6.3.10",
"@testing-library/jest-native": "^4.0.1",
@@ -88,7 +87,6 @@
"react-github-button": "^0.1.9",
"react-intl": "^2.2.3",
"react-native": "0.64.2",
- "react-native-gesture-handler": "~2.2.1",
"react-native-mocker": "^0.0.12",
"react-native-reanimated": "^2.2.0",
"react-native-safe-area-context": "4.2.4",
@@ -101,13 +99,11 @@
"typescript": "^4.3.2"
},
"peerDependencies": {
- "react": ">=17.0.1",
- "react-native": ">=0.64.1",
- "@react-native-camera-roll/camera-roll": ">= 5.0.0",
"@react-native-community/segmented-control": ">= 1.4.0",
"@react-native-community/slider": ">= 2.0.0",
- "@react-native-picker/picker": "^2.4.8",
- "react-native-gesture-handler": "^2.2.1"
+ "react": ">=17.0.1",
+ "react-native": ">=0.64.1",
+ "react-native-gesture-handler": ">=2.2.1"
},
"scripts": {
"lint": "npm run tslint && npm run srclint && npm run applint",
diff --git a/rn-kitchen-sink/App.js b/rn-kitchen-sink/App.js
index f80ef4d14..2fb856a9d 100644
--- a/rn-kitchen-sink/App.js
+++ b/rn-kitchen-sink/App.js
@@ -4,8 +4,8 @@ import React from 'react'
import { AppRegistry } from 'react-native'
import 'react-native-gesture-handler'
import Provider from '../components/provider'
-import RnIndex from './components/index'
import Theme from './components/Theme'
+import RnIndex from './components/index'
import { OTHERS, UIBARS, UICONTROLS, UIVIEWS } from './demoList'
const getOptions = (title) => ({
@@ -78,9 +78,11 @@ AppRegistry.registerComponent('KitchenSink', () => App)
export default App
// global catch error to avoid crash
-global.ErrorUtils?.setGlobalHandler((e, isFatal) => {
- if (isFatal) {
- // eslint-disable-next-line no-alert
- alert(`${e.name}: ${e.message}`)
- }
-})
+if (!__DEV__) {
+ global.ErrorUtils?.setGlobalHandler((e, isFatal) => {
+ if (isFatal) {
+ // eslint-disable-next-line no-alert
+ alert(`${e.name}: ${e.message}`)
+ }
+ })
+}
diff --git a/rn-kitchen-sink/demoList.js b/rn-kitchen-sink/demoList.js
index ac268fb56..796aa7c99 100644
--- a/rn-kitchen-sink/demoList.js
+++ b/rn-kitchen-sink/demoList.js
@@ -137,12 +137,6 @@ module.exports = {
icon: 'https://os.alipayobjects.com/rmsportal/IQtMSWmYwLEuqln.png',
module: require('../components/date-picker-view/demo/basic'),
},
- {
- title: 'ImagePicker',
- description: '图片选择',
- icon: 'https://os.alipayobjects.com/rmsportal/NDsSvklLUeodsHK.png',
- module: require('../components/image-picker/demo/basic'),
- },
{
title: 'InputItem',
description: '文本输入',
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index 8f627242c..7ceff5e7a 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -16,7 +16,6 @@ Array [
"Flex",
"Grid",
"Icon",
- "ImagePicker",
"InputItem",
"ListView",
"List",
diff --git a/yarn.lock b/yarn.lock
index 4352582b3..ec492db04 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -116,13 +116,6 @@
dependencies:
"@babel/highlight" "^7.12.13"
-"@babel/code-frame@~7.10.4":
- version "7.10.4"
- resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
- integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
- dependencies:
- "@babel/highlight" "^7.10.4"
-
"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.4":
version "7.14.4"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.4.tgz#45720fe0cecf3fd42019e1d12cc3d27fadc98d58"
@@ -1220,72 +1213,6 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
-"@expo/config-plugins@~5.0.3":
- version "5.0.4"
- resolved "https://registry.npmmirror.com/@expo/config-plugins/-/config-plugins-5.0.4.tgz#216fea6558fe66615af1370de55193f4181cb23e"
- integrity sha512-vzUcVpqOMs3h+hyRdhGwk+eGIOhXa5xYdd92yO17RMNHav3v/+ekMbs7XA2c3lepMO8Yd4/5hqmRw9ZTL6jGzg==
- dependencies:
- "@expo/config-types" "^47.0.0"
- "@expo/json-file" "8.2.36"
- "@expo/plist" "0.0.18"
- "@expo/sdk-runtime-versions" "^1.0.0"
- "@react-native/normalize-color" "^2.0.0"
- chalk "^4.1.2"
- debug "^4.3.1"
- find-up "~5.0.0"
- getenv "^1.0.0"
- glob "7.1.6"
- resolve-from "^5.0.0"
- semver "^7.3.5"
- slash "^3.0.0"
- xcode "^3.0.1"
- xml2js "0.4.23"
-
-"@expo/config-types@^47.0.0":
- version "47.0.0"
- resolved "https://registry.npmmirror.com/@expo/config-types/-/config-types-47.0.0.tgz#99eeabe0bba7a776e0f252b78beb0c574692c38d"
- integrity sha512-r0pWfuhkv7KIcXMUiNACJmJKKwlTBGMw9VZHNdppS8/0Nve8HZMTkNRFQzTHW1uH3pBj8jEXpyw/2vSWDHex9g==
-
-"@expo/config@~7.0.0":
- version "7.0.3"
- resolved "https://registry.npmmirror.com/@expo/config/-/config-7.0.3.tgz#c9c634e76186de25e296485e51418f1e52966e6e"
- integrity sha512-joVtB5o+NF40Tmsdp65UzryRtbnCuMbXkVO4wJnNJO4aaK0EYLdHCYSewORVqNcDfGN0LphQr8VTG2npbd9CJA==
- dependencies:
- "@babel/code-frame" "~7.10.4"
- "@expo/config-plugins" "~5.0.3"
- "@expo/config-types" "^47.0.0"
- "@expo/json-file" "8.2.36"
- getenv "^1.0.0"
- glob "7.1.6"
- require-from-string "^2.0.2"
- resolve-from "^5.0.0"
- semver "7.3.2"
- slugify "^1.3.4"
- sucrase "^3.20.0"
-
-"@expo/json-file@8.2.36":
- version "8.2.36"
- resolved "https://registry.npmmirror.com/@expo/json-file/-/json-file-8.2.36.tgz#62a505cb7f30a34d097386476794680a3f7385ff"
- integrity sha512-tOZfTiIFA5KmMpdW9KF7bc6CFiGjb0xnbieJhTGlHrLL+ps2G0OkqmuZ3pFEXBOMnJYUVpnSy++52LFxvpa5ZQ==
- dependencies:
- "@babel/code-frame" "~7.10.4"
- json5 "^1.0.1"
- write-file-atomic "^2.3.0"
-
-"@expo/plist@0.0.18":
- version "0.0.18"
- resolved "https://registry.npmmirror.com/@expo/plist/-/plist-0.0.18.tgz#9abcde78df703a88f6d9fa1a557ee2f045d178b0"
- integrity sha512-+48gRqUiz65R21CZ/IXa7RNBXgAI/uPSdvJqoN9x1hfL44DNbUoWHgHiEXTx7XelcATpDwNTz6sHLfy0iNqf+w==
- dependencies:
- "@xmldom/xmldom" "~0.7.0"
- base64-js "^1.2.3"
- xmlbuilder "^14.0.0"
-
-"@expo/sdk-runtime-versions@^1.0.0":
- version "1.0.0"
- resolved "https://registry.npmmirror.com/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz#d7ebd21b19f1c6b0395e50d78da4416941c57f7c"
- integrity sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==
-
"@hapi/hoek@^9.0.0":
version "9.2.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131"
@@ -1562,11 +1489,6 @@
mkdirp "^1.0.4"
rimraf "^3.0.2"
-"@react-native-camera-roll/camera-roll@^5.1.0":
- version "5.1.0"
- resolved "https://registry.npmmirror.com/@react-native-camera-roll/camera-roll/-/camera-roll-5.1.0.tgz#5cfb3cf02d72ab03b3d6a0bdda392e2896c8d55f"
- integrity sha512-74pavpt2T2U3V0r5d+pn4NChJbRNcydqakp3NVmosod35Lzxrt9My7kCLDdHXW2S6J6DhgXb/n36/heZQB4AUA==
-
"@react-native-community/cli-debugger-ui@^5.0.1-alpha.1":
version "5.0.1-alpha.1"
resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1-alpha.1.tgz#09a856ccd2954cf16eea59b14dd26ae66720e4e6"
@@ -1715,11 +1637,6 @@
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.1.0.tgz#e42b1bef12d2415411519fd528e64b593b1363dc"
integrity sha512-W/J0fNYVO01tioHjvYWQ9m6RgndVtbElzYozBq1ZPrHO/iCzlqoySHl4gO/fpCl9QEFjvJfjPgtPMTMlsoq5DQ==
-"@react-native-community/masked-view@^0.1.9":
- version "0.1.11"
- resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce"
- integrity sha512-rQfMIGSR/1r/SyN87+VD8xHHzDYeHaJq6elOSCAD+0iLagXkSI2pfA0LmSXP21uw5i3em7GkkRjfJ8wpqWXZNw==
-
"@react-native-community/segmented-control@^2.2.2":
version "2.2.2"
resolved "https://registry.yarnpkg.com/@react-native-community/segmented-control/-/segmented-control-2.2.2.tgz#4014256819ab8f40f6bc3a3929ff14a9d149cf04"
@@ -1730,11 +1647,6 @@
resolved "https://registry.yarnpkg.com/@react-native-community/slider/-/slider-3.0.3.tgz#830167fd757ba70ac638747ba3169b2dbae60330"
integrity sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw==
-"@react-native-picker/picker@^2.4.8":
- version "2.4.8"
- resolved "https://registry.npmmirror.com/@react-native-picker/picker/-/picker-2.4.8.tgz#a1a21f3d6ecadedbc3f0b691a444ddd7baa081f8"
- integrity sha512-5NQ5XPo1B03YNqKFrV6h9L3CQaHlB80wd4ETHUEABRP2iLh7FHLVObX2GfziD+K/VJb8G4KZcZ23NFBFP1f7bg==
-
"@react-native/assets@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e"
@@ -1745,11 +1657,6 @@
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-1.0.0.tgz#c52a99d4fe01049102d47dc45d40cbde4f720ab6"
integrity sha512-xUNRvNmCl3UGCPbbHvfyFMnpvLPoOjDCcp5bT9m2k+TF/ZBklEQwhPZlkrxRx2NhgFh1X3a5uL7mJ7ZR+8G7Qg==
-"@react-native/normalize-color@^2.0.0":
- version "2.1.0"
- resolved "https://registry.npmmirror.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91"
- integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==
-
"@react-native/polyfills@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-1.0.0.tgz#05bb0031533598f9458cf65a502b8df0eecae780"
@@ -2019,11 +1926,6 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
-"@types/qs@^6.5.3":
- version "6.9.7"
- resolved "https://registry.npmmirror.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
- integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
-
"@types/react-native@^0.64.10":
version "0.64.10"
resolved "https://registry.npmjs.org/@types/react-native/-/react-native-0.64.10.tgz#5eb6a72c77ce0f7e6e14b19c61a6bc585975eef5"
@@ -2456,11 +2358,6 @@
"@webassemblyjs/wast-parser" "1.9.0"
"@xtuc/long" "4.2.2"
-"@xmldom/xmldom@~0.7.0":
- version "0.7.9"
- resolved "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.7.9.tgz#7f9278a50e737920e21b297b8a35286e9942c056"
- integrity sha512-yceMpm/xd4W2a85iqZyO09gTnHvXF6pyiWjD2jcOJs7hRoZtNNOO1eJlhHj1ixA+xip2hOyGn+LgcvLCMo5zXA==
-
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@@ -2786,11 +2683,6 @@ any-observable@^0.3.0:
resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b"
integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==
-any-promise@^1.0.0:
- version "1.3.0"
- resolved "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
- integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
-
anymatch@^1.3.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a"
@@ -2965,7 +2857,7 @@ array-tree-filter@^1.0.0:
resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-1.0.1.tgz#0a8ad1eefd38ce88858632f9cc0423d7634e4d5d"
integrity sha1-CorR7v04zoiFhjL5zAQj12NOTV0=
-array-tree-filter@^2.1.0, array-tree-filter@~2.1.0:
+array-tree-filter@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190"
integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==
@@ -3422,7 +3314,7 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.3, base64-js@^1.5.1:
+base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@@ -3452,11 +3344,6 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
-big-integer@1.6.x:
- version "1.6.51"
- resolved "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
- integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
-
big-integer@^1.6.44:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
@@ -3594,13 +3481,6 @@ bplist-creator@0.0.8:
dependencies:
stream-buffers "~2.2.0"
-bplist-creator@0.1.0:
- version "0.1.0"
- resolved "https://registry.npmmirror.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e"
- integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==
- dependencies:
- stream-buffers "2.2.x"
-
bplist-parser@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e"
@@ -3608,13 +3488,6 @@ bplist-parser@0.2.0:
dependencies:
big-integer "^1.6.44"
-bplist-parser@0.3.1:
- version "0.3.1"
- resolved "https://registry.npmmirror.com/bplist-parser/-/bplist-parser-0.3.1.tgz#e1c90b2ca2a9f9474cc72f6862bbf3fee8341fd1"
- integrity sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==
- dependencies:
- big-integer "1.6.x"
-
brace-expansion@^1.0.0, brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -3992,14 +3865,6 @@ chalk@^4.0.0, chalk@^4.1.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-chalk@^4.1.2:
- version "4.1.2"
- resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
- integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
char-regex@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
@@ -4404,7 +4269,7 @@ commander@^2.12.1, commander@^2.14.1, commander@^2.18.0, commander@^2.19.0, comm
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-commander@^4.0.0, commander@^4.0.1:
+commander@^4.0.1:
version "4.1.1"
resolved "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
@@ -5010,6 +4875,11 @@ date-fns@^1.27.2, date-fns@^1.30.1:
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
+dayjs@^1.11.7:
+ version "1.11.10"
+ resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
+ integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
+
dayjs@^1.8.15:
version "1.10.5"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.5.tgz#5600df4548fc2453b3f163ebb2abbe965ccfb986"
@@ -6011,25 +5881,6 @@ expect@^26.6.2:
jest-message-util "^26.6.2"
jest-regex-util "^26.0.0"
-expo-constants@~13.2.0:
- version "13.2.4"
- resolved "https://registry.npmmirror.com/expo-constants/-/expo-constants-13.2.4.tgz#eab4a553f074b2c60ad7a158d3b82e3484a94606"
- integrity sha512-Zobau8EuTk2GgafwkfGnWM6CmSLB7X8qnQXVuXe0nd3v92hfQUmRWGhJwH88uxXj3LrfqctM6PaJ8taG1vxfBw==
- dependencies:
- "@expo/config" "~7.0.0"
- uuid "^3.3.2"
-
-expo-linking@~3.2.2:
- version "3.2.4"
- resolved "https://registry.npmmirror.com/expo-linking/-/expo-linking-3.2.4.tgz#94d65ee957f45e2d2e12a25df7ad063a17807eee"
- integrity sha512-yVH72hqridgxiknuqeVs5ZClxIaEft2xyDfC9+x8pSelGb1oMnaOYY4muE9ytph6VCXbmMI9VNf0etrfFoDLqQ==
- dependencies:
- "@types/qs" "^6.5.3"
- expo-constants "~13.2.0"
- invariant "^2.2.4"
- qs "^6.9.1"
- url-parse "^1.5.9"
-
express@^4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
@@ -6406,14 +6257,6 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
-find-up@~5.0.0:
- version "5.0.0"
- resolved "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
- integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
- dependencies:
- locate-path "^6.0.0"
- path-exists "^4.0.0"
-
findup-sync@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc"
@@ -6732,11 +6575,6 @@ get-value@^2.0.3, get-value@^2.0.6:
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
-getenv@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmmirror.com/getenv/-/getenv-1.0.0.tgz#874f2e7544fbca53c7a4738f37de8605c3fcfc31"
- integrity sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==
-
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
@@ -6833,18 +6671,6 @@ glob-watcher@^5.0.3:
normalize-path "^3.0.0"
object.defaults "^1.1.0"
-glob@7.1.6:
- version "7.1.6"
- resolved "https://registry.npmmirror.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
- integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.4"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
@@ -9245,12 +9071,10 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
-locate-path@^6.0.0:
- version "6.0.0"
- resolved "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
- integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
- dependencies:
- p-locate "^5.0.0"
+lodash.assignwith@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.npmmirror.com/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz#127a97f02adc41751a954d24b0de17e100e038eb"
+ integrity sha512-ZznplvbvtjK2gMvnQ1BR/zqPFZmS6jbK4p+6Up4xcRYA7yMIwxHCfbTcrYxXKzzqLsQ05eJPVznEW3tuwV7k1g==
lodash.clonedeep@^4.5.0:
version "4.5.0"
@@ -10152,15 +9976,6 @@ mute-stream@0.0.7:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
-mz@^2.7.0:
- version "2.7.0"
- resolved "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
- integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
- dependencies:
- any-promise "^1.0.0"
- object-assign "^4.0.1"
- thenify-all "^1.0.0"
-
nan@^2.12.1:
version "2.14.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
@@ -10787,13 +10602,6 @@ p-locate@^4.1.0:
dependencies:
p-limit "^2.2.0"
-p-locate@^5.0.0:
- version "5.0.0"
- resolved "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
- integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
- dependencies:
- p-limit "^3.0.2"
-
p-map@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
@@ -11142,14 +10950,6 @@ plist@^3.0.1:
xmlbuilder "^9.0.7"
xmldom "^0.5.0"
-plist@^3.0.5:
- version "3.0.6"
- resolved "https://registry.npmmirror.com/plist/-/plist-3.0.6.tgz#7cfb68a856a7834bca6dbfe3218eb9c7740145d3"
- integrity sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA==
- dependencies:
- base64-js "^1.5.1"
- xmlbuilder "^15.1.1"
-
plugin-error@1.0.1, plugin-error@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c"
@@ -11914,13 +11714,6 @@ qs@6.7.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
-qs@^6.9.1:
- version "6.11.0"
- resolved "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
- integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
- dependencies:
- side-channel "^1.0.4"
-
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
@@ -13208,7 +13001,7 @@ requires-port@^1.0.0:
resize-observer-polyfill@^1.5.0:
version "1.5.1"
- resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
+ resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-cwd@^2.0.0:
@@ -13478,7 +13271,7 @@ sass-loader@^8.0.0:
schema-utils "^2.6.1"
semver "^6.3.0"
-sax@>=0.6.0, sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.4:
+sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
@@ -13569,11 +13362,6 @@ semver@7.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
-semver@7.3.2:
- version "7.3.2"
- resolved "https://registry.npmmirror.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
- integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
-
semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
@@ -13792,15 +13580,6 @@ simple-plist@^1.0.0:
bplist-parser "0.2.0"
plist "^3.0.1"
-simple-plist@^1.1.0:
- version "1.3.1"
- resolved "https://registry.npmmirror.com/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017"
- integrity sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==
- dependencies:
- bplist-creator "0.1.0"
- bplist-parser "0.3.1"
- plist "^3.0.5"
-
simple-swizzle@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
@@ -13846,11 +13625,6 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
-slugify@^1.3.4:
- version "1.6.5"
- resolved "https://registry.npmmirror.com/slugify/-/slugify-1.6.5.tgz#c8f5c072bf2135b80703589b39a3d41451fbe8c8"
- integrity sha512-8mo9bslnBO3tr5PEVFzMPIWwWnipGS0xVbYf65zxDqfNwmzYn1LpiKNrR6DlClusuvo+hDHd1zKpmfAe83NQSQ==
-
snapdragon-node@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@@ -14157,7 +13931,7 @@ stream-browserify@^2.0.1:
inherits "~2.0.1"
readable-stream "^2.0.2"
-stream-buffers@2.2.x, stream-buffers@~2.2.0:
+stream-buffers@~2.2.0:
version "2.2.0"
resolved "https://registry.npmmirror.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==
@@ -14436,18 +14210,6 @@ stylehacks@^4.0.0:
postcss "^7.0.0"
postcss-selector-parser "^3.0.0"
-sucrase@^3.20.0:
- version "3.29.0"
- resolved "https://registry.npmmirror.com/sucrase/-/sucrase-3.29.0.tgz#3207c5bc1b980fdae1e539df3f8a8a518236da7d"
- integrity sha512-bZPAuGA5SdFHuzqIhTAqt9fvNEo9rESqXIG3oiKdF8K4UmkQxC4KlNL3lVyAErXp+mPvUqZ5l13qx6TrDIGf3A==
- dependencies:
- commander "^4.0.0"
- glob "7.1.6"
- lines-and-columns "^1.1.6"
- mz "^2.7.0"
- pirates "^4.0.1"
- ts-interface-checker "^0.1.9"
-
sudo-prompt@^9.0.0:
version "9.2.1"
resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd"
@@ -14659,20 +14421,6 @@ text-table@0.2.0, text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
-thenify-all@^1.0.0:
- version "1.6.0"
- resolved "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
- integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==
- dependencies:
- thenify ">= 3.1.0 < 4"
-
-"thenify@>= 3.1.0 < 4":
- version "3.3.1"
- resolved "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
- integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
- dependencies:
- any-promise "^1.0.0"
-
throat@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"
@@ -14879,11 +14627,6 @@ trough@^1.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
-ts-interface-checker@^0.1.9:
- version "0.1.13"
- resolved "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
- integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
-
ts-jest@^25.4.0:
version "25.5.1"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.5.1.tgz#2913afd08f28385d54f2f4e828be4d261f4337c7"
@@ -15374,14 +15117,6 @@ url-parse@^1.1.8, url-parse@^1.4.3, url-parse@^1.5.1:
querystringify "^2.1.1"
requires-port "^1.0.0"
-url-parse@^1.5.9:
- version "1.5.10"
- resolved "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
- integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
- dependencies:
- querystringify "^2.1.1"
- requires-port "^1.0.0"
-
url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
@@ -15451,11 +15186,6 @@ uuid@^3.3.2, uuid@^3.4.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-uuid@^7.0.3:
- version "7.0.3"
- resolved "https://registry.npmmirror.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
- integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
-
uuid@^8.3.0:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
@@ -15692,8 +15422,10 @@ watchpack@^1.7.4:
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453"
integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==
dependencies:
+ chokidar "^3.4.1"
graceful-fs "^4.1.2"
neo-async "^2.5.0"
+ watchpack-chokidar2 "^2.0.1"
optionalDependencies:
chokidar "^3.4.1"
watchpack-chokidar2 "^2.0.1"
@@ -16121,14 +15853,6 @@ xcode@^2.0.0:
simple-plist "^1.0.0"
uuid "^3.3.2"
-xcode@^3.0.1:
- version "3.0.1"
- resolved "https://registry.npmmirror.com/xcode/-/xcode-3.0.1.tgz#3efb62aac641ab2c702458f9a0302696146aa53c"
- integrity sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==
- dependencies:
- simple-plist "^1.1.0"
- uuid "^7.0.3"
-
"xml-name-validator@>= 2.0.1 < 3.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
@@ -16139,34 +15863,11 @@ xml-name-validator@^3.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
-xml2js@0.4.23:
- version "0.4.23"
- resolved "https://registry.npmmirror.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
- integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
- dependencies:
- sax ">=0.6.0"
- xmlbuilder "~11.0.0"
-
-xmlbuilder@^14.0.0:
- version "14.0.0"
- resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-14.0.0.tgz#876b5aec4f05ffd5feb97b0a871c855d16fbeb8c"
- integrity sha512-ts+B2rSe4fIckR6iquDjsKbQFK2NlUk6iG5nf14mDEyldgoc2nEKZ3jZWMPTxGQwVgToSjt6VGIho1H8/fNFTg==
-
-xmlbuilder@^15.1.1:
- version "15.1.1"
- resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
- integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==
-
xmlbuilder@^9.0.7:
version "9.0.7"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
-xmlbuilder@~11.0.0:
- version "11.0.1"
- resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
- integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
-
xmlchars@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"