From 1c1ef86988f87274067a055658a44849fd56460a Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 16 Jan 2025 15:39:08 +0800 Subject: [PATCH 01/54] =?UTF-8?q?feat:=20=E5=88=9D=E5=A7=8B=E5=8C=96picker?= =?UTF-8?q?view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.json | 13 +++++++ .../pickerview/__test__/pickerview.spec.tsx | 9 +++++ src/packages/pickerview/demo.taro.tsx | 31 ++++++++++++++++ src/packages/pickerview/demo.tsx | 25 +++++++++++++ src/packages/pickerview/demos/h5/demo1.tsx | 13 +++++++ src/packages/pickerview/demos/taro/demo1.tsx | 13 +++++++ src/packages/pickerview/doc.md | 36 +++++++++++++++++++ src/packages/pickerview/index.taro.ts | 4 +++ src/packages/pickerview/index.ts | 4 +++ src/packages/pickerview/pickerview.scss | 2 ++ src/packages/pickerview/pickerview.taro.tsx | 28 +++++++++++++++ src/packages/pickerview/pickerview.tsx | 26 ++++++++++++++ src/packages/pickerview/types.ts | 3 ++ 13 files changed, 207 insertions(+) create mode 100644 src/packages/pickerview/__test__/pickerview.spec.tsx create mode 100644 src/packages/pickerview/demo.taro.tsx create mode 100644 src/packages/pickerview/demo.tsx create mode 100644 src/packages/pickerview/demos/h5/demo1.tsx create mode 100644 src/packages/pickerview/demos/taro/demo1.tsx create mode 100644 src/packages/pickerview/doc.md create mode 100644 src/packages/pickerview/index.taro.ts create mode 100644 src/packages/pickerview/index.ts create mode 100644 src/packages/pickerview/pickerview.scss create mode 100644 src/packages/pickerview/pickerview.taro.tsx create mode 100644 src/packages/pickerview/pickerview.tsx create mode 100644 src/packages/pickerview/types.ts diff --git a/src/config.json b/src/config.json index 72a1f2e5d6..15d33f6f26 100644 --- a/src/config.json +++ b/src/config.json @@ -690,6 +690,19 @@ "author": "dsj", "dd": false }, + { + "version": "3.0.0", + "name": "PickerView", + "type": "component", + "cName": "选择器视图", + "desc": "PickerView 是 Picker 的内容区域。", + "sort": 15, + "show": true, + "taro": true, + "v15": false, + "dd": true, + "author": "songsong" + }, { "version": "3.0.0", "name": "Radio", diff --git a/src/packages/pickerview/__test__/pickerview.spec.tsx b/src/packages/pickerview/__test__/pickerview.spec.tsx new file mode 100644 index 0000000000..d63fdee45c --- /dev/null +++ b/src/packages/pickerview/__test__/pickerview.spec.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import { render } from '@testing-library/react' +import '@testing-library/jest-dom' +import { PickerView } from '../pickerview' + +test('should match snapshot', () => { + const { container } = render() + expect(container).toMatchSnapshot() +}) diff --git a/src/packages/pickerview/demo.taro.tsx b/src/packages/pickerview/demo.taro.tsx new file mode 100644 index 0000000000..14a5c1e209 --- /dev/null +++ b/src/packages/pickerview/demo.taro.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import Taro from '@tarojs/taro' +import { ScrollView, View } from '@tarojs/components' +import { useTranslate } from '@/sites/assets/locale/taro' +import Header from '@/sites/components/header' +import Demo1 from './demos/taro/demo1' + +const PickerViewDemo = () => { + const [translated] = useTranslate({ + 'zh-CN': { + title: '基础用法', + }, + 'en-US': { + title: 'Basic Usage', + }, + 'zh-TW': { + title: '基礎用法', + }, + }) + return ( + <> +
+ + {translated.title} + + + + ) +} + +export default PickerViewDemo diff --git a/src/packages/pickerview/demo.tsx b/src/packages/pickerview/demo.tsx new file mode 100644 index 0000000000..a9570d035f --- /dev/null +++ b/src/packages/pickerview/demo.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { useTranslate } from '@/sites/assets/locale' +import Demo1 from './demos/h5/demo1' + +const PickerViewDemo = () => { + const [translated] = useTranslate({ + 'zh-CN': { + title: '基础用法', + }, + 'en-US': { + title: 'Basic Usage', + }, + 'zh-TW': { + title: '基礎用法', + }, + }) + return ( +
+

{translated.title}

+ +
+ ) +} + +export default PickerViewDemo diff --git a/src/packages/pickerview/demos/h5/demo1.tsx b/src/packages/pickerview/demos/h5/demo1.tsx new file mode 100644 index 0000000000..22e86eaff9 --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo1.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { Cell, PickerView } from '@nutui/nutui-react' +// import { Dongdong } from '@nutui/icons-react' + +const Demo1 = () => { + return ( + + + + ) +} + +export default Demo1 diff --git a/src/packages/pickerview/demos/taro/demo1.tsx b/src/packages/pickerview/demos/taro/demo1.tsx new file mode 100644 index 0000000000..e0b0467aa2 --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo1.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { Cell, PickerView } from '@nutui/nutui-react-taro' +// import { Dongdong } from '@nutui/icons-react-taro' + +const Demo1 = () => { + return ( + + + + ) +} + +export default Demo1 diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md new file mode 100644 index 0000000000..d9b1436be8 --- /dev/null +++ b/src/packages/pickerview/doc.md @@ -0,0 +1,36 @@ +# PickerView 选择器视图 + +PickerView 是 Picker 的内容区域。 + +## 引入 + +```tsx +import { name } from '@nutui/nutui-react' +``` + +## 示例代码 + +### 基础用法 + +:::demo + + + +::: + +## PickerView + +### Props + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| name | 图标名 | String | - | + +## 主题定制 + +### 样式变量 + +组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。 +| 名称 | 说明 | 默认值 | +| --- | --- | --- | +| --nutui-pickerview-height | badge 的高度 | `14px` | diff --git a/src/packages/pickerview/index.taro.ts b/src/packages/pickerview/index.taro.ts new file mode 100644 index 0000000000..b7fd78006c --- /dev/null +++ b/src/packages/pickerview/index.taro.ts @@ -0,0 +1,4 @@ +import { PickerView } from './pickerview.taro' + +export type { PickerViewProps } from './types' +export default PickerView diff --git a/src/packages/pickerview/index.ts b/src/packages/pickerview/index.ts new file mode 100644 index 0000000000..1865a8b105 --- /dev/null +++ b/src/packages/pickerview/index.ts @@ -0,0 +1,4 @@ +import { PickerView } from './pickerview' + +export type { PickerViewProps } from './types' +export default PickerView diff --git a/src/packages/pickerview/pickerview.scss b/src/packages/pickerview/pickerview.scss new file mode 100644 index 0000000000..78d89b48d9 --- /dev/null +++ b/src/packages/pickerview/pickerview.scss @@ -0,0 +1,2 @@ +.nut-pickerview { +} diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx new file mode 100644 index 0000000000..5db2fd07ad --- /dev/null +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -0,0 +1,28 @@ +import React, { FunctionComponent } from 'react' +import classNames from 'classnames' +import { View } from '@tarojs/components' +import { ComponentDefaults } from '@/utils/typings' +import { PickerViewProps } from './types' + +// import { useConfig } from '@/packages/configprovider/configprovider.taro' +// import { useRtl } from '@/packages/configprovider/index.taro' + +const defaultProps = { + ...ComponentDefaults, +} as PickerViewProps +export const PickerView: FunctionComponent< + Partial & React.HTMLAttributes +> = (props) => { + // const { locale } = useConfig() + // const rtl = useRtl() + const { className, style } = { ...defaultProps, ...props } + const classPrefix = 'nut-pickerview' + const cls = classNames(classPrefix, className) + return ( + + PickerView + + ) +} + +PickerView.displayName = 'NutPickerView' diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx new file mode 100644 index 0000000000..0f33d583db --- /dev/null +++ b/src/packages/pickerview/pickerview.tsx @@ -0,0 +1,26 @@ +import React, { FunctionComponent } from 'react' +import classNames from 'classnames' +import { ComponentDefaults } from '@/utils/typings' +import { PickerViewProps } from './types' +// import { useConfig } from '@/packages/configprovider' +// import { useRtl } from '@/packages/configprovider' + +const defaultProps = { + ...ComponentDefaults, +} as PickerViewProps +export const PickerView: FunctionComponent< + Partial & React.HTMLAttributes +> = (props) => { + // const { locale } = useConfig() + // const rtl = useRtl() + const { className, style } = { ...defaultProps, ...props } + const classPrefix = 'nut-pickerview' + const cls = classNames(classPrefix, className) + return ( +
+ PickerView +
+ ) +} + +PickerView.displayName = 'NutPickerView' diff --git a/src/packages/pickerview/types.ts b/src/packages/pickerview/types.ts new file mode 100644 index 0000000000..a72b2c3b4e --- /dev/null +++ b/src/packages/pickerview/types.ts @@ -0,0 +1,3 @@ +import { BasicComponent } from '@/utils/typings' + +export interface PickerViewProps extends BasicComponent {} From 3acf368eb1f983ecdd7ae331d91ec6ff44773272 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 22 Jan 2025 18:01:14 +0800 Subject: [PATCH 02/54] feat: add pickerView --- .../pickerview/__test__/pickerview.spec.tsx | 2 +- src/packages/pickerview/demo.taro.tsx | 18 + src/packages/pickerview/demo.tsx | 18 + src/packages/pickerview/demos/h5/demo1.tsx | 32 +- src/packages/pickerview/demos/h5/demo2.tsx | 34 ++ src/packages/pickerview/demos/h5/demo3.tsx | 39 +++ src/packages/pickerview/demos/h5/demo4.tsx | 38 ++ src/packages/pickerview/demos/taro/demo1.tsx | 32 +- src/packages/pickerview/demos/taro/demo2.tsx | 34 ++ src/packages/pickerview/demos/taro/demo3.tsx | 39 +++ src/packages/pickerview/demos/taro/demo4.tsx | 38 ++ src/packages/pickerview/doc.md | 47 ++- src/packages/pickerview/index.taro.ts | 2 +- src/packages/pickerview/index.ts | 2 +- src/packages/pickerview/pickerroller.taro.tsx | 328 ++++++++++++++++++ src/packages/pickerview/pickerroller.tsx | 302 ++++++++++++++++ src/packages/pickerview/pickerview.scss | 78 +++++ src/packages/pickerview/pickerview.taro.tsx | 111 +++++- src/packages/pickerview/pickerview.tsx | 110 +++++- src/packages/pickerview/types.ts | 32 +- src/styles/variables.scss | 4 +- 21 files changed, 1295 insertions(+), 45 deletions(-) create mode 100644 src/packages/pickerview/demos/h5/demo2.tsx create mode 100644 src/packages/pickerview/demos/h5/demo3.tsx create mode 100644 src/packages/pickerview/demos/h5/demo4.tsx create mode 100644 src/packages/pickerview/demos/taro/demo2.tsx create mode 100644 src/packages/pickerview/demos/taro/demo3.tsx create mode 100644 src/packages/pickerview/demos/taro/demo4.tsx create mode 100644 src/packages/pickerview/pickerroller.taro.tsx create mode 100644 src/packages/pickerview/pickerroller.tsx diff --git a/src/packages/pickerview/__test__/pickerview.spec.tsx b/src/packages/pickerview/__test__/pickerview.spec.tsx index d63fdee45c..089e995187 100644 --- a/src/packages/pickerview/__test__/pickerview.spec.tsx +++ b/src/packages/pickerview/__test__/pickerview.spec.tsx @@ -1,7 +1,7 @@ import React from 'react' import { render } from '@testing-library/react' import '@testing-library/jest-dom' -import { PickerView } from '../pickerview' +import PickerView from '../pickerview' test('should match snapshot', () => { const { container } = render() diff --git a/src/packages/pickerview/demo.taro.tsx b/src/packages/pickerview/demo.taro.tsx index 14a5c1e209..9773e2cae5 100644 --- a/src/packages/pickerview/demo.taro.tsx +++ b/src/packages/pickerview/demo.taro.tsx @@ -4,17 +4,29 @@ import { ScrollView, View } from '@tarojs/components' import { useTranslate } from '@/sites/assets/locale/taro' import Header from '@/sites/components/header' import Demo1 from './demos/taro/demo1' +import Demo2 from './demos/taro/demo2' +import Demo3 from './demos/taro/demo3' +import Demo4 from './demos/taro/demo4' const PickerViewDemo = () => { const [translated] = useTranslate({ 'zh-CN': { title: '基础用法', + adjustHeight: '自适应高度', + multiColumn: '多列', + controlled: '受控', }, 'en-US': { title: 'Basic Usage', + adjustHeight: 'Adjust Height', + multiColumn: 'Multi Column', + controlled: 'Controlled', }, 'zh-TW': { title: '基礎用法', + adjustHeight: '自適應高度', + multiColumn: '多列', + controlled: '受控', }, }) return ( @@ -23,6 +35,12 @@ const PickerViewDemo = () => { {translated.title} + {translated.controlled} + + {translated.adjustHeight} + + {translated.multiColumn} + ) diff --git a/src/packages/pickerview/demo.tsx b/src/packages/pickerview/demo.tsx index a9570d035f..b45f73be58 100644 --- a/src/packages/pickerview/demo.tsx +++ b/src/packages/pickerview/demo.tsx @@ -1,23 +1,41 @@ import React from 'react' import { useTranslate } from '@/sites/assets/locale' import Demo1 from './demos/h5/demo1' +import Demo2 from './demos/h5/demo2' +import Demo3 from './demos/h5/demo3' +import Demo4 from './demos/h5/demo4' const PickerViewDemo = () => { const [translated] = useTranslate({ 'zh-CN': { title: '基础用法', + adjustHeight: '自适应高度', + multiColumn: '多列', + controlled: '受控', }, 'en-US': { title: 'Basic Usage', + adjustHeight: 'Adjust Height', + multiColumn: 'Multi Column', + controlled: 'Controlled', }, 'zh-TW': { title: '基礎用法', + adjustHeight: '自適應高度', + multiColumn: '多列', + controlled: '受控', }, }) return (

{translated.title}

+

{translated.controlled}

+ +

{translated.adjustHeight}

+ +

{translated.multiColumn}

+
) } diff --git a/src/packages/pickerview/demos/h5/demo1.tsx b/src/packages/pickerview/demos/h5/demo1.tsx index 22e86eaff9..cacf341036 100644 --- a/src/packages/pickerview/demos/h5/demo1.tsx +++ b/src/packages/pickerview/demos/h5/demo1.tsx @@ -1,13 +1,33 @@ import React from 'react' -import { Cell, PickerView } from '@nutui/nutui-react' -// import { Dongdong } from '@nutui/icons-react' +import { PickerView, Cell } from '@nutui/nutui-react' const Demo1 = () => { + const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ] + return ( - - - + <> + + { + console.log('onChange', value, selectOptions) + }} + /> + + ) } - export default Demo1 diff --git a/src/packages/pickerview/demos/h5/demo2.tsx b/src/packages/pickerview/demos/h5/demo2.tsx new file mode 100644 index 0000000000..dd16c8a92e --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo2.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import { PickerView, Cell } from '@nutui/nutui-react' + +const Demo1 = () => { + const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ] + + return ( + <> + + { + console.log('onChange', value, selectOptions) + }} + /> + + + ) +} +export default Demo1 diff --git a/src/packages/pickerview/demos/h5/demo3.tsx b/src/packages/pickerview/demos/h5/demo3.tsx new file mode 100644 index 0000000000..555c655e39 --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo3.tsx @@ -0,0 +1,39 @@ +import React, { useState } from 'react' +import { PickerView, Cell } from '@nutui/nutui-react' +import isEqual from 'react-fast-compare' + +const Demo3 = () => { + const listData = [ + [ + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, + ], + [ + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, + ], + ] + const [value, setValue] = useState(['Tuesday', 'Evening']) + + return ( + <> + + { + console.log('onChange', value, selectOptions) + if (isEqual(value, ['Tuesday', 'Afternoon'])) { + setValue(['Monday', 'Evening']) + } + }} + /> + + + ) +} +export default Demo3 diff --git a/src/packages/pickerview/demos/h5/demo4.tsx b/src/packages/pickerview/demos/h5/demo4.tsx new file mode 100644 index 0000000000..e205b5513a --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo4.tsx @@ -0,0 +1,38 @@ +import React, { useState } from 'react' +import { PickerView, Cell } from '@nutui/nutui-react' + +const Demo1 = () => { + const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ] + + const [value, setValue] = useState([2]) + + return ( + <> + + { + console.log('onChange', value, selectOptions) + if (value[0] === 3) { + setValue([1]) + } + }} + /> + + + ) +} +export default Demo1 diff --git a/src/packages/pickerview/demos/taro/demo1.tsx b/src/packages/pickerview/demos/taro/demo1.tsx index e0b0467aa2..4d3d363bc2 100644 --- a/src/packages/pickerview/demos/taro/demo1.tsx +++ b/src/packages/pickerview/demos/taro/demo1.tsx @@ -1,13 +1,33 @@ import React from 'react' -import { Cell, PickerView } from '@nutui/nutui-react-taro' -// import { Dongdong } from '@nutui/icons-react-taro' +import { PickerView, Cell } from '@nutui/nutui-react-taro' const Demo1 = () => { + const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ] + return ( - - - + <> + + { + console.log('onChange', value, selectOptions) + }} + /> + + ) } - export default Demo1 diff --git a/src/packages/pickerview/demos/taro/demo2.tsx b/src/packages/pickerview/demos/taro/demo2.tsx new file mode 100644 index 0000000000..f717632ce1 --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo2.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import { PickerView, Cell } from '@nutui/nutui-react-taro' + +const Demo1 = () => { + const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ] + + return ( + <> + + { + console.log('onChange', value, selectOptions) + }} + /> + + + ) +} +export default Demo1 diff --git a/src/packages/pickerview/demos/taro/demo3.tsx b/src/packages/pickerview/demos/taro/demo3.tsx new file mode 100644 index 0000000000..ae6954258d --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo3.tsx @@ -0,0 +1,39 @@ +import React, { useState } from 'react' +import { PickerView, Cell } from '@nutui/nutui-react-taro' +import isEqual from 'react-fast-compare' + +const Demo3 = () => { + const listData = [ + [ + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, + ], + [ + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, + ], + ] + const [value, setValue] = useState(['Tuesday', 'Evening']) + + return ( + <> + + { + console.log('onChange', value, selectOptions) + if (isEqual(value, ['Tuesday', 'Afternoon'])) { + setValue(['Monday', 'Evening']) + } + }} + /> + + + ) +} +export default Demo3 diff --git a/src/packages/pickerview/demos/taro/demo4.tsx b/src/packages/pickerview/demos/taro/demo4.tsx new file mode 100644 index 0000000000..c0234cdb03 --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo4.tsx @@ -0,0 +1,38 @@ +import React, { useState } from 'react' +import { PickerView, Cell } from '@nutui/nutui-react-taro' + +const Demo1 = () => { + const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ] + + const [value, setValue] = useState([2]) + + return ( + <> + + { + console.log('onChange', value, selectOptions) + if (value[0] === 3) { + setValue([1]) + } + }} + /> + + + ) +} +export default Demo1 diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index d9b1436be8..0069376561 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -18,19 +18,60 @@ import { name } from '@nutui/nutui-react' ::: -## PickerView +### 受控 + +:::demo + + + +::: + +### 自定义高度 + +:::demo + + + +::: + +### 多列 + +:::demo + + + +::: + +## Picker ### Props | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| name | 图标名 | String | - | +| options | 列表数据 | `Array` | `[]` | +| value | 选中值,受控 | `Array` | `[]` | +| defaultValue | 默认选中 | `Array` | `[]` | +| threeDimensional | 是否开启3D效果 | `boolean` | `true` | +| duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | +| onChange | 每一列值变更时调用 | `(options, value) => void` | `-` | + +### options 数据结构 + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| label | 选项的文字内容 | `string` \| `number` | `-` | +| value | 选项对应的值,且唯一 | `string` \| `number` | `-` | ## 主题定制 ### 样式变量 组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。 + | 名称 | 说明 | 默认值 | | --- | --- | --- | -| --nutui-pickerview-height | badge 的高度 | `14px` | +| \--nutui-picker-item-height | 面板每条数据高度 | `36px` | +| \--nutui-picker-item-text-color | 面板每条数据的字色 | `$color-title` | +| \--nutui-picker-item-text-font-size | 面板每条数据的字号 | `$font-size-base` | +| \--nutui-picker-item-active-line-border | 面板当前选中的border值 | `1px solid $color-border` | +| \--nut-picker-mask-background | 面板遮挡区渐变值 | `linear-gradient(180deg, var(--nutui-white-12), var(--nutui-white-7)),linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7))` | diff --git a/src/packages/pickerview/index.taro.ts b/src/packages/pickerview/index.taro.ts index b7fd78006c..b42ce6bc9a 100644 --- a/src/packages/pickerview/index.taro.ts +++ b/src/packages/pickerview/index.taro.ts @@ -1,4 +1,4 @@ -import { PickerView } from './pickerview.taro' +import PickerView from './pickerview.taro' export type { PickerViewProps } from './types' export default PickerView diff --git a/src/packages/pickerview/index.ts b/src/packages/pickerview/index.ts index 1865a8b105..47b8834afe 100644 --- a/src/packages/pickerview/index.ts +++ b/src/packages/pickerview/index.ts @@ -1,4 +1,4 @@ -import { PickerView } from './pickerview' +import PickerView from './pickerview' export type { PickerViewProps } from './types' export default PickerView diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx new file mode 100644 index 0000000000..4c5b1493bd --- /dev/null +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -0,0 +1,328 @@ +import React, { + useState, + useEffect, + useRef, + ForwardRefRenderFunction, + useImperativeHandle, +} from 'react' +import { View } from '@tarojs/components' +import { useTouch } from '@/utils/use-touch' +import { passiveSupported } from '@/utils/supports-passive' +import { PickerRollerProps, PickerOptionItem } from './types' + +const InternalPickerRoller: ForwardRefRenderFunction< + { stopMomentum: () => void; moving: boolean }, + Partial +> = (props, ref) => { + const { + keyIndex = 0, + options = [], + threeDimensional = true, + duration = 1000, + onSelect, + renderLabel = (item: PickerOptionItem) => { + return item.label + }, + } = props + + const touch = useTouch() + const DEFAULT_DURATION = 200 + // 触发惯性滑动条件: 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move, 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动 + const INERTIA_TIME = 300 + const INERTIA_DISTANCE = 15 + const [currIndex, setCurrIndex] = useState(1) + const lineSpacing = useRef(36) + const [touchTime, setTouchTime] = useState(0) + const [touchDeg, setTouchDeg] = useState('0deg') + const rotation = 20 + const moving = useRef(false) + + const rollerRef = useRef(null) + const pickerRollerRef = useRef(null) + + const [startTime, setStartTime] = useState(0) + const [startY, setStartY] = useState(0) + + const transformY = useRef(0) + const [scrollDistance, setScrollDistance] = useState(0) + + // 获取 lineSpacing.current CSS变量 + useEffect(() => { + const element = pickerRollerRef.current + if (element) { + const computedStyle = getComputedStyle(element) + const currentLineSpacing = computedStyle.getPropertyValue( + '--nutui-picker-item-height' + ) + console.log('currentLineSpacing', currentLineSpacing) + currentLineSpacing && + (lineSpacing.current = parseFloat(currentLineSpacing)) + } + }, [pickerRollerRef.current]) + + const isHidden = (index: number) => { + if (index >= currIndex + 8 || index <= currIndex - 8) { + return true + } + return false + } + + const setTransform = ( + type: string, + deg: string, + time = DEFAULT_DURATION, + translateY = 0 + ) => { + let nTime = time + if (type !== 'end') { + nTime = 0 + } + setTouchTime(nTime) + setTouchDeg(deg) + setScrollDistance(translateY) + } + + const setMove = (move: number, type?: string, time?: number) => { + let updateMove = move + transformY.current + if (type === 'end') { + // 限定滚动距离 + if (updateMove > 0) { + updateMove = 0 + } + if (updateMove < -(options.length - 1) * lineSpacing.current) { + updateMove = -(options.length - 1) * lineSpacing.current + } + + // 设置滚动距离为lineSpacing.current的倍数值 + const endMove = + Math.round(updateMove / lineSpacing.current) * lineSpacing.current + const deg = `${ + (Math.abs(Math.round(endMove / lineSpacing.current)) + 1) * rotation + }deg` + + setTransform(type, deg, time, endMove) + setCurrIndex(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) + } else { + let deg = 0 + const currentDeg = (-updateMove / lineSpacing.current + 1) * rotation + + // picker 滚动的最大角度 + const maxDeg = (options.length + 1) * rotation + const minDeg = 0 + + deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) + + if (minDeg < deg && deg < maxDeg) { + setTransform('', `${deg}deg`, undefined, updateMove) + setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) + } + } + } + + const setChooseValue = (move: number) => { + onSelect?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex) + } + + // 开始滚动 + const touchStart = (event: React.TouchEvent) => { + touch.start(event) + setStartY(touch.deltaY.current) + setStartTime(Date.now()) + transformY.current = scrollDistance + } + + const touchMove = (event: React.TouchEvent) => { + touch.move(event) + if ((touch as any).isVertical) { + moving.current = true + preventDefault(event, true) + } + const move = touch.deltaY.current - startY + setMove(move) + } + + const touchEnd = () => { + if (!moving.current) return + const move = touch.deltaY.current - startY + const moveTime = Date.now() - startTime + // 区分是否为惯性滚动 + if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { + // 惯性滚动 + const distance = momentum(move, moveTime) + setMove(distance, 'end', +duration) + } else { + setMove(move, 'end') + } + setTimeout(() => { + touch.reset() + }, 0) + } + + // 惯性滚动 距离 + const momentum = (distance: number, duration: number) => { + let nDistance = distance + // 惯性滚动的速度 + const speed = Math.abs(nDistance / duration) + // 惯性滚动的距离 + nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) + return nDistance + } + + const modifyStatus = (type?: boolean, val?: string | number) => { + const value = val || props.value + let index = -1 + if (value) { + options.some((item, idx) => { + if (item.value === value) { + index = idx + return true + } + return false + }) + } else { + options.forEach((item, i) => { + if (item.value === props.value) { + index = i + } + }) + } + + setCurrIndex(index === -1 ? 1 : index + 1) + const move = index * lineSpacing.current + type && setChooseValue(-move) + console.log(index, move, 'props.value.index') + setMove(-move) + } + + // 惯性滚动结束 + const stopMomentum = () => { + moving.current = false + setTouchTime(0) + setChooseValue(scrollDistance) + } + // 阻止默认事件 + const preventDefault = ( + event: React.TouchEvent, + isStopPropagation?: boolean + ) => { + event.preventDefault() + + if (isStopPropagation) { + event.stopPropagation() + } + } + + useEffect(() => { + setScrollDistance(0) + transformY.current = 0 + modifyStatus(false) + }, [options]) + + useImperativeHandle(ref, () => ({ + stopMomentum, + moving: moving.current, + })) + + useEffect(() => { + const eventOptions = passiveSupported + ? { passive: false, once: true } + : false + pickerRollerRef.current?.addEventListener( + 'touchstart', + touchStart, + eventOptions + ) + pickerRollerRef.current?.addEventListener( + 'touchmove', + touchMove, + eventOptions + ) + pickerRollerRef.current?.addEventListener( + 'touchend', + touchEnd, + eventOptions + ) + return () => { + pickerRollerRef.current?.removeEventListener( + 'touchstart', + touchStart, + eventOptions + ) + pickerRollerRef.current?.removeEventListener( + 'touchmove', + touchMove, + eventOptions + ) + pickerRollerRef.current?.removeEventListener( + 'touchend', + touchEnd, + eventOptions + ) + } + }) + + const touchRollerStyle = () => { + return { + transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, + transform: `rotate3d(1, 0, 0, ${touchDeg})`, + } + } + const touchTileStyle = () => { + return { + transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, + transform: `translate3d(0, ${scrollDistance}px, 0)`, + } + } + + const rollerStyle = (index: number) => { + return { + transform: `rotate3d(1, 0, 0, ${ + -rotation * (index + 1) + }deg) translate3d(0px, 0px, ${Math.round(lineSpacing.current * 3.2)}px)`, + } + } + + return ( + + + {/* 3D 效果 */} + {threeDimensional && + options.map((item, index) => ( + + {renderLabel(item)} + + ))} + {/* 平铺 */} + {!threeDimensional && + options.map((item, index) => { + return ( + + {renderLabel(item)} + + ) + })} + + + ) +} + +const PickerRoller = React.forwardRef< + { stopMomentum: () => void; moving: boolean }, + Partial +>(InternalPickerRoller) + +export default PickerRoller diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx new file mode 100644 index 0000000000..604cf97476 --- /dev/null +++ b/src/packages/pickerview/pickerroller.tsx @@ -0,0 +1,302 @@ +import React, { + useState, + useEffect, + useRef, + ForwardRefRenderFunction, + useImperativeHandle, +} from 'react' +import { useTouch } from '@/utils/use-touch' +import { passiveSupported } from '@/utils/supports-passive' +import { PickerRollerProps, PickerOptionItem } from './types' + +const InternalPickerRoller: ForwardRefRenderFunction< + { stopMomentum: () => void; moving: boolean }, + Partial +> = (props, ref) => { + const { + keyIndex = 0, + options = [], + threeDimensional = true, + duration = 1000, + onSelect, + renderLabel = (item: PickerOptionItem) => { + return item.label + }, + } = props + + const touch = useTouch() + const DEFAULT_DURATION = 200 + // 触发惯性滑动条件: 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move, 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动 + const INERTIA_TIME = 300 + const INERTIA_DISTANCE = 15 + const [currIndex, setCurrIndex] = useState(1) + const lineSpacing = useRef(36) + const [touchTime, setTouchTime] = useState(0) + const [touchDeg, setTouchDeg] = useState('0deg') + const rotation = 20 + const moving = useRef(false) + + const rollerRef = useRef(null) + const pickerRollerRef = useRef(null) + + const [startTime, setStartTime] = useState(0) + const [startY, setStartY] = useState(0) + + const transformY = useRef(0) + const [scrollDistance, setScrollDistance] = useState(0) + + // 获取 lineSpacing.current CSS变量 + useEffect(() => { + const element = pickerRollerRef.current + if (element) { + const computedStyle = getComputedStyle(element) + const currentLineSpacing = computedStyle.getPropertyValue( + '--nutui-picker-item-height' + ) + lineSpacing.current = parseFloat(currentLineSpacing) + } + }, []) + + const isHidden = (index: number) => { + if (index >= currIndex + 8 || index <= currIndex - 8) { + return true + } + return false + } + + const setTransform = ( + type: string, + deg: string, + time = DEFAULT_DURATION, + translateY = 0 + ) => { + let nTime = time + if (type !== 'end') { + nTime = 0 + } + setTouchTime(nTime) + setTouchDeg(deg) + setScrollDistance(translateY) + } + + const setMove = (move: number, type?: string, time?: number) => { + let updateMove = move + transformY.current + if (type === 'end') { + // 限定滚动距离 + if (updateMove > 0) { + updateMove = 0 + } + if (updateMove < -(options.length - 1) * lineSpacing.current) { + updateMove = -(options.length - 1) * lineSpacing.current + } + + // 设置滚动距离为lineSpacing.current的倍数值 + const endMove = + Math.round(updateMove / lineSpacing.current) * lineSpacing.current + const deg = `${ + (Math.abs(Math.round(endMove / lineSpacing.current)) + 1) * rotation + }deg` + + setTransform(type, deg, time, endMove) + setCurrIndex(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) + } else { + let deg = 0 + const currentDeg = (-updateMove / lineSpacing.current + 1) * rotation + + // picker 滚动的最大角度 + const maxDeg = (options.length + 1) * rotation + const minDeg = 0 + + deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) + + if (minDeg < deg && deg < maxDeg) { + setTransform('', `${deg}deg`, undefined, updateMove) + setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) + } + } + } + + const setChooseValue = (move: number) => { + onSelect?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex) + } + + // 开始滚动 + const touchStart = (event: React.TouchEvent) => { + touch.start(event) + setStartY(touch.deltaY.current) + setStartTime(Date.now()) + transformY.current = scrollDistance + } + + const touchMove = (event: React.TouchEvent) => { + touch.move(event) + if ((touch as any).isVertical) { + moving.current = true + preventDefault(event, true) + } + const move = touch.deltaY.current - startY + setMove(move) + } + + const touchEnd = () => { + if (!moving.current) return + const move = touch.deltaY.current - startY + const moveTime = Date.now() - startTime + // 区分是否为惯性滚动 + if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { + // 惯性滚动 + const distance = momentum(move, moveTime) + setMove(distance, 'end', +duration) + } else { + setMove(move, 'end') + } + setTimeout(() => { + touch.reset() + }, 0) + } + + // 惯性滚动 距离 + const momentum = (distance: number, duration: number) => { + let nDistance = distance + // 惯性滚动的速度 + const speed = Math.abs(nDistance / duration) + // 惯性滚动的距离 + nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) + return nDistance + } + + const modifyStatus = (type?: boolean, val?: string | number) => { + const value = val || props.value + let index = -1 + if (value) { + options.some((item, idx) => { + if (item.value === value) { + index = idx + return true + } + return false + }) + } else { + options.forEach((item, i) => { + if (item.value === props.value) { + index = i + } + }) + } + + setCurrIndex(index === -1 ? 1 : index + 1) + const move = index * lineSpacing.current + type && setChooseValue(-move) + console.log(index, move, 'props.value.index') + setMove(-move) + } + + // 惯性滚动结束 + const stopMomentum = () => { + moving.current = false + setTouchTime(0) + setChooseValue(scrollDistance) + } + // 阻止默认事件 + const preventDefault = ( + event: React.TouchEvent, + isStopPropagation?: boolean + ) => { + if (typeof event.cancelable !== 'boolean' || event.cancelable) { + event.preventDefault() + } + + if (isStopPropagation) { + event.stopPropagation() + } + } + + useEffect(() => { + setScrollDistance(0) + transformY.current = 0 + modifyStatus(false) + console.log('modifyStatus', props.value) + }, [options]) + + useImperativeHandle(ref, () => ({ + stopMomentum, + moving: moving.current, + })) + + useEffect(() => { + const options = passiveSupported ? { passive: false } : false + pickerRollerRef.current?.addEventListener('touchstart', touchStart, options) + pickerRollerRef.current?.addEventListener('touchmove', touchMove, options) + pickerRollerRef.current?.addEventListener('touchend', touchEnd, options) + return () => { + pickerRollerRef.current?.removeEventListener('touchstart', touchStart) + pickerRollerRef.current?.removeEventListener('touchmove', touchMove) + pickerRollerRef.current?.removeEventListener('touchend', touchEnd) + } + }) + + const touchRollerStyle = () => { + return { + transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, + transform: `rotate3d(1, 0, 0, ${touchDeg})`, + } + } + const touchTileStyle = () => { + return { + transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, + transform: `translate3d(0, ${scrollDistance}px, 0)`, + } + } + + const rollerStyle = (index: number) => { + return { + transform: `rotate3d(1, 0, 0, ${ + -rotation * (index + 1) + }deg) translate3d(0px, 0px, ${Math.round(lineSpacing.current * 3.2)}px)`, + } + } + + return ( +
+
+ {/* 3D 效果 */} + {threeDimensional && + options.map((item, index) => ( +
+ {renderLabel(item)} +
+ ))} + {/* 平铺 */} + {!threeDimensional && + options.map((item, index) => { + return ( +
+ {renderLabel(item)} +
+ ) + })} +
+
+ ) +} + +const PickerRoller = React.forwardRef< + { stopMomentum: () => void; moving: boolean }, + Partial +>(InternalPickerRoller) + +export default PickerRoller diff --git a/src/packages/pickerview/pickerview.scss b/src/packages/pickerview/pickerview.scss index 78d89b48d9..c4b3d1f29d 100644 --- a/src/packages/pickerview/pickerview.scss +++ b/src/packages/pickerview/pickerview.scss @@ -1,2 +1,80 @@ .nut-pickerview { + --nutui-picker-item-height: 36px; + position: relative; + display: flex; + width: 100%; + height: $picker-list-height; + border: 1px solid #000; + $pickerview-top: calc(($picker-list-height - $picker-item-height) / 2); + overflow: hidden; + + &-mask, + &-indicator { + position: absolute; + left: 0; + right: 0; + z-index: 3; + pointer-events: none; + } + + &-mask { + top: 0; + bottom: 0; + background-image: $picker-mask-background; + background-position: top, bottom; + background-size: 100% $pickerview-top; + background-repeat: no-repeat; + transform: translateZ(0); + } + + &-indicator { + top: $pickerview-top; + height: $picker-item-height; + border: $picker-item-active-line-border; + border-left: 0; + border-right: 0; + box-sizing: border-box; + } + + &-list { + position: relative; + flex: 1; + height: $picker-list-height; + overflow: hidden; + touch-action: none; + } + + &-roller { + position: absolute; + top: $pickerview-top; + width: 100%; + height: $picker-item-height; + z-index: 1; + transform-style: preserve-3d; + } + + &-roller-item { + position: absolute; + top: 0; + backface-visibility: hidden; + -moz-backface-visibility: hidden; + -webkit-backface-visibility: hidden; + &-hidden { + visibility: hidden; + opacity: 0; + } + } + + &-roller-item, + &-roller-item-title { + width: 100%; + height: $picker-item-height; + line-height: $picker-item-height; + color: $picker-item-text-color; + font-size: $picker-item-text-font-size; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index 5db2fd07ad..d6f1540de1 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -1,28 +1,113 @@ -import React, { FunctionComponent } from 'react' +import React, { + ForwardRefRenderFunction, + useCallback, + useEffect, + useMemo, + useState, +} from 'react' import classNames from 'classnames' import { View } from '@tarojs/components' import { ComponentDefaults } from '@/utils/typings' -import { PickerViewProps } from './types' - -// import { useConfig } from '@/packages/configprovider/configprovider.taro' -// import { useRtl } from '@/packages/configprovider/index.taro' +import { PickerViewProps, PickerOptionItem, PickerValue } from './types' +import PickerRoller from './pickerroller.taro' +import { usePropsValue } from '@/utils/use-props-value' const defaultProps = { ...ComponentDefaults, + options: [], + defaultValue: [], + value: undefined, + renderLabel: (item: PickerOptionItem) => item.label, } as PickerViewProps -export const PickerView: FunctionComponent< - Partial & React.HTMLAttributes -> = (props) => { - // const { locale } = useConfig() - // const rtl = useRtl() - const { className, style } = { ...defaultProps, ...props } + +const InternalPickerView: ForwardRefRenderFunction< + unknown, + Partial +> = (props, ref) => { + const { + options, + defaultValue, + value, + duration, + threeDimensional, + renderLabel, + className, + style, + onChange, + } = { ...defaultProps, ...props } const classPrefix = 'nut-pickerview' const cls = classNames(classPrefix, className) + + const [selectedValue] = usePropsValue({ + value, + defaultValue, + finalValue: defaultValue, + }) + + const [innerValue, setInnerValue] = useState(selectedValue) + const [innerOptions, setInnerOptions] = useState(options) + + useEffect(() => { + if (selectedValue !== innerValue) { + setInnerValue(selectedValue) + } + }, [selectedValue]) + + useEffect(() => { + if (options !== innerOptions) { + setInnerOptions(options) + } + }, [options]) + + const handleSelect = useCallback( + (option: PickerOptionItem, index: number) => { + const newValue = option?.value + if (!newValue) return + setInnerValue((prev) => { + if (prev[index] === newValue) return prev + const next = [...prev] + next[index] = newValue + return next + }) + }, + [] + ) + + const selectedOptions = useMemo(() => { + return options.map((columnOptions, index) => { + const selectedOption = columnOptions.find( + (item) => item.value === innerValue[index] + ) + return selectedOption || columnOptions[0] // Fallback to the first option if not found + }) + }, [options, innerValue]) + + useEffect(() => { + onChange?.(innerValue, selectedOptions) + }, [innerValue, selectedOptions, onChange]) + return ( - PickerView + {innerOptions.map((item, index) => ( + + ))} + + ) } -PickerView.displayName = 'NutPickerView' +const PickerView = React.forwardRef>( + InternalPickerView +) + +export default PickerView diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index 0f33d583db..b94b9db13f 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -1,26 +1,112 @@ -import React, { FunctionComponent } from 'react' +import React, { + ForwardRefRenderFunction, + useCallback, + useEffect, + useMemo, + useState, +} from 'react' import classNames from 'classnames' import { ComponentDefaults } from '@/utils/typings' -import { PickerViewProps } from './types' -// import { useConfig } from '@/packages/configprovider' -// import { useRtl } from '@/packages/configprovider' +import { PickerViewProps, PickerOptionItem, PickerValue } from './types' +import PickerRoller from './pickerroller' +import { usePropsValue } from '@/utils/use-props-value' const defaultProps = { ...ComponentDefaults, + options: [], + defaultValue: [], + value: undefined, + renderLabel: (item: PickerOptionItem) => item.label, } as PickerViewProps -export const PickerView: FunctionComponent< - Partial & React.HTMLAttributes -> = (props) => { - // const { locale } = useConfig() - // const rtl = useRtl() - const { className, style } = { ...defaultProps, ...props } + +const InternalPickerView: ForwardRefRenderFunction< + unknown, + Partial +> = (props, ref) => { + const { + options, + defaultValue, + value, + duration, + threeDimensional, + renderLabel, + className, + style, + onChange, + } = { ...defaultProps, ...props } const classPrefix = 'nut-pickerview' const cls = classNames(classPrefix, className) + + const [selectedValue] = usePropsValue({ + value, + defaultValue, + finalValue: defaultValue, + }) + + const [innerValue, setInnerValue] = useState(selectedValue) + const [innerOptions, setInnerOptions] = useState(options) + + useEffect(() => { + if (selectedValue !== innerValue) { + setInnerValue(selectedValue) + } + }, [selectedValue]) + + useEffect(() => { + if (options !== innerOptions) { + setInnerOptions(options) + } + }, [options]) + + const handleSelect = useCallback( + (option: PickerOptionItem, index: number) => { + const newValue = option?.value + if (!newValue) return + setInnerValue((prev) => { + if (prev[index] === newValue) return prev + const next = [...prev] + next[index] = newValue + return next + }) + }, + [] + ) + + const selectedOptions = useMemo(() => { + return options.map((columnOptions, index) => { + const selectedOption = columnOptions.find( + (item) => item.value === innerValue[index] + ) + return selectedOption || columnOptions[0] // Fallback to the first option if not found + }) + }, [options, innerValue]) + + useEffect(() => { + onChange?.(innerValue, selectedOptions) + }, [innerValue, selectedOptions, onChange]) + return (
- PickerView + {innerOptions.map((item, index) => ( + + ))} +
+
) } -PickerView.displayName = 'NutPickerView' +const PickerView = React.forwardRef>( + InternalPickerView +) + +export default PickerView diff --git a/src/packages/pickerview/types.ts b/src/packages/pickerview/types.ts index a72b2c3b4e..a65deb939d 100644 --- a/src/packages/pickerview/types.ts +++ b/src/packages/pickerview/types.ts @@ -1,3 +1,33 @@ import { BasicComponent } from '@/utils/typings' -export interface PickerViewProps extends BasicComponent {} +export type PickerValue = string | number | null + +export interface PickerOptionItem { + label: string | number + value: string | number + disabled?: boolean + children?: PickerOptionItem[] + className?: string | number +} + +export type PickerOptions = PickerOptionItem[] + +export interface PickerRollerProps { + options: PickerOptionItem[] + keyIndex: number + value: PickerValue + threeDimensional?: boolean + duration?: number | string + onSelect: (option: PickerOptionItem, index: number) => void + renderLabel: (item: PickerOptionItem) => React.ReactNode +} + +export interface PickerViewProps extends BasicComponent { + options: PickerOptions[] + value?: PickerValue[] + defaultValue?: PickerValue[] + threeDimensional?: boolean + duration?: number | string + renderLabel: (item: PickerOptionItem) => React.ReactNode + onChange?: (value: PickerValue[], selectOptions: PickerOptionItem[]) => void +} diff --git a/src/styles/variables.scss b/src/styles/variables.scss index 99b31976c7..51a884d520 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -559,8 +559,10 @@ $picker-title-ok-font-size: var( --nutui-picker-title-ok-font-size, $font-size-base ) !default; -$picker-list-height: var(--nutui-picker-list-height, 252px) !default; + +// picker-view(✅) $picker-item-height: var(--nutui-picker-item-height, 36px) !default; +$picker-list-height: calc($picker-item-height * 7) !default; $picker-item-text-color: var( --nutui-picker-item-text-color, $color-title From 2e3a00aea69e3a82775a7728a0218382cd0533f6 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 22 Jan 2025 18:35:06 +0800 Subject: [PATCH 03/54] feat: add tiled demo --- src/packages/pickerview/demo.taro.tsx | 6 ++++ src/packages/pickerview/demo.tsx | 6 ++++ src/packages/pickerview/demos/h5/demo5.tsx | 35 ++++++++++++++++++++ src/packages/pickerview/demos/taro/demo5.tsx | 35 ++++++++++++++++++++ src/packages/pickerview/doc.md | 10 +++++- src/packages/pickerview/pickerview.scss | 1 - src/packages/pickerview/pickerview.taro.tsx | 2 +- src/packages/pickerview/pickerview.tsx | 2 +- 8 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 src/packages/pickerview/demos/h5/demo5.tsx create mode 100644 src/packages/pickerview/demos/taro/demo5.tsx diff --git a/src/packages/pickerview/demo.taro.tsx b/src/packages/pickerview/demo.taro.tsx index 9773e2cae5..d05584575b 100644 --- a/src/packages/pickerview/demo.taro.tsx +++ b/src/packages/pickerview/demo.taro.tsx @@ -7,6 +7,7 @@ import Demo1 from './demos/taro/demo1' import Demo2 from './demos/taro/demo2' import Demo3 from './demos/taro/demo3' import Demo4 from './demos/taro/demo4' +import Demo5 from './demos/taro/demo5' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -15,18 +16,21 @@ const PickerViewDemo = () => { adjustHeight: '自适应高度', multiColumn: '多列', controlled: '受控', + tiled: '平铺', }, 'en-US': { title: 'Basic Usage', adjustHeight: 'Adjust Height', multiColumn: 'Multi Column', controlled: 'Controlled', + tiled: 'Tiled', }, 'zh-TW': { title: '基礎用法', adjustHeight: '自適應高度', multiColumn: '多列', controlled: '受控', + tiled: '平鋪', }, }) return ( @@ -41,6 +45,8 @@ const PickerViewDemo = () => { {translated.multiColumn} + {translated.tiled} + ) diff --git a/src/packages/pickerview/demo.tsx b/src/packages/pickerview/demo.tsx index b45f73be58..99de463ecd 100644 --- a/src/packages/pickerview/demo.tsx +++ b/src/packages/pickerview/demo.tsx @@ -4,6 +4,7 @@ import Demo1 from './demos/h5/demo1' import Demo2 from './demos/h5/demo2' import Demo3 from './demos/h5/demo3' import Demo4 from './demos/h5/demo4' +import Demo5 from './demos/h5/demo5' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -12,18 +13,21 @@ const PickerViewDemo = () => { adjustHeight: '自适应高度', multiColumn: '多列', controlled: '受控', + tiled: '平铺', }, 'en-US': { title: 'Basic Usage', adjustHeight: 'Adjust Height', multiColumn: 'Multi Column', controlled: 'Controlled', + tiled: 'Tiled', }, 'zh-TW': { title: '基礎用法', adjustHeight: '自適應高度', multiColumn: '多列', controlled: '受控', + tiled: '平鋪', }, }) return ( @@ -36,6 +40,8 @@ const PickerViewDemo = () => {

{translated.multiColumn}

+

{translated.tiled}

+
) } diff --git a/src/packages/pickerview/demos/h5/demo5.tsx b/src/packages/pickerview/demos/h5/demo5.tsx new file mode 100644 index 0000000000..27741464db --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo5.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { PickerView, Cell } from '@nutui/nutui-react' + +const Demo1 = () => { + const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ] + + return ( + <> + + { + console.log('onChange', value, selectOptions) + }} + /> + + + ) +} +export default Demo1 diff --git a/src/packages/pickerview/demos/taro/demo5.tsx b/src/packages/pickerview/demos/taro/demo5.tsx new file mode 100644 index 0000000000..98cfbdbeca --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo5.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { PickerView, Cell } from '@nutui/nutui-react-taro' + +const Demo1 = () => { + const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ] + + return ( + <> + + { + console.log('onChange', value, selectOptions) + }} + /> + + + ) +} +export default Demo1 diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index 0069376561..bb230e0346 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -42,7 +42,15 @@ import { name } from '@nutui/nutui-react' ::: -## Picker +### 平铺 + +:::demo + + + +::: + +## PickerView ### Props diff --git a/src/packages/pickerview/pickerview.scss b/src/packages/pickerview/pickerview.scss index c4b3d1f29d..595e61d224 100644 --- a/src/packages/pickerview/pickerview.scss +++ b/src/packages/pickerview/pickerview.scss @@ -4,7 +4,6 @@ display: flex; width: 100%; height: $picker-list-height; - border: 1px solid #000; $pickerview-top: calc(($picker-list-height - $picker-item-height) / 2); overflow: hidden; diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index d6f1540de1..251a8c4d1d 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -78,7 +78,7 @@ const InternalPickerView: ForwardRefRenderFunction< const selectedOption = columnOptions.find( (item) => item.value === innerValue[index] ) - return selectedOption || columnOptions[0] // Fallback to the first option if not found + return selectedOption || columnOptions[0] }) }, [options, innerValue]) diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index b94b9db13f..e00e5f4d52 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -77,7 +77,7 @@ const InternalPickerView: ForwardRefRenderFunction< const selectedOption = columnOptions.find( (item) => item.value === innerValue[index] ) - return selectedOption || columnOptions[0] // Fallback to the first option if not found + return selectedOption || columnOptions[0] }) }, [options, innerValue]) From c651e623be4cc91039a2644671345acede2c2782 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Fri, 24 Jan 2025 15:05:19 +0800 Subject: [PATCH 04/54] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84h5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.json | 4 +- src/packages/picker/demo.tsx | 14 +- src/packages/picker/demos/h5/demo1.tsx | 55 ++-- src/packages/picker/demos/h5/demo2.tsx | 48 ++- src/packages/picker/demos/h5/demo3.tsx | 76 ++--- src/packages/picker/demos/h5/demo4.tsx | 18 +- src/packages/picker/demos/h5/demo5.tsx | 20 +- src/packages/picker/demos/h5/demo6.tsx | 44 +-- src/packages/picker/demos/h5/demo7.tsx | 41 ++- src/packages/picker/demos/h5/demo8.tsx | 20 +- src/packages/picker/demos/taro/demo1.tsx | 20 +- src/packages/picker/demos/taro/demo2.tsx | 20 +- src/packages/picker/demos/taro/demo3.tsx | 20 +- src/packages/picker/demos/taro/demo4.tsx | 18 +- src/packages/picker/demos/taro/demo5.tsx | 20 +- src/packages/picker/demos/taro/demo6.tsx | 44 +-- src/packages/picker/demos/taro/demo7.tsx | 28 +- src/packages/picker/demos/taro/demo8.tsx | 20 +- src/packages/picker/picker.tsx | 344 ++++++++++----------- src/packages/picker/pickerpanel.tsx | 305 ------------------ src/packages/picker/types.ts | 11 +- src/packages/pickerview/demos/h5/demo1.tsx | 4 +- src/packages/pickerview/demos/h5/demo2.tsx | 4 +- src/packages/pickerview/demos/h5/demo3.tsx | 4 +- src/packages/pickerview/demos/h5/demo4.tsx | 4 +- src/packages/pickerview/demos/h5/demo5.tsx | 4 +- src/packages/pickerview/index.taro.ts | 8 +- src/packages/pickerview/index.ts | 8 +- src/packages/pickerview/pickerview.tsx | 14 +- src/packages/pickerview/types.ts | 8 +- 30 files changed, 481 insertions(+), 767 deletions(-) delete mode 100644 src/packages/picker/pickerpanel.tsx diff --git a/src/config.json b/src/config.json index 15d33f6f26..4cee8900de 100644 --- a/src/config.json +++ b/src/config.json @@ -679,7 +679,7 @@ "dd": true }, { - "version": "2.0.0", + "version": "3.0.0", "name": "Picker", "type": "component", "cName": "选择器", @@ -688,7 +688,7 @@ "show": true, "taro": true, "author": "dsj", - "dd": false + "dd": true }, { "version": "3.0.0", diff --git a/src/packages/picker/demo.tsx b/src/packages/picker/demo.tsx index 9bc11fc9ff..6553975ff1 100644 --- a/src/packages/picker/demo.tsx +++ b/src/packages/picker/demo.tsx @@ -3,11 +3,11 @@ import { useTranslate } from '@/sites/assets/locale' import Demo1 from './demos/h5/demo1' import Demo2 from './demos/h5/demo2' import Demo3 from './demos/h5/demo3' -import Demo4 from './demos/h5/demo4' -import Demo5 from './demos/h5/demo5' -import Demo6 from './demos/h5/demo6' -import Demo7 from './demos/h5/demo7' -import Demo8 from './demos/h5/demo8' +// import Demo4 from './demos/h5/demo4' +// import Demo5 from './demos/h5/demo5' +// import Demo6 from './demos/h5/demo6' +// import Demo7 from './demos/h5/demo7' +// import Demo8 from './demos/h5/demo8' const PickerDemo = () => { const [translated] = useTranslate({ @@ -53,7 +53,7 @@ const PickerDemo = () => {

{translated.controlled}

{translated.multipleColumns}

- + {/*

{translated.tileDesc}

{translated.cascade}

@@ -61,7 +61,7 @@ const PickerDemo = () => {

{translated.async}

{translated.theme}

- + */}
) diff --git a/src/packages/picker/demos/h5/demo1.tsx b/src/packages/picker/demos/h5/demo1.tsx index b0c5f799ba..feba07dcd4 100644 --- a/src/packages/picker/demos/h5/demo1.tsx +++ b/src/packages/picker/demos/h5/demo1.tsx @@ -1,40 +1,41 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' - -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' +import { + PickerOnChangeCallbackParameter, + PickerOptionItem, +} from '@/packages/pickerview/types' const Demo1 = () => { const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const listData1 = [ + const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] - const changePicker = (list: any[], option: any, columnIndex: number) => { - console.log(columnIndex, option) + const changePicker = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('changePicker', value, index, selectedOptions) } const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { + console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: PickerOptionItem) => { + description += ` ${option.label}` }) setBaseDesc(description) } @@ -48,8 +49,8 @@ const Demo1 = () => { confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} onClose={() => setVisible(false)} onChange={changePicker} /> diff --git a/src/packages/picker/demos/h5/demo2.tsx b/src/packages/picker/demos/h5/demo2.tsx index 22b87f1203..643137b163 100644 --- a/src/packages/picker/demos/h5/demo2.tsx +++ b/src/packages/picker/demos/h5/demo2.tsx @@ -1,54 +1,46 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo2 = () => { const [visible, setVisible] = useState(false) - const [baseDefault, setbaseDefault] = useState('') + const [baseDesc, setBaseDesc] = useState('无锡市') const [defaultValue] = useState([2]) - - const listData1 = [ + const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: any) => { + description += ` ${option.label}` }) - setbaseDefault(description) + setBaseDesc(description) } return ( <> setVisible(!visible)} /> confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} onClose={() => setVisible(false)} /> diff --git a/src/packages/picker/demos/h5/demo3.tsx b/src/packages/picker/demos/h5/demo3.tsx index a4652af91d..d2b39e3440 100644 --- a/src/packages/picker/demos/h5/demo3.tsx +++ b/src/packages/picker/demos/h5/demo3.tsx @@ -1,59 +1,63 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' +import isEqual from 'react-fast-compare' +import { PickerOnChangeCallbackParameter } from '@/packages/pickerview/types' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo3 = () => { - const [isVisible, setIsVisible] = useState(false) + const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const [val, setVal] = useState>([]) + const [value, setValue] = useState([]) const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] + + const changePicker = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('changePicker', value, index, selectedOptions) + } const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` - }) - setBaseDesc(description) + if (isEqual(selectedValue, [3])) { + selectedValue = [1] + setBaseDesc('南京市') + } else { + let description = '' + selectedOptions.forEach((option: any) => { + description += ` ${option.label}` + }) + setBaseDesc(description) + } } return ( <> setIsVisible(!isVisible)} + onClick={() => setVisible(!visible)} /> { - confirmPicker(list, values) - setVal(values) - }} - onClose={() => { - setIsVisible(false) - }} + onChange={changePicker} + onConfirm={confirmPicker} + onClose={() => setVisible(false)} /> ) diff --git a/src/packages/picker/demos/h5/demo4.tsx b/src/packages/picker/demos/h5/demo4.tsx index b9594ffb6b..984fe74d67 100644 --- a/src/packages/picker/demos/h5/demo4.tsx +++ b/src/packages/picker/demos/h5/demo4.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -14,17 +14,17 @@ const Demo4 = () => { const listData2 = [ // 第一列 [ - { text: '周一', value: 'Monday' }, - { text: '周二', value: 'Tuesday' }, - { text: '周三', value: 'Wednesday' }, - { text: '周四', value: 'Thursday' }, - { text: '周五', value: 'Friday' }, + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, ], // 第二列 [ - { text: '上午', value: 'Morning' }, - { text: '下午', value: 'Afternoon' }, - { text: '晚上', value: 'Evening' }, + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, ], ] const confirmPicker = ( diff --git a/src/packages/picker/demos/h5/demo5.tsx b/src/packages/picker/demos/h5/demo5.tsx index aeb6a2f1e0..c9a8bdf80a 100644 --- a/src/packages/picker/demos/h5/demo5.tsx +++ b/src/packages/picker/demos/h5/demo5.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -14,15 +14,15 @@ const Demo5 = () => { const listData1 = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] diff --git a/src/packages/picker/demos/h5/demo6.tsx b/src/packages/picker/demos/h5/demo6.tsx index 007a1f8352..d81010ced5 100644 --- a/src/packages/picker/demos/h5/demo6.tsx +++ b/src/packages/picker/demos/h5/demo6.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -13,57 +13,57 @@ const Demo6 = () => { const customCityData = [ { value: 1, - text: '北京', + label: '北京', children: [ { value: 1, - text: '朝阳区', + label: '朝阳区', }, { value: 2, - text: '海淀区', + label: '海淀区', }, { value: 3, - text: '大兴区', + label: '大兴区', }, { value: 4, - text: '东城区', + label: '东城区', }, { value: 5, - text: '西城区', + label: '西城区', }, { value: 6, - text: '丰台区', + label: '丰台区', }, ], }, { value: 2, - text: '上海', + label: '上海', children: [ { value: 1, - text: '黄埔区', + label: '黄埔区', }, { value: 2, - text: '长宁区', + label: '长宁区', }, { value: 3, - text: '普陀区', + label: '普陀区', }, { value: 4, - text: '杨浦区', + label: '杨浦区', }, { value: 5, - text: '浦东新区', + label: '浦东新区', }, ], }, @@ -71,19 +71,19 @@ const Demo6 = () => { const [asyncData] = useState([ { value: 1, - text: '北京市', + label: '北京市', children: [ - { value: 1, text: '朝阳区' }, - { value: 2, text: '海淀区' }, - { value: 3, text: '大兴区' }, - { value: 4, text: '东城区' }, - { value: 5, text: '西城区' }, - { value: 6, text: '丰台区' }, + { value: 1, label: '朝阳区' }, + { value: 2, label: '海淀区' }, + { value: 3, label: '大兴区' }, + { value: 4, label: '东城区' }, + { value: 5, label: '西城区' }, + { value: 6, label: '丰台区' }, ], }, { value: 2, - text: '上海市', + label: '上海市', children: [], }, ]) diff --git a/src/packages/picker/demos/h5/demo7.tsx b/src/packages/picker/demos/h5/demo7.tsx index 1707e8c4aa..2db350c2fa 100644 --- a/src/packages/picker/demos/h5/demo7.tsx +++ b/src/packages/picker/demos/h5/demo7.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -14,19 +14,32 @@ const Demo7 = () => { const [asyncData, setAsyncData] = useState([ { value: 1, - text: '北京市', + label: '北京市', children: [ - { value: 1, text: '朝阳区' }, - { value: 2, text: '海淀区' }, - { value: 3, text: '大兴区' }, - { value: 4, text: '东城区' }, - { value: 5, text: '西城区' }, - { value: 6, text: '丰台区' }, + { + value: 1, + label: '朝阳区', + children: [ + { + value: 1, + label: '常营', + }, + { + value: 2, + label: '望京', + }, + ], + }, + { value: 2, label: '海淀区' }, + { value: 3, label: '大兴区' }, + { value: 4, label: '东城区' }, + { value: 5, label: '西城区' }, + { value: 6, label: '丰台区' }, ], }, { value: 2, - text: '上海市', + label: '上海市', children: [], }, ]) @@ -42,23 +55,23 @@ const Demo7 = () => { asyncData[1].children = [ { value: 1, - text: '黄埔区', + label: '黄埔区', }, { value: 2, - text: '长宁区', + label: '长宁区', }, { value: 3, - text: '普陀区', + label: '普陀区', }, { value: 4, - text: '杨浦区', + label: '杨浦区', }, { value: 5, - text: '浦东新区', + label: '浦东新区', }, ] setAsyncData([...asyncData]) diff --git a/src/packages/picker/demos/h5/demo8.tsx b/src/packages/picker/demos/h5/demo8.tsx index 493b5dce06..b44a1b9fa2 100644 --- a/src/packages/picker/demos/h5/demo8.tsx +++ b/src/packages/picker/demos/h5/demo8.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell, ConfigProvider } from '@nutui/nutui-react' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -12,15 +12,15 @@ const Demo8 = () => { const [isVisible, setIsVisible] = useState(false) const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] diff --git a/src/packages/picker/demos/taro/demo1.tsx b/src/packages/picker/demos/taro/demo1.tsx index f34f7003f3..fa97842a99 100644 --- a/src/packages/picker/demos/taro/demo1.tsx +++ b/src/packages/picker/demos/taro/demo1.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react-taro' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -14,15 +14,15 @@ const Demo1 = () => { const [baseDesc, setBaseDesc] = useState('') const listData1 = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] const changePicker = (list: any[], option: any, columnIndex: number) => { diff --git a/src/packages/picker/demos/taro/demo2.tsx b/src/packages/picker/demos/taro/demo2.tsx index ca6a3d4c09..058f1da314 100644 --- a/src/packages/picker/demos/taro/demo2.tsx +++ b/src/packages/picker/demos/taro/demo2.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react-taro' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -15,15 +15,15 @@ const Demo2 = () => { const listData1 = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] const confirmPicker = ( diff --git a/src/packages/picker/demos/taro/demo3.tsx b/src/packages/picker/demos/taro/demo3.tsx index f183117990..c7f8aee67e 100644 --- a/src/packages/picker/demos/taro/demo3.tsx +++ b/src/packages/picker/demos/taro/demo3.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react-taro' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -14,15 +14,15 @@ const Demo3 = () => { const [val, setVal] = useState>([]) const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] const confirmPicker = ( diff --git a/src/packages/picker/demos/taro/demo4.tsx b/src/packages/picker/demos/taro/demo4.tsx index 79992790c3..5461f2c801 100644 --- a/src/packages/picker/demos/taro/demo4.tsx +++ b/src/packages/picker/demos/taro/demo4.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react-taro' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -14,17 +14,17 @@ const Demo4 = () => { const listData2 = [ // 第一列 [ - { text: '周一', value: 'Monday' }, - { text: '周二', value: 'Tuesday' }, - { text: '周三', value: 'Wednesday' }, - { text: '周四', value: 'Thursday' }, - { text: '周五', value: 'Friday' }, + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, ], // 第二列 [ - { text: '上午', value: 'Morning' }, - { text: '下午', value: 'Afternoon' }, - { text: '晚上', value: 'Evening' }, + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, ], ] const confirmPicker = ( diff --git a/src/packages/picker/demos/taro/demo5.tsx b/src/packages/picker/demos/taro/demo5.tsx index df1c6c3d36..0b29374c37 100644 --- a/src/packages/picker/demos/taro/demo5.tsx +++ b/src/packages/picker/demos/taro/demo5.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react-taro' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -14,15 +14,15 @@ const Demo5 = () => { const listData1 = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] diff --git a/src/packages/picker/demos/taro/demo6.tsx b/src/packages/picker/demos/taro/demo6.tsx index 3c3e880622..2c46b1d3c2 100644 --- a/src/packages/picker/demos/taro/demo6.tsx +++ b/src/packages/picker/demos/taro/demo6.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react-taro' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -13,57 +13,57 @@ const Demo6 = () => { const customCityData = [ { value: 1, - text: '北京', + label: '北京', children: [ { value: 1, - text: '朝阳区', + label: '朝阳区', }, { value: 2, - text: '海淀区', + label: '海淀区', }, { value: 3, - text: '大兴区', + label: '大兴区', }, { value: 4, - text: '东城区', + label: '东城区', }, { value: 5, - text: '西城区', + label: '西城区', }, { value: 6, - text: '丰台区', + label: '丰台区', }, ], }, { value: 2, - text: '上海', + label: '上海', children: [ { value: 1, - text: '黄埔区', + label: '黄埔区', }, { value: 2, - text: '长宁区', + label: '长宁区', }, { value: 3, - text: '普陀区', + label: '普陀区', }, { value: 4, - text: '杨浦区', + label: '杨浦区', }, { value: 5, - text: '浦东新区', + label: '浦东新区', }, ], }, @@ -71,19 +71,19 @@ const Demo6 = () => { const [asyncData] = useState([ { value: 1, - text: '北京市', + label: '北京市', children: [ - { value: 1, text: '朝阳区' }, - { value: 2, text: '海淀区' }, - { value: 3, text: '大兴区' }, - { value: 4, text: '东城区' }, - { value: 5, text: '西城区' }, - { value: 6, text: '丰台区' }, + { value: 1, label: '朝阳区' }, + { value: 2, label: '海淀区' }, + { value: 3, label: '大兴区' }, + { value: 4, label: '东城区' }, + { value: 5, label: '西城区' }, + { value: 6, label: '丰台区' }, ], }, { value: 2, - text: '上海市', + label: '上海市', children: [], }, ]) diff --git a/src/packages/picker/demos/taro/demo7.tsx b/src/packages/picker/demos/taro/demo7.tsx index 2bd3af5785..c46c44fad2 100644 --- a/src/packages/picker/demos/taro/demo7.tsx +++ b/src/packages/picker/demos/taro/demo7.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell } from '@nutui/nutui-react-taro' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -14,19 +14,19 @@ const Demo7 = () => { const [asyncData, setAsyncData] = useState([ { value: 1, - text: '北京市', + label: '北京市', children: [ - { value: 1, text: '朝阳区' }, - { value: 2, text: '海淀区' }, - { value: 3, text: '大兴区' }, - { value: 4, text: '东城区' }, - { value: 5, text: '西城区' }, - { value: 6, text: '丰台区' }, + { value: 1, label: '朝阳区' }, + { value: 2, label: '海淀区' }, + { value: 3, label: '大兴区' }, + { value: 4, label: '东城区' }, + { value: 5, label: '西城区' }, + { value: 6, label: '丰台区' }, ], }, { value: 2, - text: '上海市', + label: '上海市', children: [], }, ]) @@ -42,23 +42,23 @@ const Demo7 = () => { asyncData[1].children = [ { value: 1, - text: '黄埔区', + label: '黄埔区', }, { value: 2, - text: '长宁区', + label: '长宁区', }, { value: 3, - text: '普陀区', + label: '普陀区', }, { value: 4, - text: '杨浦区', + label: '杨浦区', }, { value: 5, - text: '浦东新区', + label: '浦东新区', }, ] setAsyncData([...asyncData]) diff --git a/src/packages/picker/demos/taro/demo8.tsx b/src/packages/picker/demos/taro/demo8.tsx index 029c6a2d79..0f0e87c6be 100644 --- a/src/packages/picker/demos/taro/demo8.tsx +++ b/src/packages/picker/demos/taro/demo8.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Picker, Cell, ConfigProvider } from '@nutui/nutui-react-taro' interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -12,15 +12,15 @@ const Demo8 = () => { const [isVisible, setIsVisible] = useState(false) const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 2c780b6a96..671c0588f1 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -5,53 +5,54 @@ import React, { RefObject, ForwardRefRenderFunction, useImperativeHandle, + useMemo, } from 'react' import classNames from 'classnames' +import isEqual from 'react-fast-compare' +import { + PickerView, + PickerOptions, + PickerValue, + PickerOptionItem, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react' import { Popup, PopupProps } from '@/packages/popup/popup' import { SafeArea } from '@/packages/safearea/safearea' -import PickerPanel from './pickerpanel' import useRefs from '@/utils/use-refs' import { useConfig } from '@/packages/configprovider' -import { PickerOption } from './types' import { usePropsValue } from '@/utils/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' - -export type PickerActions = { - open: () => void - close: () => void -} +import { PickerActions, PickerRef } from './types' export interface PickerProps extends Omit { visible?: boolean | undefined title?: string - options: (PickerOption | PickerOption[])[] - value?: (number | string)[] - defaultValue?: (number | string)[] + options: PickerOptions | PickerOptions[] + value?: PickerValue[] + defaultValue?: PickerValue[] threeDimensional?: boolean duration: number | string closeOnOverlayClick: boolean + renderLabel?: (item: PickerOptionItem) => React.ReactNode + popupProps: Partial< Omit > onConfirm?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void onCancel?: () => void onClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptions[], + selectedValue: PickerValue[] ) => void afterClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedOptions: PickerOptions[], + selectedValue: PickerOptions[], pickerRef: RefObject ) => void - onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => void + onChange?: (args0: PickerOnChangeCallbackParameter) => void children?: any } @@ -59,14 +60,12 @@ const defaultProps = { ...ComponentDefaults, title: '', options: [], - value: [], + value: undefined, defaultValue: [], - threeDimensional: true, closeOnOverlayClick: true, - duration: 1000, } as unknown as PickerProps const InternalPicker: ForwardRefRenderFunction< - unknown, + PickerRef, Partial > = (props, ref) => { const { locale } = useConfig() @@ -91,31 +90,24 @@ const InternalPicker: ForwardRefRenderFunction< } = { ...defaultProps, ...props } const classPrefix = 'nut-picker' const classes = classNames(classPrefix, className) - const [selectedValue, setSelectedValue] = usePropsValue< - Array - >({ + const [selectedValue, setSelectedValue] = usePropsValue({ value: props.value, defaultValue: [...defaultValue], finalValue: [...defaultValue], - onChange: (val: (string | number)[]) => { - props.onConfirm?.(setSelectedOptions(), val) + onChange: (value: PickerValue[]) => { + props.onConfirm?.(selectedOptions, value) }, }) const [innerVisible, setInnerVisible] = usePropsValue({ value: props.visible, defaultValue: false, finalValue: false, - onChange: (val: boolean) => { - props.onClose?.(setSelectedOptions(), innerValue) + onChange: (v: boolean) => { + if (!v) { + props.onClose?.(selectedOptions, innerValue) + } }, }) - const [innerValue, setInnerValue] = useState(selectedValue) - const [columnIndex, setColumnIndex] = useState(0) // 选中列 - const pickerRef = useRef(null) - const [refs, setRefs] = useRefs() - const [columnsList, setColumnsList] = useState([]) // 格式化后每一列的数据 - const isConfirmEvent = useRef(false) - const actions: PickerActions = { open: () => { setInnerVisible(true) @@ -127,146 +119,130 @@ const InternalPicker: ForwardRefRenderFunction< useImperativeHandle(ref, () => actions) - // 级联数据格式化 - const formatCascade = ( - columns: PickerOption[], - values: (number | string)[] + const [innerValue, setInnerValue] = useState(selectedValue) + const [innerOptions, setInnerOptions] = useState([]) + const changeIndex = useRef(-1) + const isConfirmEvent = useRef(false) + const pickerRef = useRef(null) + const [refs, setRefs] = useRefs() + + const formatCascadeOptions = ( + options: PickerOptions, + values: PickerValue[] ) => { - const formatted: PickerOption[][] = [] - let columnOptions: PickerOption = { - text: '', - value: '', - children: columns, - } + if (!options.length || !values.length) return [] + + const formatted: PickerOptions[] = [] + let currentOptions: PickerOptions = options + + for (let i = 0; i < values.length; i++) { + const value = values[i] + const foundItem = currentOptions.find((item) => item.value === value) - let columnIndex = 0 - while (columnOptions && columnOptions.children) { - const options: PickerOption[] = columnOptions.children - const value = values[columnIndex] - let index = options.findIndex((columnItem) => columnItem.value === value) - if (index === -1) index = 0 - columnOptions = columnOptions.children[index] - columnIndex++ - formatted.push(options) + if (!foundItem) break // 如果未找到匹配项,终止循环 + + formatted.push(currentOptions) // 将当前层级的选项添加到结果中 + + if (foundItem.children) { + currentOptions = foundItem.children // 更新当前层级为子选项 + } else { + break // 如果没有子选项,终止循环 + } } + return formatted } - // 数据类型:多列、嵌套、单列 - const columnsType = () => { - const firstColumn: PickerOption | PickerOption[] = options[0] - if (firstColumn) { - if (Array.isArray(firstColumn)) { - return 'multiple' - } - if ('children' in firstColumn) { - return 'cascade' - } - } + /** + * 数据类型:多列、嵌套、单列 + */ + const columnsType = useMemo(() => { + const [firstColumn] = options + if (!firstColumn) return 'single' + if (Array.isArray(firstColumn)) return 'multiple' + if ('children' in firstColumn) return 'cascade' return 'single' - } + }, [options]) - // 传入的数据格式化 - const normalListData = (innerValue: any) => { - const type = columnsType() - switch (type) { - case 'multiple': + const formatOptions = useMemo( + (value = null) => { + if (columnsType === 'multiple') { return options - case 'cascade': - // 级联数据处理 - return formatCascade(options as PickerOption[], innerValue) - default: - return [options] - } - } - const init = () => { - const normalData: PickerOption[][] = normalListData( - innerValue - ) as PickerOption[][] - setColumnsList(normalData) - // 初始化默认选中数据 - const data: (string | number)[] = [] - normalData.length > 0 && - normalData.map((item) => { - item[0] && data.push(item[0].value) - return item - }) - if (!innerValue.length && innerValue.length === 0) { - setInnerValue([...data]) - } - } + } - useEffect(() => { - setInnerValue(innerValue !== selectedValue ? selectedValue : innerValue) - }, [innerVisible, selectedValue]) + if (columnsType === 'cascade') { + return formatCascadeOptions( + options as PickerOptions, + value || innerValue + ) + } + + return [options] + }, + [innerValue, options, columnsType] + ) useEffect(() => { if (innerVisible) { - init() + console.log('selectedValue变更', selectedValue) + setInnerValue(selectedValue) + setInnerOptions(formatOptions as PickerOptions[]) } - }, [options, innerVisible]) + }, [options, innerVisible, selectedValue, innerOptions]) - // 选中值进行修改 useEffect(() => { - onChange && onChange(setSelectedOptions(), innerValue, columnIndex) - }, [innerValue, columnsList]) + console.log('innerValue变更onChange', innerValue, innerVisible) + innerVisible && + onChange && + onChange({ + selectedOptions, + value: innerValue, + index: changeIndex.current, + }) + }, [innerValue, innerVisible]) - const setSelectedOptions = () => { - const options: PickerOption[] = [] - let currOptions = [] - columnsList.forEach((columnOptions: PickerOption[], index: number) => { - currOptions = columnOptions.filter( + const selectedOptions = useMemo(() => { + return options.map((columnOptions, index) => { + const selectedOption = columnOptions.find( (item) => item.value === innerValue[index] ) - if (currOptions[0]) { - options.push(currOptions[0]) - } else { - columnOptions[0] && options.push(columnOptions[0]) - } + return selectedOption || columnOptions[0] }) - return options - } + }, [innerOptions, innerValue]) - // 更新已选择数据 - const chooseItem = (columnOptions: PickerOption, columnIndex: number) => { + // 确保value值变更再返回 + const onChangeItem = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { const values: any = [] - const start = columnIndex - if (columnOptions && Object.keys(columnOptions).length) { - // 切换数据后,数据有变动才触发。 - if (values[columnIndex] !== columnOptions.value) { - if (columnsType() === 'cascade') { - values[columnIndex] = columnOptions.value || '' - while (columnOptions?.children?.[0]) { - values[columnIndex + 1] = columnOptions.children[0].value - columnIndex++ - columnOptions = columnOptions.children[0] - } - // 当前改变列的下一列 children 值为空 - if (columnOptions?.children?.length) { - values[columnIndex + 1] = '' - } - const combineResult = [ - ...innerValue.slice(0, start), - ...values.splice(start), - ] - setInnerValue(combineResult) - setColumnsList(normalListData(combineResult) as PickerOption[][]) - } else { - setInnerValue((data: (number | string)[]) => { - const cdata: (number | string)[] = [...data] - cdata[columnIndex] = Object.prototype.hasOwnProperty.call( - columnOptions, - 'value' - ) - ? columnOptions.value - : '' - return cdata - }) - } - setColumnIndex(columnIndex) + const start = index + if (isEqual(value, innerValue)) return + console.log('onChangeItem', value, innerValue, index, selectedOptions) + if (columnsType === 'cascade') { + values[value] = value.value || '' + while (value?.children?.[0]) { + values[value + 1] = value.children[0].value + value++ + value = value.children[0] + } + // 当前改变列的下一列 children 值为空 + if (value?.children?.length) { + values[value + 1] = '' } + const combineResult = [ + ...innerValue.slice(0, start), + ...values.splice(start), + ] + setInnerValue(combineResult) + // setInnerOptions(formatOptions(combineResult)) + } else { + setInnerValue(value) } + changeIndex.current = index } + const confirm = () => { let moving = false refs.forEach((ref: any) => { @@ -311,6 +287,32 @@ const InternalPicker: ForwardRefRenderFunction< ) } + console.log('innerValue渲染子组件', innerValue) + + const renderPickerElement = () => { + return ( +
+ {renderTitleBar()} + {typeof children !== 'function' && children} +
+ { + onChangeItem({ value, index, selectedOptions }) + }} + /> +
+
+ ) + } + return ( <> {typeof children === 'function' && children(selectedValue)} @@ -319,42 +321,20 @@ const InternalPicker: ForwardRefRenderFunction< visible={innerVisible} position="bottom" onOverlayClick={() => { - if (closeOnOverlayClick) { - props.onCancel?.() - setInnerVisible(false) - } + if (!closeOnOverlayClick) return + props.onCancel?.() + setInnerVisible(false) }} afterClose={() => { - afterClose?.(setSelectedOptions(), innerValue, pickerRef) + // afterClose?.(setSelectedOptions(), innerValue, pickerRef) }} > -
- {renderTitleBar()} - {typeof children !== 'function' && children} -
- {columnsList?.map((item, index) => { - return ( - - chooseItem(value, index) - } - duration={duration} - key={index} - keyIndex={index} - /> - ) - })} -
-
+ {renderPickerElement()} ) } -const Picker = React.forwardRef>(InternalPicker) +const Picker = React.forwardRef>(InternalPicker) export default Picker diff --git a/src/packages/picker/pickerpanel.tsx b/src/packages/picker/pickerpanel.tsx deleted file mode 100644 index 15e66ce773..0000000000 --- a/src/packages/picker/pickerpanel.tsx +++ /dev/null @@ -1,305 +0,0 @@ -import React, { - useState, - useEffect, - useRef, - ForwardRefRenderFunction, - useImperativeHandle, -} from 'react' -import { PickerOption } from './types' -import { useTouch } from '@/utils/use-touch' -import { passiveSupported } from '@/utils/supports-passive' - -interface PickerPanelProps { - keyIndex?: number - defaultValue?: string | number - options?: PickerOption[] - threeDimensional: boolean - duration: number | string - chooseItem?: (val: PickerOption, idx: number) => void -} - -const InternalPickerPanel: ForwardRefRenderFunction< - { stopMomentum: () => void; moving: boolean }, - Partial -> = (props, ref) => { - const { - keyIndex = 0, - defaultValue, - options = [], - threeDimensional = true, - duration = 1000, - chooseItem, - } = props - - const touch = useTouch() - const DEFAULT_DURATION = 200 - // 触发惯性滑动条件: - // 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move - // 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动 - const INERTIA_TIME = 300 - const INERTIA_DISTANCE = 15 - const [currIndex, setCurrIndex] = useState(1) - const lineSpacing = 36 - const [touchTime, setTouchTime] = useState(0) - const [touchDeg, setTouchDeg] = useState('0deg') - const rotation = 20 - const moving = useRef(false) - let timer: number | undefined - - const rollerRef = useRef(null) - const PickerPanelRef = useRef(null) - - const [startTime, setStartTime] = useState(0) - const [startY, setStartY] = useState(0) - - const transformY = useRef(0) - const [scrollDistance, setScrollDistance] = useState(0) - - const isHidden = (index: number) => { - if (index >= currIndex + 8 || index <= currIndex - 8) { - return true - } - return false - } - - const setTransform = ( - type: string, - deg: string, - time = DEFAULT_DURATION, - translateY = 0 - ) => { - let nTime = time - if (type !== 'end') { - nTime = 0 - } - setTouchTime(nTime) - setTouchDeg(deg) - setScrollDistance(translateY) - } - - const setMove = (move: number, type?: string, time?: number) => { - let updateMove = move + transformY.current - if (type === 'end') { - // 限定滚动距离 - if (updateMove > 0) { - updateMove = 0 - } - if (updateMove < -(options.length - 1) * lineSpacing) { - updateMove = -(options.length - 1) * lineSpacing - } - - // 设置滚动距离为lineSpacing的倍数值 - const endMove = Math.round(updateMove / lineSpacing) * lineSpacing - const deg = `${ - (Math.abs(Math.round(endMove / lineSpacing)) + 1) * rotation - }deg` - - setTransform(type, deg, time, endMove) - setCurrIndex(Math.abs(Math.round(endMove / lineSpacing)) + 1) - } else { - let deg = 0 - const currentDeg = (-updateMove / lineSpacing + 1) * rotation - - // picker 滚动的最大角度 - const maxDeg = (options.length + 1) * rotation - const minDeg = 0 - - deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - - if (minDeg < deg && deg < maxDeg) { - setTransform('', `${deg}deg`, undefined, updateMove) - setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing)) + 1) - } - } - } - - const setChooseValue = (move: number) => { - chooseItem?.(options?.[Math.round(-move / lineSpacing)], keyIndex) - } - - // 开始滚动 - const touchStart = (event: React.TouchEvent) => { - touch.start(event) - setStartY(touch.deltaY.current) - setStartTime(Date.now()) - transformY.current = scrollDistance - } - - const touchMove = (event: React.TouchEvent) => { - touch.move(event) - if ((touch as any).isVertical) { - moving.current = true - preventDefault(event, true) - } - const move = touch.deltaY.current - startY - setMove(move) - } - - const touchEnd = () => { - if (!moving.current) return - const move = touch.deltaY.current - startY - const moveTime = Date.now() - startTime - // 区分是否为惯性滚动 - if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { - // 惯性滚动 - const distance = momentum(move, moveTime) - setMove(distance, 'end', +duration) - } else { - setMove(move, 'end') - } - setTimeout(() => { - touch.reset() - }, 0) - } - - // 惯性滚动 距离 - const momentum = (distance: number, duration: number) => { - let nDistance = distance - // 惯性滚动的速度 - const speed = Math.abs(nDistance / duration) - // 惯性滚动的距离 - nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) - return nDistance - } - - const modifyStatus = (type?: boolean, val?: string | number) => { - const value = val || defaultValue - let index = -1 - if (value) { - options.some((item, idx) => { - if (item.value === value) { - index = idx - return true - } - return false - }) - } else { - options.forEach((item, i) => { - if (item.value === defaultValue) { - index = i - } - }) - } - - setCurrIndex(index === -1 ? 1 : index + 1) - const move = index === -1 ? 0 : index * lineSpacing - type && setChooseValue(-move) - setMove(-move) - } - - // 惯性滚动结束 - const stopMomentum = () => { - moving.current = false - setTouchTime(0) - setChooseValue(scrollDistance) - } - // 阻止默认事件 - const preventDefault = ( - event: React.TouchEvent, - isStopPropagation?: boolean - ) => { - /* istanbul ignore else */ - if (typeof event.cancelable !== 'boolean' || event.cancelable) { - event.preventDefault() - } - - if (isStopPropagation) { - event.stopPropagation() - } - } - - const touchRollerStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `rotate3d(1, 0, 0, ${touchDeg})`, - } - } - const touchTileStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `translate3d(0, ${scrollDistance}px, 0)`, - } - } - - useEffect(() => { - setScrollDistance(0) - transformY.current = 0 - modifyStatus(false) - return () => { - clearTimeout(timer) - } - }, [options]) - - useImperativeHandle(ref, () => ({ - stopMomentum, - moving: moving.current, - })) - - useEffect(() => { - const options = passiveSupported ? { passive: false } : false - PickerPanelRef.current?.addEventListener('touchstart', touchStart, options) - PickerPanelRef.current?.addEventListener('touchmove', touchMove, options) - PickerPanelRef.current?.addEventListener('touchend', touchEnd, options) - return () => { - PickerPanelRef.current?.removeEventListener('touchstart', touchStart) - PickerPanelRef.current?.removeEventListener('touchmove', touchMove) - PickerPanelRef.current?.removeEventListener('touchend', touchEnd) - } - }) - - return ( -
-
- {/* 3D 效果 */} - {threeDimensional && - options.map((item, index) => { - return ( -
- <>{item.text} -
- ) - })} - {/* 平铺 */} - {!threeDimensional && - options.map((item, index) => { - return ( -
- <>{item.text} -
- ) - })} -
-
-
-
- ) -} -const PickerPanel = React.forwardRef< - { stopMomentum: () => void; moving: boolean }, - Partial ->(InternalPickerPanel) -export default PickerPanel diff --git a/src/packages/picker/types.ts b/src/packages/picker/types.ts index 5c7e00b846..ae6fabdd59 100644 --- a/src/packages/picker/types.ts +++ b/src/packages/picker/types.ts @@ -1,7 +1,6 @@ -export interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number +export type PickerRef = PickerActions +export type PickerActions = { + open: () => void + close: () => void } +export type ColumnsType = 'single' | 'multiple' | 'cascade' diff --git a/src/packages/pickerview/demos/h5/demo1.tsx b/src/packages/pickerview/demos/h5/demo1.tsx index cacf341036..dbadb8c5a7 100644 --- a/src/packages/pickerview/demos/h5/demo1.tsx +++ b/src/packages/pickerview/demos/h5/demo1.tsx @@ -22,8 +22,8 @@ const Demo1 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/demos/h5/demo2.tsx b/src/packages/pickerview/demos/h5/demo2.tsx index dd16c8a92e..dff4a404b0 100644 --- a/src/packages/pickerview/demos/h5/demo2.tsx +++ b/src/packages/pickerview/demos/h5/demo2.tsx @@ -23,8 +23,8 @@ const Demo1 = () => { style={{ '--nutui-picker-item-height': '28px' }} options={listData} defaultValue={[1]} - onChange={(value, selectOptions) => { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/demos/h5/demo3.tsx b/src/packages/pickerview/demos/h5/demo3.tsx index 555c655e39..d23f719786 100644 --- a/src/packages/pickerview/demos/h5/demo3.tsx +++ b/src/packages/pickerview/demos/h5/demo3.tsx @@ -25,8 +25,8 @@ const Demo3 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) if (isEqual(value, ['Tuesday', 'Afternoon'])) { setValue(['Monday', 'Evening']) } diff --git a/src/packages/pickerview/demos/h5/demo4.tsx b/src/packages/pickerview/demos/h5/demo4.tsx index e205b5513a..320c7cc39e 100644 --- a/src/packages/pickerview/demos/h5/demo4.tsx +++ b/src/packages/pickerview/demos/h5/demo4.tsx @@ -24,8 +24,8 @@ const Demo1 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) if (value[0] === 3) { setValue([1]) } diff --git a/src/packages/pickerview/demos/h5/demo5.tsx b/src/packages/pickerview/demos/h5/demo5.tsx index 27741464db..4bf68a2261 100644 --- a/src/packages/pickerview/demos/h5/demo5.tsx +++ b/src/packages/pickerview/demos/h5/demo5.tsx @@ -24,8 +24,8 @@ const Demo1 = () => { options={listData} threeDimensional={false} duration={500} - onChange={(value, selectOptions) => { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/index.taro.ts b/src/packages/pickerview/index.taro.ts index b42ce6bc9a..3e1bff3e72 100644 --- a/src/packages/pickerview/index.taro.ts +++ b/src/packages/pickerview/index.taro.ts @@ -1,4 +1,10 @@ import PickerView from './pickerview.taro' -export type { PickerViewProps } from './types' +export type { + PickerViewProps, + PickerOptionItem, + PickerRollerProps, + PickerValue, + PickerOptions, +} from './types' export default PickerView diff --git a/src/packages/pickerview/index.ts b/src/packages/pickerview/index.ts index 47b8834afe..2d382fd322 100644 --- a/src/packages/pickerview/index.ts +++ b/src/packages/pickerview/index.ts @@ -1,4 +1,10 @@ import PickerView from './pickerview' -export type { PickerViewProps } from './types' +export type { + PickerViewProps, + PickerOptionItem, + PickerRollerProps, + PickerValue, + PickerOptions, +} from './types' export default PickerView diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index e00e5f4d52..427ecb33b9 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo, + useRef, useState, } from 'react' import classNames from 'classnames' @@ -45,6 +46,7 @@ const InternalPickerView: ForwardRefRenderFunction< const [innerValue, setInnerValue] = useState(selectedValue) const [innerOptions, setInnerOptions] = useState(options) + const changeIndex = useRef(0) useEffect(() => { if (selectedValue !== innerValue) { @@ -64,6 +66,7 @@ const InternalPickerView: ForwardRefRenderFunction< if (!newValue) return setInnerValue((prev) => { if (prev[index] === newValue) return prev + changeIndex.current = index const next = [...prev] next[index] = newValue return next @@ -82,7 +85,16 @@ const InternalPickerView: ForwardRefRenderFunction< }, [options, innerValue]) useEffect(() => { - onChange?.(innerValue, selectedOptions) + console.log('onChange', { + value: innerValue, + index: changeIndex.current, + selectedOptions, + }) + onChange?.({ + value: innerValue, + index: changeIndex.current, + selectedOptions, + }) }, [innerValue, selectedOptions, onChange]) return ( diff --git a/src/packages/pickerview/types.ts b/src/packages/pickerview/types.ts index a65deb939d..98a7869b5e 100644 --- a/src/packages/pickerview/types.ts +++ b/src/packages/pickerview/types.ts @@ -22,6 +22,12 @@ export interface PickerRollerProps { renderLabel: (item: PickerOptionItem) => React.ReactNode } +export interface PickerOnChangeCallbackParameter { + value: PickerValue[] + index: number + selectedOptions: PickerOptionItem[] +} + export interface PickerViewProps extends BasicComponent { options: PickerOptions[] value?: PickerValue[] @@ -29,5 +35,5 @@ export interface PickerViewProps extends BasicComponent { threeDimensional?: boolean duration?: number | string renderLabel: (item: PickerOptionItem) => React.ReactNode - onChange?: (value: PickerValue[], selectOptions: PickerOptionItem[]) => void + onChange?: (arg0: PickerOnChangeCallbackParameter) => void } From e60df8e6846dc835fd1ae79354392baae015ad69 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Fri, 24 Jan 2025 18:42:29 +0800 Subject: [PATCH 05/54] =?UTF-8?q?fix:=20=E9=80=82=E9=85=8Ddemo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/picker/demo.tsx | 14 ++++---- src/packages/picker/demos/h5/demo1.tsx | 7 ++-- src/packages/picker/demos/h5/demo3.tsx | 5 +-- src/packages/picker/demos/h5/demo4.tsx | 37 +++++++++------------ src/packages/picker/demos/h5/demo5.tsx | 25 ++++---------- src/packages/picker/demos/h5/demo8.tsx | 45 ++++++++++++-------------- src/packages/picker/picker.tsx | 2 +- src/packages/pickerview/index.taro.ts | 1 + src/packages/pickerview/index.ts | 1 + src/packages/pickerview/types.ts | 2 -- 10 files changed, 61 insertions(+), 78 deletions(-) diff --git a/src/packages/picker/demo.tsx b/src/packages/picker/demo.tsx index 6553975ff1..5572eaf6a8 100644 --- a/src/packages/picker/demo.tsx +++ b/src/packages/picker/demo.tsx @@ -3,11 +3,11 @@ import { useTranslate } from '@/sites/assets/locale' import Demo1 from './demos/h5/demo1' import Demo2 from './demos/h5/demo2' import Demo3 from './demos/h5/demo3' -// import Demo4 from './demos/h5/demo4' -// import Demo5 from './demos/h5/demo5' +import Demo4 from './demos/h5/demo4' +import Demo5 from './demos/h5/demo5' // import Demo6 from './demos/h5/demo6' // import Demo7 from './demos/h5/demo7' -// import Demo8 from './demos/h5/demo8' +import Demo8 from './demos/h5/demo8' const PickerDemo = () => { const [translated] = useTranslate({ @@ -53,15 +53,15 @@ const PickerDemo = () => {

{translated.controlled}

{translated.multipleColumns}

- {/* +

{translated.tileDesc}

-

{translated.cascade}

+ {/*

{translated.cascade}

{translated.async}

- + */}

{translated.theme}

- */} +
) diff --git a/src/packages/picker/demos/h5/demo1.tsx b/src/packages/picker/demos/h5/demo1.tsx index feba07dcd4..b1d08573ab 100644 --- a/src/packages/picker/demos/h5/demo1.tsx +++ b/src/packages/picker/demos/h5/demo1.tsx @@ -1,9 +1,12 @@ import React, { useState } from 'react' -import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' import { + Picker, + Cell, + PickerOptions, + PickerValue, PickerOnChangeCallbackParameter, PickerOptionItem, -} from '@/packages/pickerview/types' +} from '@nutui/nutui-react' const Demo1 = () => { const [visible, setVisible] = useState(false) diff --git a/src/packages/picker/demos/h5/demo3.tsx b/src/packages/picker/demos/h5/demo3.tsx index d2b39e3440..81f59dcdfb 100644 --- a/src/packages/picker/demos/h5/demo3.tsx +++ b/src/packages/picker/demos/h5/demo3.tsx @@ -6,7 +6,7 @@ import { PickerOnChangeCallbackParameter } from '@/packages/pickerview/types' const Demo3 = () => { const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const [value, setValue] = useState([]) + const [value, setValue] = useState([] as PickerValue[]) const options = [ [ { value: 1, label: '南京市' }, @@ -33,9 +33,10 @@ const Demo3 = () => { selectedValue: PickerValue[] ) => { if (isEqual(selectedValue, [3])) { - selectedValue = [1] + setValue([1]) setBaseDesc('南京市') } else { + setValue(selectedValue) let description = '' selectedOptions.forEach((option: any) => { description += ` ${option.label}` diff --git a/src/packages/picker/demos/h5/demo4.tsx b/src/packages/picker/demos/h5/demo4.tsx index 984fe74d67..16192241fd 100644 --- a/src/packages/picker/demos/h5/demo4.tsx +++ b/src/packages/picker/demos/h5/demo4.tsx @@ -1,17 +1,11 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo4 = () => { - const [isVisible2, setIsVisible2] = useState(false) - const [mutilDesc, setMutilDesc] = useState('') - const listData2 = [ + const [visible, setVisible] = useState(false) + const [mutilDesc, setMutilDesc] = useState('周三') + const [defaultValue] = useState(['Wednesday']) + const options = [ // 第一列 [ { label: '周一', value: 'Monday' }, @@ -28,12 +22,13 @@ const Demo4 = () => { ], ] const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { + console.log('confirmPicker', selectedOptions) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: any) => { + option?.label && (description += ` ${option.label}`) }) setMutilDesc(description) } @@ -42,14 +37,14 @@ const Demo4 = () => { setIsVisible2(!isVisible2)} + onClick={() => setVisible(!visible)} /> setIsVisible2(false)} - defaultValue={['Wednesday']} - onConfirm={(list, values) => confirmPicker(list, values)} + visible={visible} + options={options} + onClose={() => setVisible(false)} + defaultValue={defaultValue} + onConfirm={confirmPicker} /> ) diff --git a/src/packages/picker/demos/h5/demo5.tsx b/src/packages/picker/demos/h5/demo5.tsx index c9a8bdf80a..73a72fd091 100644 --- a/src/packages/picker/demos/h5/demo5.tsx +++ b/src/packages/picker/demos/h5/demo5.tsx @@ -1,18 +1,10 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo5 = () => { - const [tileDesc, settileDesc] = useState('') const [isVisible, setIsVisible] = useState(false) - - const listData1 = [ + const [tileDesc, settileDesc] = useState('无锡市') + const options = [ [ { value: 1, label: '南京市' }, { value: 2, label: '无锡市' }, @@ -26,13 +18,10 @@ const Demo5 = () => { ], ] - const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] - ) => { + const confirmPicker = (options: PickerOptions, values: PickerValue[]) => { let description = '' options.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) settileDesc(description) setIsVisible(false) @@ -46,8 +35,8 @@ const Demo5 = () => { /> confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} defaultValue={[2]} threeDimensional={false} duration={1000} diff --git a/src/packages/picker/demos/h5/demo8.tsx b/src/packages/picker/demos/h5/demo8.tsx index b44a1b9fa2..c96d49c3a4 100644 --- a/src/packages/picker/demos/h5/demo8.tsx +++ b/src/packages/picker/demos/h5/demo8.tsx @@ -1,15 +1,16 @@ import React, { useState } from 'react' -import { Picker, Cell, ConfigProvider } from '@nutui/nutui-react' +import { + Picker, + Cell, + ConfigProvider, + PickerOptionItem, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo8 = () => { - const [isVisible, setIsVisible] = useState(false) + const [visible, setVisible] = useState(false) + const [baseDesc, setBaseDesc] = useState('') const options = [ [ { value: 1, label: '南京市' }, @@ -24,27 +25,23 @@ const Demo8 = () => { ], ] - const [baseDesc, setBaseDesc] = useState('') - const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - console.log('demo 确定', options, values) + console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: PickerOptionItem) => { + description += ` ${option.label}` }) setBaseDesc(description) - setIsVisible(false) } - return ( <> setIsVisible(!isVisible)} + onClick={() => setVisible(!visible)} /> { }} > confirmPicker(list, values)} - onClose={() => { - setIsVisible(false) - console.log('onclose') - }} + onConfirm={confirmPicker} + onClose={() => setVisible(false)} /> diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 671c0588f1..3ecd3f707f 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -206,7 +206,7 @@ const InternalPicker: ForwardRefRenderFunction< const selectedOption = columnOptions.find( (item) => item.value === innerValue[index] ) - return selectedOption || columnOptions[0] + return selectedOption || {} }) }, [innerOptions, innerValue]) diff --git a/src/packages/pickerview/index.taro.ts b/src/packages/pickerview/index.taro.ts index 3e1bff3e72..1bd5f2a397 100644 --- a/src/packages/pickerview/index.taro.ts +++ b/src/packages/pickerview/index.taro.ts @@ -6,5 +6,6 @@ export type { PickerRollerProps, PickerValue, PickerOptions, + PickerOnChangeCallbackParameter, } from './types' export default PickerView diff --git a/src/packages/pickerview/index.ts b/src/packages/pickerview/index.ts index 2d382fd322..15c781a272 100644 --- a/src/packages/pickerview/index.ts +++ b/src/packages/pickerview/index.ts @@ -6,5 +6,6 @@ export type { PickerRollerProps, PickerValue, PickerOptions, + PickerOnChangeCallbackParameter, } from './types' export default PickerView diff --git a/src/packages/pickerview/types.ts b/src/packages/pickerview/types.ts index 98a7869b5e..e0448f662d 100644 --- a/src/packages/pickerview/types.ts +++ b/src/packages/pickerview/types.ts @@ -5,9 +5,7 @@ export type PickerValue = string | number | null export interface PickerOptionItem { label: string | number value: string | number - disabled?: boolean children?: PickerOptionItem[] - className?: string | number } export type PickerOptions = PickerOptionItem[] From 43fc55a28e7f6aca50c6293b6c90efe62757e550 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Sun, 26 Jan 2025 20:19:09 +0800 Subject: [PATCH 06/54] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=BA=A7?= =?UTF-8?q?=E8=81=94=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/picker/demo.tsx | 6 +- src/packages/picker/demos/h5/demo6.tsx | 168 ++++++++++------------- src/packages/picker/picker.tsx | 72 ++-------- src/packages/pickerview/pickerroller.tsx | 5 +- src/packages/pickerview/pickerview.tsx | 116 +++++++++++++--- 5 files changed, 192 insertions(+), 175 deletions(-) diff --git a/src/packages/picker/demo.tsx b/src/packages/picker/demo.tsx index 5572eaf6a8..a52112eda3 100644 --- a/src/packages/picker/demo.tsx +++ b/src/packages/picker/demo.tsx @@ -5,7 +5,7 @@ import Demo2 from './demos/h5/demo2' import Demo3 from './demos/h5/demo3' import Demo4 from './demos/h5/demo4' import Demo5 from './demos/h5/demo5' -// import Demo6 from './demos/h5/demo6' +import Demo6 from './demos/h5/demo6' // import Demo7 from './demos/h5/demo7' import Demo8 from './demos/h5/demo8' @@ -56,9 +56,9 @@ const PickerDemo = () => {

{translated.tileDesc}

- {/*

{translated.cascade}

+

{translated.cascade}

-

{translated.async}

+ {/*

{translated.async}

*/}

{translated.theme}

diff --git a/src/packages/picker/demos/h5/demo6.tsx b/src/packages/picker/demos/h5/demo6.tsx index d81010ced5..929d1a4d65 100644 --- a/src/packages/picker/demos/h5/demo6.tsx +++ b/src/packages/picker/demos/h5/demo6.tsx @@ -1,101 +1,82 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo6 = () => { const [isVisible, setIsVisible] = useState(false) + const [value, setValue] = useState([2]) + const [cityCustom, setCityCustom] = useState('上海') const customCityData = [ - { - value: 1, - label: '北京', - children: [ - { - value: 1, - label: '朝阳区', - }, - { - value: 2, - label: '海淀区', - }, - { - value: 3, - label: '大兴区', - }, - { - value: 4, - label: '东城区', - }, - { - value: 5, - label: '西城区', - }, - { - value: 6, - label: '丰台区', - }, - ], - }, - { - value: 2, - label: '上海', - children: [ - { - value: 1, - label: '黄埔区', - }, - { - value: 2, - label: '长宁区', - }, - { - value: 3, - label: '普陀区', - }, - { - value: 4, - label: '杨浦区', - }, - { - value: 5, - label: '浦东新区', - }, - ], - }, + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], ] - const [asyncData] = useState([ - { - value: 1, - label: '北京市', - children: [ - { value: 1, label: '朝阳区' }, - { value: 2, label: '海淀区' }, - { value: 3, label: '大兴区' }, - { value: 4, label: '东城区' }, - { value: 5, label: '西城区' }, - { value: 6, label: '丰台区' }, - ], - }, - { - value: 2, - label: '上海市', - children: [], - }, - ]) - const [cityCustom, setCityCustom] = useState('') const setChooseValueCustom = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - const str = options.map((item) => item.text).join('-') - setCityCustom(str) + console.log('onconfirm', selectedOptions, selectedValue) + const str = selectedOptions.map((item) => item.label).join('-') + setCityCustom(selectedValue.join('-')) + setValue(selectedValue) } + + console.log('demo6===>value', value) return ( <> { setIsVisible(false)} - onConfirm={(list, values) => setChooseValueCustom(list, values)} - onChange={( - options: PickerOption[], - value: (string | number)[], - columnIndex: number - ) => console.log(asyncData, '多级联动', columnIndex, value, options)} + onConfirm={setChooseValueCustom} + onChange={({ value, index, selectedOptions }) => + console.log('多级联动', value, index, selectedOptions) + } /> ) diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 3ecd3f707f..6b05d39633 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -27,7 +27,7 @@ import { PickerActions, PickerRef } from './types' export interface PickerProps extends Omit { visible?: boolean | undefined title?: string - options: PickerOptions | PickerOptions[] + options: PickerOptions[] value?: PickerValue[] defaultValue?: PickerValue[] threeDimensional?: boolean @@ -44,11 +44,11 @@ export interface PickerProps extends Omit { ) => void onCancel?: () => void onClose?: ( - selectedOptions: PickerOptions[], + selectedOptions: PickerOptions, selectedValue: PickerValue[] ) => void afterClose?: ( - selectedOptions: PickerOptions[], + selectedOptions: PickerOptions, selectedValue: PickerOptions[], pickerRef: RefObject ) => void @@ -153,40 +153,11 @@ const InternalPicker: ForwardRefRenderFunction< return formatted } - /** - * 数据类型:多列、嵌套、单列 - */ - const columnsType = useMemo(() => { - const [firstColumn] = options - if (!firstColumn) return 'single' - if (Array.isArray(firstColumn)) return 'multiple' - if ('children' in firstColumn) return 'cascade' - return 'single' - }, [options]) - - const formatOptions = useMemo( - (value = null) => { - if (columnsType === 'multiple') { - return options - } - - if (columnsType === 'cascade') { - return formatCascadeOptions( - options as PickerOptions, - value || innerValue - ) - } - - return [options] - }, - [innerValue, options, columnsType] - ) - useEffect(() => { if (innerVisible) { console.log('selectedValue变更', selectedValue) setInnerValue(selectedValue) - setInnerOptions(formatOptions as PickerOptions[]) + setInnerOptions(options as PickerOptions[]) } }, [options, innerVisible, selectedValue, innerOptions]) @@ -202,12 +173,18 @@ const InternalPicker: ForwardRefRenderFunction< }, [innerValue, innerVisible]) const selectedOptions = useMemo(() => { - return options.map((columnOptions, index) => { - const selectedOption = columnOptions.find( + console.log('selectedOptions', innerOptions) + const options: PickerOptions = [] + let currOptions = [] + innerOptions.forEach((columnOptions: PickerOptions, index: number) => { + currOptions = columnOptions.filter( (item) => item.value === innerValue[index] ) - return selectedOption || {} + if (currOptions[0]) { + options.push(currOptions[0]) + } }) + return options }, [innerOptions, innerValue]) // 确保value值变更再返回 @@ -216,30 +193,9 @@ const InternalPicker: ForwardRefRenderFunction< index, selectedOptions, }: PickerOnChangeCallbackParameter) => { - const values: any = [] - const start = index if (isEqual(value, innerValue)) return console.log('onChangeItem', value, innerValue, index, selectedOptions) - if (columnsType === 'cascade') { - values[value] = value.value || '' - while (value?.children?.[0]) { - values[value + 1] = value.children[0].value - value++ - value = value.children[0] - } - // 当前改变列的下一列 children 值为空 - if (value?.children?.length) { - values[value + 1] = '' - } - const combineResult = [ - ...innerValue.slice(0, start), - ...values.splice(start), - ] - setInnerValue(combineResult) - // setInnerOptions(formatOptions(combineResult)) - } else { - setInnerValue(value) - } + setInnerValue(value) changeIndex.current = index } diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 604cf97476..7f63b62761 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -108,7 +108,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< const minDeg = 0 deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - if (minDeg < deg && deg < maxDeg) { setTransform('', `${deg}deg`, undefined, updateMove) setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) @@ -187,7 +186,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< setCurrIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current type && setChooseValue(-move) - console.log(index, move, 'props.value.index') setMove(-move) } @@ -215,8 +213,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< setScrollDistance(0) transformY.current = 0 modifyStatus(false) - console.log('modifyStatus', props.value) - }, [options]) + }, [options, props.value]) useImperativeHandle(ref, () => ({ stopMomentum, diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index 427ecb33b9..ac5bc7d68c 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -8,7 +8,12 @@ import React, { } from 'react' import classNames from 'classnames' import { ComponentDefaults } from '@/utils/typings' -import { PickerViewProps, PickerOptionItem, PickerValue } from './types' +import { + PickerViewProps, + PickerOptionItem, + PickerValue, + PickerOptions, +} from './types' import PickerRoller from './pickerroller' import { usePropsValue } from '@/utils/use-props-value' @@ -26,7 +31,7 @@ const InternalPickerView: ForwardRefRenderFunction< > = (props, ref) => { const { options, - defaultValue, + defaultValue = [], value, duration, threeDimensional, @@ -40,14 +45,72 @@ const InternalPickerView: ForwardRefRenderFunction< const [selectedValue] = usePropsValue({ value, - defaultValue, - finalValue: defaultValue, + defaultValue: [...defaultValue], + finalValue: [...defaultValue], }) const [innerValue, setInnerValue] = useState(selectedValue) - const [innerOptions, setInnerOptions] = useState(options) + const [innerOptions, setInnerOptions] = useState([] as PickerOptions[]) const changeIndex = useRef(0) + /** + * 数据类型:多列、嵌套、单列 + */ + const columnsType = useMemo(() => { + const [firstColumn] = options + if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { + return 'cascade' + } + return 'multiple' + }, [options]) + + const formatCascadeOptions = ( + options: PickerOptions, + value: PickerValue[] + ) => { + if (!options.length) return [] // 如果 options 为空,直接返回空数组 + + const formatted: PickerOptions[] = [] + let columnOptions: PickerOptionItem = { + label: '', + value: '', + children: options, + } + + let columnIndex = 0 + while (columnOptions && columnOptions.children) { + const currentOptions: PickerOptions = columnOptions.children + formatted.push(currentOptions) + + const currentValue = value?.[columnIndex] + if (currentValue === 0) { + // 如果 currentValue 为 0,返回第一个 children + columnOptions = currentOptions[0] + } else if (currentValue) { + // 如果 currentValue 存在,查找匹配的项 + const index = currentOptions.findIndex( + (columnItem) => columnItem.value === currentValue + ) + columnOptions = currentOptions[index === -1 ? 0 : index] // 如果未找到,默认取第一个 + } else { + break // 如果 currentValue 不存在,终止循环 + } + + columnIndex++ + } + return formatted + } + + const formatOptions = useMemo(() => { + if (columnsType === 'multiple') { + return options + } + if (columnsType === 'cascade') { + return formatCascadeOptions(options[0] as PickerOptions, innerValue) + } + return options + }, [innerValue, options, columnsType]) + useEffect(() => { if (selectedValue !== innerValue) { setInnerValue(selectedValue) @@ -56,23 +119,44 @@ const InternalPickerView: ForwardRefRenderFunction< useEffect(() => { if (options !== innerOptions) { - setInnerOptions(options) + setInnerOptions(formatOptions) } - }, [options]) + }, [options, innerValue]) const handleSelect = useCallback( (option: PickerOptionItem, index: number) => { const newValue = option?.value - if (!newValue) return - setInnerValue((prev) => { - if (prev[index] === newValue) return prev - changeIndex.current = index - const next = [...prev] - next[index] = newValue - return next - }) + if (!newValue || innerValue[index] === newValue) return + changeIndex.current = index + if (columnsType === 'multiple') { + setInnerValue((prev) => { + const next = [...prev] + next[index] = newValue + return next + }) + } else { + const startIndex = index + const values: PickerValue[] = [] + values[index] = option.value + while (option?.children?.[0]) { + values[index + 1] = option.children[0].value + index++ + option = option.children[0] + } + // 当前改变列的下一列 children 值为空 + if (option?.children?.length) { + values[index + 1] = '' + } + const combineResult = [ + ...innerValue.slice(0, startIndex), + ...values.splice(startIndex), + ] + setInnerValue([...combineResult]) + console.log('combineResult', combineResult) + setInnerOptions(formatCascadeOptions(options[0], combineResult)) + } }, - [] + [innerValue, options, columnsType] ) const selectedOptions = useMemo(() => { From 4ce2688a1f4c109889e09a136f6a671cd927e5fc Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Mon, 10 Feb 2025 18:53:48 +0800 Subject: [PATCH 07/54] =?UTF-8?q?fix:=20=E7=BA=A7=E8=81=94=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/picker/demos/h5/demo6.tsx | 7 +-- src/packages/picker/picker.tsx | 70 +++++++----------------- src/packages/pickerview/pickerroller.tsx | 2 +- src/packages/pickerview/pickerview.tsx | 17 +++--- 4 files changed, 34 insertions(+), 62 deletions(-) diff --git a/src/packages/picker/demos/h5/demo6.tsx b/src/packages/picker/demos/h5/demo6.tsx index 929d1a4d65..92a4677cf4 100644 --- a/src/packages/picker/demos/h5/demo6.tsx +++ b/src/packages/picker/demos/h5/demo6.tsx @@ -71,16 +71,15 @@ const Demo6 = () => { selectedValue: PickerValue[] ) => { console.log('onconfirm', selectedOptions, selectedValue) - const str = selectedOptions.map((item) => item.label).join('-') - setCityCustom(selectedValue.join('-')) + const city = selectedOptions.map((item) => item.label).join('-') + setCityCustom(city) setValue(selectedValue) } - console.log('demo6===>value', value) return ( <> setIsVisible(!isVisible)} /> diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 6b05d39633..1c97234e54 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -5,7 +5,6 @@ import React, { RefObject, ForwardRefRenderFunction, useImperativeHandle, - useMemo, } from 'react' import classNames from 'classnames' import isEqual from 'react-fast-compare' @@ -95,7 +94,7 @@ const InternalPicker: ForwardRefRenderFunction< defaultValue: [...defaultValue], finalValue: [...defaultValue], onChange: (value: PickerValue[]) => { - props.onConfirm?.(selectedOptions, value) + props.onConfirm?.(selectedOptionsRef.current, value) }, }) const [innerVisible, setInnerVisible] = usePropsValue({ @@ -104,10 +103,11 @@ const InternalPicker: ForwardRefRenderFunction< finalValue: false, onChange: (v: boolean) => { if (!v) { - props.onClose?.(selectedOptions, innerValue) + props.onClose?.(selectedOptionsRef.current, innerValue) } }, }) + const actions: PickerActions = { open: () => { setInnerVisible(true) @@ -116,43 +116,16 @@ const InternalPicker: ForwardRefRenderFunction< setInnerVisible(false) }, } - useImperativeHandle(ref, () => actions) const [innerValue, setInnerValue] = useState(selectedValue) const [innerOptions, setInnerOptions] = useState([]) + const selectedOptionsRef = useRef([] as PickerOptionItem[]) const changeIndex = useRef(-1) const isConfirmEvent = useRef(false) const pickerRef = useRef(null) const [refs, setRefs] = useRefs() - const formatCascadeOptions = ( - options: PickerOptions, - values: PickerValue[] - ) => { - if (!options.length || !values.length) return [] - - const formatted: PickerOptions[] = [] - let currentOptions: PickerOptions = options - - for (let i = 0; i < values.length; i++) { - const value = values[i] - const foundItem = currentOptions.find((item) => item.value === value) - - if (!foundItem) break // 如果未找到匹配项,终止循环 - - formatted.push(currentOptions) // 将当前层级的选项添加到结果中 - - if (foundItem.children) { - currentOptions = foundItem.children // 更新当前层级为子选项 - } else { - break // 如果没有子选项,终止循环 - } - } - - return formatted - } - useEffect(() => { if (innerVisible) { console.log('selectedValue变更', selectedValue) @@ -162,37 +135,36 @@ const InternalPicker: ForwardRefRenderFunction< }, [options, innerVisible, selectedValue, innerOptions]) useEffect(() => { - console.log('innerValue变更onChange', innerValue, innerVisible) + console.log( + 'innerValue变更onChange', + innerValue, + innerVisible, + 'innerVisible为false不执行' + ) innerVisible && onChange && onChange({ - selectedOptions, + selectedOptions: selectedOptionsRef.current, value: innerValue, index: changeIndex.current, }) }, [innerValue, innerVisible]) - const selectedOptions = useMemo(() => { - console.log('selectedOptions', innerOptions) - const options: PickerOptions = [] - let currOptions = [] - innerOptions.forEach((columnOptions: PickerOptions, index: number) => { - currOptions = columnOptions.filter( - (item) => item.value === innerValue[index] - ) - if (currOptions[0]) { - options.push(currOptions[0]) - } - }) - return options - }, [innerOptions, innerValue]) + // const selectedOptions = useMemo(() => { + // console.log('selectedOptions.current', selectedOptionsObj.current) + // return selectedOptionsObj.current + // }, [selectedOptionsObj.current]) - // 确保value值变更再返回 const onChangeItem = ({ value, index, selectedOptions, }: PickerOnChangeCallbackParameter) => { + if (selectedOptions?.length) { + selectedOptionsRef.current = selectedOptions + } + + console.log('selectedOptions', selectedOptions) if (isEqual(value, innerValue)) return console.log('onChangeItem', value, innerValue, index, selectedOptions) setInnerValue(value) @@ -243,8 +215,6 @@ const InternalPicker: ForwardRefRenderFunction< ) } - console.log('innerValue渲染子组件', innerValue) - const renderPickerElement = () => { return (
diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 7f63b62761..47b66ef808 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -108,7 +108,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< const minDeg = 0 deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - if (minDeg < deg && deg < maxDeg) { + if (minDeg <= deg && deg < maxDeg) { setTransform('', `${deg}deg`, undefined, updateMove) setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) } diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index ac5bc7d68c..91f5369df0 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -160,13 +160,16 @@ const InternalPickerView: ForwardRefRenderFunction< ) const selectedOptions = useMemo(() => { - return options.map((columnOptions, index) => { - const selectedOption = columnOptions.find( - (item) => item.value === innerValue[index] - ) - return selectedOption || columnOptions[0] - }) - }, [options, innerValue]) + return innerOptions + .map((columnOptions, index) => { + const selectedOption = columnOptions.find( + (item) => item.value === innerValue[index] + ) + return selectedOption + // return selectedOption || columnOptions[0] + }) + .filter(Boolean) as PickerOptionItem[] + }, [innerOptions, innerValue]) useEffect(() => { console.log('onChange', { From 28c4e2cad15a0b348a12540035b0e0b8e942c7c8 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 11 Feb 2025 12:00:20 +0800 Subject: [PATCH 08/54] =?UTF-8?q?feat:=20pickerview=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BA=A7=E8=81=94=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/pickerview/demo.taro.tsx | 6 + src/packages/pickerview/demo.tsx | 6 + src/packages/pickerview/demos/h5/demo6.tsx | 80 +++++++++ src/packages/pickerview/demos/taro/demo1.tsx | 4 +- src/packages/pickerview/demos/taro/demo2.tsx | 4 +- src/packages/pickerview/demos/taro/demo3.tsx | 4 +- src/packages/pickerview/demos/taro/demo4.tsx | 4 +- src/packages/pickerview/demos/taro/demo5.tsx | 4 +- src/packages/pickerview/demos/taro/demo6.tsx | 80 +++++++++ src/packages/pickerview/doc.en-US.md | 94 ++++++++++ src/packages/pickerview/doc.md | 10 +- src/packages/pickerview/doc.taro.md | 93 ++++++++++ src/packages/pickerview/doc.zh-TW.md | 93 ++++++++++ src/packages/pickerview/pickerroller.taro.tsx | 9 +- src/packages/pickerview/pickerview.taro.tsx | 165 +++++++++++++++--- src/packages/pickerview/pickerview.tsx | 32 ++-- 16 files changed, 633 insertions(+), 55 deletions(-) create mode 100644 src/packages/pickerview/demos/h5/demo6.tsx create mode 100644 src/packages/pickerview/demos/taro/demo6.tsx create mode 100644 src/packages/pickerview/doc.en-US.md create mode 100644 src/packages/pickerview/doc.taro.md create mode 100644 src/packages/pickerview/doc.zh-TW.md diff --git a/src/packages/pickerview/demo.taro.tsx b/src/packages/pickerview/demo.taro.tsx index d05584575b..854ebb089d 100644 --- a/src/packages/pickerview/demo.taro.tsx +++ b/src/packages/pickerview/demo.taro.tsx @@ -8,6 +8,7 @@ import Demo2 from './demos/taro/demo2' import Demo3 from './demos/taro/demo3' import Demo4 from './demos/taro/demo4' import Demo5 from './demos/taro/demo5' +import Demo6 from './demos/taro/demo6' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -17,6 +18,7 @@ const PickerViewDemo = () => { multiColumn: '多列', controlled: '受控', tiled: '平铺', + cascade: '级联', }, 'en-US': { title: 'Basic Usage', @@ -24,6 +26,7 @@ const PickerViewDemo = () => { multiColumn: 'Multi Column', controlled: 'Controlled', tiled: 'Tiled', + cascade: 'Cascade', }, 'zh-TW': { title: '基礎用法', @@ -31,6 +34,7 @@ const PickerViewDemo = () => { multiColumn: '多列', controlled: '受控', tiled: '平鋪', + cascade: '級聯', }, }) return ( @@ -47,6 +51,8 @@ const PickerViewDemo = () => { {translated.tiled} +

{translated.cascade}

+ ) diff --git a/src/packages/pickerview/demo.tsx b/src/packages/pickerview/demo.tsx index 99de463ecd..b94f91de80 100644 --- a/src/packages/pickerview/demo.tsx +++ b/src/packages/pickerview/demo.tsx @@ -5,6 +5,7 @@ import Demo2 from './demos/h5/demo2' import Demo3 from './demos/h5/demo3' import Demo4 from './demos/h5/demo4' import Demo5 from './demos/h5/demo5' +import Demo6 from './demos/h5/demo6' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -14,6 +15,7 @@ const PickerViewDemo = () => { multiColumn: '多列', controlled: '受控', tiled: '平铺', + cascade: '级联', }, 'en-US': { title: 'Basic Usage', @@ -21,6 +23,7 @@ const PickerViewDemo = () => { multiColumn: 'Multi Column', controlled: 'Controlled', tiled: 'Tiled', + cascade: 'Cascade', }, 'zh-TW': { title: '基礎用法', @@ -28,6 +31,7 @@ const PickerViewDemo = () => { multiColumn: '多列', controlled: '受控', tiled: '平鋪', + cascade: '級聯', }, }) return ( @@ -42,6 +46,8 @@ const PickerViewDemo = () => {

{translated.tiled}

+

{translated.cascade}

+
) } diff --git a/src/packages/pickerview/demos/h5/demo6.tsx b/src/packages/pickerview/demos/h5/demo6.tsx new file mode 100644 index 0000000000..44aa46d8ae --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo6.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import { PickerView, Cell } from '@nutui/nutui-react' + +const Demo6 = () => { + const listData = [ + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], + ] + + return ( + <> + + { + console.log('onChange', value, selectedOptions) + }} + /> + + + ) +} +export default Demo6 diff --git a/src/packages/pickerview/demos/taro/demo1.tsx b/src/packages/pickerview/demos/taro/demo1.tsx index 4d3d363bc2..44c4bc6f3b 100644 --- a/src/packages/pickerview/demos/taro/demo1.tsx +++ b/src/packages/pickerview/demos/taro/demo1.tsx @@ -22,8 +22,8 @@ const Demo1 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} />
diff --git a/src/packages/pickerview/demos/taro/demo2.tsx b/src/packages/pickerview/demos/taro/demo2.tsx index f717632ce1..72a79c0a84 100644 --- a/src/packages/pickerview/demos/taro/demo2.tsx +++ b/src/packages/pickerview/demos/taro/demo2.tsx @@ -23,8 +23,8 @@ const Demo1 = () => { style={{ '--nutui-picker-item-height': '28px' }} options={listData} defaultValue={[1]} - onChange={(value, selectOptions) => { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} />
diff --git a/src/packages/pickerview/demos/taro/demo3.tsx b/src/packages/pickerview/demos/taro/demo3.tsx index ae6954258d..9c7b9cd1f9 100644 --- a/src/packages/pickerview/demos/taro/demo3.tsx +++ b/src/packages/pickerview/demos/taro/demo3.tsx @@ -25,8 +25,8 @@ const Demo3 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) if (isEqual(value, ['Tuesday', 'Afternoon'])) { setValue(['Monday', 'Evening']) } diff --git a/src/packages/pickerview/demos/taro/demo4.tsx b/src/packages/pickerview/demos/taro/demo4.tsx index c0234cdb03..2716cd35fd 100644 --- a/src/packages/pickerview/demos/taro/demo4.tsx +++ b/src/packages/pickerview/demos/taro/demo4.tsx @@ -24,8 +24,8 @@ const Demo1 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) if (value[0] === 3) { setValue([1]) } diff --git a/src/packages/pickerview/demos/taro/demo5.tsx b/src/packages/pickerview/demos/taro/demo5.tsx index 98cfbdbeca..a8d43a86b0 100644 --- a/src/packages/pickerview/demos/taro/demo5.tsx +++ b/src/packages/pickerview/demos/taro/demo5.tsx @@ -24,8 +24,8 @@ const Demo1 = () => { options={listData} threeDimensional={false} duration={500} - onChange={(value, selectOptions) => { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} />
diff --git a/src/packages/pickerview/demos/taro/demo6.tsx b/src/packages/pickerview/demos/taro/demo6.tsx new file mode 100644 index 0000000000..9cf8ae0ac2 --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo6.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import { PickerView, Cell } from '@nutui/nutui-react-taro' + +const Demo6 = () => { + const listData = [ + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], + ] + + return ( + <> + + { + console.log('onChange', value, selectedOptions) + }} + /> + + + ) +} +export default Demo6 diff --git a/src/packages/pickerview/doc.en-US.md b/src/packages/pickerview/doc.en-US.md new file mode 100644 index 0000000000..6ec4aa0aa8 --- /dev/null +++ b/src/packages/pickerview/doc.en-US.md @@ -0,0 +1,94 @@ +# PickerView + +The PickerView is the content area of the Picker. + +## Import + +```tsx +import { Picker } from '@nutui/nutui-react' +``` + +## Demo + +### Basic Usage + +:::demo + + + +::: + +### Controlled + +:::demo + + + +::: + +### Adjust Height + +:::demo + + + +::: + +### Multi Column + +:::demo + + + +::: + +### Tiled + +:::demo + + + +::: + +### Cascade + +:::demo + + + +::: + +## PickerView + +### Props + +| Property | Description | Type | Default | +| --- | --- | --- | --- | +| options | Tabular data | `Array` | `[]` | +| value | Selected value, controlled | `Array` | `[]` | +| defaultValue | Default value | `Array` | `[]` | +| threeDimensional | Whether to enable 3D effect | `boolean` | `true` | +| duration | The duration of inertial rolling during rapid sliding, in ms | `string` \| `number` | `1000` | +| onChange | Called when the value of each column changes | `({value, index, selectedOptions}) => void` | `-` | + +### options + +| Property | Description | Type | Default | +| --- | --- | --- | --- | +| label | Text of column | `string` \| `number` | `-` | +| value | Value of column | `string` \| `number` | `-` | +| children | Cascader Option | `Array` | `-` | + +## Theming + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. Please refer to [ConfigProvider component](#/en-US/component/configprovider). + +| Name | Description | Default | +| --- | --- | --- | +| \--nutui-picker-item-height | Height of each data item on the panel | `36px` | +| \--nutui-picker-item-text-color | The color of each piece of data in the panel | `$color-title` | +| \--nutui-picker-item-text-font-size | The font size of each piece of data in the panel | `$font-size-base` | +| \--nutui-picker-item-active-line-border | The border value currently selected by the panel | `1px solid $color-border` | +| \--nut-picker-mask-background | Panel shade gradient value | `linear-gradient(180deg, var(--nutui-white-12), var(--nutui-white-7)),linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7))` | diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index bb230e0346..ad1777daa9 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -50,6 +50,14 @@ import { name } from '@nutui/nutui-react' ::: +### 级联 + +:::demo + + + +::: + ## PickerView ### Props @@ -61,7 +69,7 @@ import { name } from '@nutui/nutui-react' | defaultValue | 默认选中 | `Array` | `[]` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | -| onChange | 每一列值变更时调用 | `(options, value) => void` | `-` | +| onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | ### options 数据结构 diff --git a/src/packages/pickerview/doc.taro.md b/src/packages/pickerview/doc.taro.md new file mode 100644 index 0000000000..f8e186b1be --- /dev/null +++ b/src/packages/pickerview/doc.taro.md @@ -0,0 +1,93 @@ +# PickerView 选择器视图 + +PickerView 是 Picker 的内容区域。 + +## 引入 + +```tsx +import { name } from '@nutui/nutui-react-taro' +``` + +## 示例代码 + +### 基础用法 + +:::demo + + + +::: + +### 受控 + +:::demo + + + +::: + +### 自定义高度 + +:::demo + + + +::: + +### 多列 + +:::demo + + + +::: + +### 平铺 + +:::demo + + + +::: + +### 级联 + +:::demo + + + +::: + +## PickerView + +### Props + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| options | 列表数据 | `Array` | `[]` | +| value | 选中值,受控 | `Array` | `[]` | +| defaultValue | 默认选中 | `Array` | `[]` | +| threeDimensional | 是否开启3D效果 | `boolean` | `true` | +| duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | +| onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | + +### options 数据结构 + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| label | 选项的文字内容 | `string` \| `number` | `-` | +| value | 选项对应的值,且唯一 | `string` \| `number` | `-` | + +## 主题定制 + +### 样式变量 + +组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。 + +| 名称 | 说明 | 默认值 | +| --- | --- | --- | +| \--nutui-picker-item-height | 面板每条数据高度 | `36px` | +| \--nutui-picker-item-text-color | 面板每条数据的字色 | `$color-title` | +| \--nutui-picker-item-text-font-size | 面板每条数据的字号 | `$font-size-base` | +| \--nutui-picker-item-active-line-border | 面板当前选中的border值 | `1px solid $color-border` | +| \--nut-picker-mask-background | 面板遮挡区渐变值 | `linear-gradient(180deg, var(--nutui-white-12), var(--nutui-white-7)),linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7))` | diff --git a/src/packages/pickerview/doc.zh-TW.md b/src/packages/pickerview/doc.zh-TW.md new file mode 100644 index 0000000000..c944d61291 --- /dev/null +++ b/src/packages/pickerview/doc.zh-TW.md @@ -0,0 +1,93 @@ +# PickerView 選擇器視圖 + +PickerView 是 Picker 的內容區域。 + +## 引入 + +```tsx +import { name } from '@nutui/nutui-react-taro' +``` + +## 示例代碼 + +### 基礎用法 + +:::demo + + + +::: + +### 受控 + +:::demo + + + +::: + +### 自定義高度 + +:::demo + + + +::: + +### 多列 + +:::demo + + + +::: + +### 平鋪 + +:::demo + + + +::: + +### 級聯 + +:::demo + + + +::: + +## PickerView + +### Props + +| 屬性 | 說明 | 類型 | 默認值 | +| --- | --- | --- | --- | +| options | 列錶數據 | `Array` | `[]` | +| value | 選中值,受控 | `Array` | `[]` | +| defaultValue | 默認選中 | `Array` | `[]` | +| threeDimensional | 是否開啟3D效果 | `boolean` | `true` | +| duration | 快速滑動時慣性滾動的時長,單位 ms | `string` \| `number` | `1000` | +| onChange | 每一列值變更時調用 | `({value, index, selectedOptions}) => void` | `-` | + +### options 數據結構 + +| 屬性 | 說明 | 類型 | 默認值 | +| --- | --- | --- | --- | +| label | 選項的文字內容 | `string` \| `number` | `-` | +| value | 選項對應的值,且唯一 | `string` \| `number` | `-` | + +## 主題定制 + +### 樣式變量 + +組件提供了下列 CSS 變量,可用於自定義樣式,使用方法請參考 [ConfigProvider 組件](#/zh-CN/component/configprovider)。 + +| 名稱 | 說明 | 默認值 | +| --- | --- | --- | +| \--nutui-picker-item-height | 面闆每條數據高度 | `36px` | +| \--nutui-picker-item-text-color | 面闆每條數據的字色 | `$color-title` | +| \--nutui-picker-item-text-font-size | 面闆每條數據的字號 | `$font-size-base` | +| \--nutui-picker-item-active-line-border | 面闆當前選中的border值 | `1px solid $color-border` | +| \--nut-picker-mask-background | 面闆遮擋區漸變值 | `linear-gradient(180deg, var(--nutui-white-12), var(--nutui-white-7)),linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7))` | diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 4c5b1493bd..99fda23f72 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -9,6 +9,7 @@ import { View } from '@tarojs/components' import { useTouch } from '@/utils/use-touch' import { passiveSupported } from '@/utils/supports-passive' import { PickerRollerProps, PickerOptionItem } from './types' +import { web } from '@/utils/platform-taro' const InternalPickerRoller: ForwardRefRenderFunction< { stopMomentum: () => void; moving: boolean }, @@ -49,7 +50,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< // 获取 lineSpacing.current CSS变量 useEffect(() => { const element = pickerRollerRef.current - if (element) { + if (element && web()) { const computedStyle = getComputedStyle(element) const currentLineSpacing = computedStyle.getPropertyValue( '--nutui-picker-item-height' @@ -111,8 +112,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< const minDeg = 0 deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - - if (minDeg < deg && deg < maxDeg) { + if (minDeg <= deg && deg < maxDeg) { setTransform('', `${deg}deg`, undefined, updateMove) setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) } @@ -190,7 +190,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< setCurrIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current type && setChooseValue(-move) - console.log(index, move, 'props.value.index') setMove(-move) } @@ -216,7 +215,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< setScrollDistance(0) transformY.current = 0 modifyStatus(false) - }, [options]) + }, [options, props.value]) useImperativeHandle(ref, () => ({ stopMomentum, diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index 251a8c4d1d..9bc0ed45e9 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -3,12 +3,19 @@ import React, { useCallback, useEffect, useMemo, + useRef, useState, } from 'react' import classNames from 'classnames' import { View } from '@tarojs/components' +import isEqual from 'react-fast-compare' import { ComponentDefaults } from '@/utils/typings' -import { PickerViewProps, PickerOptionItem, PickerValue } from './types' +import { + PickerViewProps, + PickerOptionItem, + PickerValue, + PickerOptions, +} from './types' import PickerRoller from './pickerroller.taro' import { usePropsValue } from '@/utils/use-props-value' @@ -26,7 +33,7 @@ const InternalPickerView: ForwardRefRenderFunction< > = (props, ref) => { const { options, - defaultValue, + defaultValue = [], value, duration, threeDimensional, @@ -40,51 +47,153 @@ const InternalPickerView: ForwardRefRenderFunction< const [selectedValue] = usePropsValue({ value, - defaultValue, - finalValue: defaultValue, + defaultValue: [...defaultValue], + finalValue: [...defaultValue], }) const [innerValue, setInnerValue] = useState(selectedValue) - const [innerOptions, setInnerOptions] = useState(options) + const [innerOptions, setInnerOptions] = useState([] as PickerOptions[]) + const changeIndex = useRef(0) - useEffect(() => { - if (selectedValue !== innerValue) { - setInnerValue(selectedValue) + /** + * 数据类型:级联、多列 + */ + const columnsType = useMemo(() => { + const [firstColumn] = options + if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { + return 'cascade' } - }, [selectedValue]) + return 'multiple' + }, [options]) + + const formatCascadeOptions = ( + options: PickerOptions, + value: PickerValue[] + ) => { + if (!options.length) return [] // 如果 options 为空,直接返回空数组 + + const formatted: PickerOptions[] = [] + let columnOptions: PickerOptionItem = { + label: '', + value: '', + children: options, + } + + let columnIndex = 0 + while (columnOptions && columnOptions.children) { + const currentOptions: PickerOptions = columnOptions.children + formatted.push(currentOptions) + + const currentValue = value?.[columnIndex] + if (currentValue === 0) { + // 如果 currentValue 为 0,返回第一个 children + columnOptions = currentOptions[0] + } else if (currentValue) { + // 如果 currentValue 存在,查找匹配的项 + const index = currentOptions.findIndex( + (columnItem) => columnItem.value === currentValue + ) + columnOptions = currentOptions[index === -1 ? 0 : index] // 如果未找到,默认取第一个 + } else { + break // 如果 currentValue 不存在,终止循环 + } + + columnIndex++ + } + return formatted + } + + const formatOptions = useMemo(() => { + if (columnsType === 'multiple') { + return options + } + if (columnsType === 'cascade') { + return formatCascadeOptions(options[0] as PickerOptions, innerValue) + } + return options + }, [innerValue, options, columnsType]) useEffect(() => { if (options !== innerOptions) { - setInnerOptions(options) + setInnerOptions(formatOptions) } - }, [options]) + }, [options, innerValue]) + + useEffect(() => { + if (selectedValue !== innerValue) { + setInnerValue(selectedValue) + } + }, [selectedValue]) const handleSelect = useCallback( (option: PickerOptionItem, index: number) => { + console.log(innerValue, options, columnsType, innerOptions) const newValue = option?.value - if (!newValue) return - setInnerValue((prev) => { - if (prev[index] === newValue) return prev - const next = [...prev] - next[index] = newValue - return next - }) + if (!newValue || innerValue[index] === newValue) return + changeIndex.current = index + if (columnsType === 'multiple') { + setInnerValue((prev) => { + const next = [...prev] + next[index] = newValue + return next + }) + } else { + const startIndex = index + const values: PickerValue[] = [] + values[index] = option.value + while (option?.children?.[0]) { + values[index + 1] = option.children[0].value + index++ + option = option.children[0] + } + // 当前改变列的下一列 children 值为空 + if (option?.children?.length) { + values[index + 1] = '' + } + const combineResult = [ + ...innerValue.slice(0, startIndex), + ...values.splice(startIndex), + ] + console.log('combineResult', combineResult) + setInnerValue([...combineResult]) + + if ( + !isEqual( + formatCascadeOptions(options[0], combineResult), + innerOptions + ) + ) { + setInnerOptions(formatCascadeOptions(options[0], combineResult)) + } + } }, - [] + [innerValue, options, columnsType, innerOptions] ) const selectedOptions = useMemo(() => { - return options.map((columnOptions, index) => { - const selectedOption = columnOptions.find( - (item) => item.value === innerValue[index] - ) - return selectedOption || columnOptions[0] - }) - }, [options, innerValue]) + return innerOptions + .map((columnOptions, index) => { + const selectedOption = columnOptions.find( + (item) => item.value === innerValue[index] + ) + return selectedOption + // return selectedOption || columnOptions[0] + }) + .filter(Boolean) as PickerOptionItem[] + }, [innerOptions, innerValue]) useEffect(() => { - onChange?.(innerValue, selectedOptions) - }, [innerValue, selectedOptions, onChange]) + console.log('onChange', { + value: innerValue, + index: changeIndex.current, + selectedOptions, + }) + onChange?.({ + value: innerValue, + index: changeIndex.current, + selectedOptions, + }) + }, [innerValue, onChange]) return ( diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index 91f5369df0..67db6b2281 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -7,6 +7,7 @@ import React, { useState, } from 'react' import classNames from 'classnames' +import isEqual from 'react-fast-compare' import { ComponentDefaults } from '@/utils/typings' import { PickerViewProps, @@ -54,7 +55,7 @@ const InternalPickerView: ForwardRefRenderFunction< const changeIndex = useRef(0) /** - * 数据类型:多列、嵌套、单列 + * 数据类型:级联、多列 */ const columnsType = useMemo(() => { const [firstColumn] = options @@ -111,20 +112,21 @@ const InternalPickerView: ForwardRefRenderFunction< return options }, [innerValue, options, columnsType]) - useEffect(() => { - if (selectedValue !== innerValue) { - setInnerValue(selectedValue) - } - }, [selectedValue]) - useEffect(() => { if (options !== innerOptions) { setInnerOptions(formatOptions) } }, [options, innerValue]) + useEffect(() => { + if (selectedValue !== innerValue) { + setInnerValue(selectedValue) + } + }, [selectedValue]) + const handleSelect = useCallback( (option: PickerOptionItem, index: number) => { + console.log(innerValue, options, columnsType, innerOptions) const newValue = option?.value if (!newValue || innerValue[index] === newValue) return changeIndex.current = index @@ -151,12 +153,20 @@ const InternalPickerView: ForwardRefRenderFunction< ...innerValue.slice(0, startIndex), ...values.splice(startIndex), ] - setInnerValue([...combineResult]) console.log('combineResult', combineResult) - setInnerOptions(formatCascadeOptions(options[0], combineResult)) + setInnerValue([...combineResult]) + + if ( + !isEqual( + formatCascadeOptions(options[0], combineResult), + innerOptions + ) + ) { + setInnerOptions(formatCascadeOptions(options[0], combineResult)) + } } }, - [innerValue, options, columnsType] + [innerValue, options, columnsType, innerOptions] ) const selectedOptions = useMemo(() => { @@ -182,7 +192,7 @@ const InternalPickerView: ForwardRefRenderFunction< index: changeIndex.current, selectedOptions, }) - }, [innerValue, selectedOptions, onChange]) + }, [innerValue, onChange]) return (
From bc3fb3561dbbb3b90a3091e1141d2d03e4b20d4b Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 11 Feb 2025 18:57:55 +0800 Subject: [PATCH 09/54] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0taro=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.json | 13 +- src/packages/form/demos/h5/demo7.tsx | 17 +- src/packages/form/demos/taro/demo7.tsx | 16 +- src/packages/picker/demo.tsx | 6 +- src/packages/picker/demos/h5/demo7.tsx | 172 ++++---- src/packages/picker/demos/taro/demo1.tsx | 40 +- src/packages/picker/demos/taro/demo2.tsx | 35 +- src/packages/picker/demos/taro/demo3.tsx | 64 +-- src/packages/picker/demos/taro/demo4.tsx | 46 +- src/packages/picker/demos/taro/demo5.tsx | 34 +- src/packages/picker/demos/taro/demo6.tsx | 174 ++++---- src/packages/picker/demos/taro/demo7.tsx | 159 ++++--- src/packages/picker/demos/taro/demo8.tsx | 45 +- src/packages/picker/index.taro.ts | 1 - src/packages/picker/index.ts | 1 - src/packages/picker/picker.taro.tsx | 413 +++++------------- src/packages/picker/picker.tsx | 99 ++--- src/packages/picker/pickerpanel.taro.tsx | 322 -------------- src/packages/pickerview/pickerroller.taro.tsx | 1 - src/packages/pickerview/pickerview.taro.tsx | 34 +- src/packages/pickerview/pickerview.tsx | 34 +- src/packages/pickerview/types.ts | 1 + 22 files changed, 603 insertions(+), 1124 deletions(-) delete mode 100644 src/packages/picker/pickerpanel.taro.tsx diff --git a/src/config.json b/src/config.json index 4cee8900de..515f48c690 100644 --- a/src/config.json +++ b/src/config.json @@ -594,7 +594,7 @@ "show": true, "taro": true, "author": "hanyuxinting", - "dd": false + "dd": true }, { "version": "2.0.0", @@ -607,7 +607,7 @@ "taro": true, "exportEmpty": true, "author": "hanyuxinting", - "dd": false + "dd": true }, { "version": "3.0.0", @@ -679,7 +679,7 @@ "dd": true }, { - "version": "3.0.0", + "version": "2.0.0", "name": "Picker", "type": "component", "cName": "选择器", @@ -687,11 +687,11 @@ "sort": 10, "show": true, "taro": true, - "author": "dsj", - "dd": true + "dd": true, + "author": "songsong" }, { - "version": "3.0.0", + "version": "2.0.0", "name": "PickerView", "type": "component", "cName": "选择器视图", @@ -699,7 +699,6 @@ "sort": 15, "show": true, "taro": true, - "v15": false, "dd": true, "author": "songsong" }, diff --git a/src/packages/form/demos/h5/demo7.tsx b/src/packages/form/demos/h5/demo7.tsx index f0387f47a7..8ba20b1cd3 100644 --- a/src/packages/form/demos/h5/demo7.tsx +++ b/src/packages/form/demos/h5/demo7.tsx @@ -17,14 +17,15 @@ import { ArrowRight } from '@nutui/icons-react' const Demo7 = () => { const pickerOptions = [ - { value: 4, text: 'BeiJing' }, - { value: 1, text: 'NanJing' }, - { value: 2, text: 'WuXi' }, - { value: 8, text: 'DaQing' }, - { value: 9, text: 'SuiHua' }, - { value: 10, text: 'WeiFang' }, - { value: 12, text: 'ShiJiaZhuang' }, + { value: 1, label: 'BeiJing' }, + { value: 2, label: 'NanJing' }, + { value: 3, label: 'WuXi' }, + { value: 4, label: 'DaQing' }, + { value: 5, label: 'SuiHua' }, + { value: 6, label: 'WeiFang' }, + { value: 7, label: 'ShiJiaZhuang' }, ] + const submitFailed = (error: any) => { Toast.show({ content: JSON.stringify(error), icon: 'fail' }) } @@ -106,7 +107,7 @@ const Demo7 = () => { title={ value.length ? pickerOptions.filter((po) => po.value === value[0])[0] - ?.text + ?.label : 'Please select' } extra={} diff --git a/src/packages/form/demos/taro/demo7.tsx b/src/packages/form/demos/taro/demo7.tsx index bee2dae660..97713f58e1 100644 --- a/src/packages/form/demos/taro/demo7.tsx +++ b/src/packages/form/demos/taro/demo7.tsx @@ -18,13 +18,13 @@ import { View } from '@tarojs/components' const Demo7 = () => { const pickerOptions = [ - { value: 4, text: 'BeiJing' }, - { value: 1, text: 'NanJing' }, - { value: 2, text: 'WuXi' }, - { value: 8, text: 'DaQing' }, - { value: 9, text: 'SuiHua' }, - { value: 10, text: 'WeiFang' }, - { value: 12, text: 'ShiJiaZhuang' }, + { value: 1, label: 'BeiJing' }, + { value: 2, label: 'NanJing' }, + { value: 3, label: 'WuXi' }, + { value: 4, label: 'DaQing' }, + { value: 5, label: 'SuiHua' }, + { value: 6, label: 'WeiFang' }, + { value: 7, label: 'ShiJiaZhuang' }, ] const submitFailed = (error: any) => { Taro.showToast({ title: JSON.stringify(error), icon: 'error' }) @@ -107,7 +107,7 @@ const Demo7 = () => { title={ value.length ? pickerOptions.filter((po) => po.value === value[0])[0] - ?.text + ?.label : 'Please select' } extra={} diff --git a/src/packages/picker/demo.tsx b/src/packages/picker/demo.tsx index a52112eda3..9bc11fc9ff 100644 --- a/src/packages/picker/demo.tsx +++ b/src/packages/picker/demo.tsx @@ -6,7 +6,7 @@ import Demo3 from './demos/h5/demo3' import Demo4 from './demos/h5/demo4' import Demo5 from './demos/h5/demo5' import Demo6 from './demos/h5/demo6' -// import Demo7 from './demos/h5/demo7' +import Demo7 from './demos/h5/demo7' import Demo8 from './demos/h5/demo8' const PickerDemo = () => { @@ -58,8 +58,8 @@ const PickerDemo = () => {

{translated.cascade}

- {/*

{translated.async}

- */} +

{translated.async}

+

{translated.theme}

diff --git a/src/packages/picker/demos/h5/demo7.tsx b/src/packages/picker/demos/h5/demo7.tsx index 2db350c2fa..a8688a41c2 100644 --- a/src/packages/picker/demos/h5/demo7.tsx +++ b/src/packages/picker/demos/h5/demo7.tsx @@ -1,90 +1,98 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo7 = () => { const [isVisible, setIsVisible] = useState(false) - const [asyncDesc, setasyncDesc] = useState('') + const [value, setValue] = useState([1]) + const [asyncDesc, setasyncDesc] = useState('北京') const [asyncData, setAsyncData] = useState([ - { - value: 1, - label: '北京市', - children: [ - { - value: 1, - label: '朝阳区', - children: [ - { - value: 1, - label: '常营', - }, - { - value: 2, - label: '望京', - }, - ], - }, - { value: 2, label: '海淀区' }, - { value: 3, label: '大兴区' }, - { value: 4, label: '东城区' }, - { value: 5, label: '西城区' }, - { value: 6, label: '丰台区' }, - ], - }, - { - value: 2, - label: '上海市', - children: [], - }, + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [], + }, + ], ]) - const updateChooseValueCustmer = ( - options: PickerOption[], - values: (string | number)[], - columnIndex: number - ) => { - console.log('updateChooseValueCustmer', columnIndex, values, options) - if (columnIndex === 0 && values[0] === 2) { + const updateChooseValueCustmer = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + if (value[0] === 2 && asyncData[0]?.[1].children.length === 0) { + console.log('updateChooseValueCustmer', index, value, selectedOptions) setTimeout(() => { - if (asyncData[1].children.length === 0) { - asyncData[1].children = [ - { - value: 1, - label: '黄埔区', - }, - { - value: 2, - label: '长宁区', - }, - { - value: 3, - label: '普陀区', - }, - { - value: 4, - label: '杨浦区', - }, - { - value: 5, - label: '浦东新区', - }, - ] - setAsyncData([...asyncData]) - } - }, 100) + asyncData[0][1].children = [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ] + setValue(value) + setAsyncData([...[...asyncData]]) + }, 0) } } const setAsyncConfirm = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - const str = options.map((item) => item.text).join('-') - setasyncDesc(str) + console.log('onconfirm', selectedOptions, selectedValue) + const city = selectedOptions.map((item) => item.label).join('-') + setasyncDesc(city) + setValue(selectedValue) } return ( @@ -94,18 +102,14 @@ const Demo7 = () => { description={asyncDesc} onClick={() => setIsVisible(!isVisible)} /> + setIsVisible(false)} - onConfirm={(list, values) => setAsyncConfirm(list, values)} - onChange={( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => - updateChooseValueCustmer(selectedOptions, selectedValue, columnIndex) - } + onConfirm={setAsyncConfirm} + onChange={updateChooseValueCustmer} /> ) diff --git a/src/packages/picker/demos/taro/demo1.tsx b/src/packages/picker/demos/taro/demo1.tsx index fa97842a99..88678f7c5e 100644 --- a/src/packages/picker/demos/taro/demo1.tsx +++ b/src/packages/picker/demos/taro/demo1.tsx @@ -1,18 +1,17 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' - -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOnChangeCallbackParameter, + PickerOptionItem, +} from '@nutui/nutui-react-taro' const Demo1 = () => { const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const listData1 = [ + const options = [ [ { value: 1, label: '南京市' }, { value: 2, label: '无锡市' }, @@ -25,16 +24,21 @@ const Demo1 = () => { { value: 12, label: '乌鲁木齐市' }, ], ] - const changePicker = (list: any[], option: any, columnIndex: number) => { - console.log(columnIndex, option) + const changePicker = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('changePicker', value, index, selectedOptions) } const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { + console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: PickerOptionItem) => { + description += ` ${option.label}` }) setBaseDesc(description) } @@ -48,8 +52,8 @@ const Demo1 = () => { confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} onClose={() => setVisible(false)} onChange={changePicker} /> diff --git a/src/packages/picker/demos/taro/demo2.tsx b/src/packages/picker/demos/taro/demo2.tsx index 058f1da314..f4fe925b36 100644 --- a/src/packages/picker/demos/taro/demo2.tsx +++ b/src/packages/picker/demos/taro/demo2.tsx @@ -1,19 +1,16 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo2 = () => { const [visible, setVisible] = useState(false) - const [baseDefault, setbaseDefault] = useState('') + const [baseDesc, setBaseDesc] = useState('无锡市') const [defaultValue] = useState([2]) - - const listData1 = [ + const options = [ [ { value: 1, label: '南京市' }, { value: 2, label: '无锡市' }, @@ -27,28 +24,28 @@ const Demo2 = () => { ], ] const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: any) => { + description += ` ${option.label}` }) - setbaseDefault(description) + setBaseDesc(description) } return ( <> setVisible(!visible)} /> confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} onClose={() => setVisible(false)} /> diff --git a/src/packages/picker/demos/taro/demo3.tsx b/src/packages/picker/demos/taro/demo3.tsx index c7f8aee67e..c0cee40ba1 100644 --- a/src/packages/picker/demos/taro/demo3.tsx +++ b/src/packages/picker/demos/taro/demo3.tsx @@ -1,17 +1,17 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' +import isEqual from 'react-fast-compare' +import { PickerOnChangeCallbackParameter } from '@/packages/pickerview/types' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo3 = () => { - const [isVisible, setIsVisible] = useState(false) + const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const [val, setVal] = useState>([]) + const [value, setValue] = useState([] as PickerValue[]) const options = [ [ { value: 1, label: '南京市' }, @@ -25,35 +25,45 @@ const Demo3 = () => { { value: 12, label: '乌鲁木齐市' }, ], ] + + const changePicker = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('changePicker', value, index, selectedOptions) + } const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` - }) - setBaseDesc(description) + if (isEqual(selectedValue, [3])) { + setValue([1]) + setBaseDesc('南京市') + } else { + setValue(selectedValue) + let description = '' + selectedOptions.forEach((option: any) => { + description += ` ${option.label}` + }) + setBaseDesc(description) + } } return ( <> setIsVisible(!isVisible)} + onClick={() => setVisible(!visible)} /> { - confirmPicker(list, values) - setVal(values) - }} - onClose={() => { - setIsVisible(false) - }} + onChange={changePicker} + onConfirm={confirmPicker} + onClose={() => setVisible(false)} /> ) diff --git a/src/packages/picker/demos/taro/demo4.tsx b/src/packages/picker/demos/taro/demo4.tsx index 5461f2c801..ea0cfdfab9 100644 --- a/src/packages/picker/demos/taro/demo4.tsx +++ b/src/packages/picker/demos/taro/demo4.tsx @@ -1,17 +1,16 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo4 = () => { - const [isVisible2, setIsVisible2] = useState(false) - const [mutilDesc, setMutilDesc] = useState('') - const listData2 = [ + const [visible, setVisible] = useState(false) + const [mutilDesc, setMutilDesc] = useState('周三') + const [defaultValue] = useState(['Wednesday']) + const options = [ // 第一列 [ { label: '周一', value: 'Monday' }, @@ -28,32 +27,29 @@ const Demo4 = () => { ], ] const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { + console.log('confirmPicker', selectedOptions) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: any) => { + option?.label && (description += ` ${option.label}`) }) setMutilDesc(description) } - const changePicker = (options: any[], values: any, columnIndex: number) => { - console.log('picker onChange', columnIndex, values, options) - } return ( <> setIsVisible2(!isVisible2)} + onClick={() => setVisible(!visible)} /> setIsVisible2(false)} - defaultValue={['Wednesday']} - onChange={changePicker} - onConfirm={(list, values) => confirmPicker(list, values)} + visible={visible} + options={options} + onClose={() => setVisible(false)} + defaultValue={defaultValue} + onConfirm={confirmPicker} /> ) diff --git a/src/packages/picker/demos/taro/demo5.tsx b/src/packages/picker/demos/taro/demo5.tsx index 0b29374c37..cd4fcd0af9 100644 --- a/src/packages/picker/demos/taro/demo5.tsx +++ b/src/packages/picker/demos/taro/demo5.tsx @@ -1,18 +1,15 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo5 = () => { - const [tileDesc, settileDesc] = useState('') const [isVisible, setIsVisible] = useState(false) - - const listData1 = [ + const [tileDesc, settileDesc] = useState('无锡市') + const options = [ [ { value: 1, label: '南京市' }, { value: 2, label: '无锡市' }, @@ -26,20 +23,14 @@ const Demo5 = () => { ], ] - const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] - ) => { + const confirmPicker = (options: PickerOptions, values: PickerValue[]) => { let description = '' options.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) settileDesc(description) setIsVisible(false) } - const changePicker = (options: any[], values: any, columnIndex: number) => { - console.log('picker onChange', columnIndex, values, options) - } return ( <> { /> confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} defaultValue={[2]} threeDimensional={false} duration={1000} onClose={() => setIsVisible(false)} - onChange={changePicker} /> ) diff --git a/src/packages/picker/demos/taro/demo6.tsx b/src/packages/picker/demos/taro/demo6.tsx index 2c46b1d3c2..1a0d29aae4 100644 --- a/src/packages/picker/demos/taro/demo6.tsx +++ b/src/packages/picker/demos/taro/demo6.tsx @@ -1,105 +1,90 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo6 = () => { const [isVisible, setIsVisible] = useState(false) + const [value, setValue] = useState([2]) + const [cityCustom, setCityCustom] = useState('上海') const customCityData = [ - { - value: 1, - label: '北京', - children: [ - { - value: 1, - label: '朝阳区', - }, - { - value: 2, - label: '海淀区', - }, - { - value: 3, - label: '大兴区', - }, - { - value: 4, - label: '东城区', - }, - { - value: 5, - label: '西城区', - }, - { - value: 6, - label: '丰台区', - }, - ], - }, - { - value: 2, - label: '上海', - children: [ - { - value: 1, - label: '黄埔区', - }, - { - value: 2, - label: '长宁区', - }, - { - value: 3, - label: '普陀区', - }, - { - value: 4, - label: '杨浦区', - }, - { - value: 5, - label: '浦东新区', - }, - ], - }, + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], ] - const [asyncData] = useState([ - { - value: 1, - label: '北京市', - children: [ - { value: 1, label: '朝阳区' }, - { value: 2, label: '海淀区' }, - { value: 3, label: '大兴区' }, - { value: 4, label: '东城区' }, - { value: 5, label: '西城区' }, - { value: 6, label: '丰台区' }, - ], - }, - { - value: 2, - label: '上海市', - children: [], - }, - ]) - const [cityCustom, setCityCustom] = useState('') const setChooseValueCustom = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - const str = options.map((item) => item.text).join('-') - setCityCustom(str) + console.log('onconfirm', selectedOptions, selectedValue) + const city = selectedOptions.map((item) => item.label).join('-') + setCityCustom(city) + setValue(selectedValue) } + return ( <> setIsVisible(!isVisible)} /> @@ -107,13 +92,12 @@ const Demo6 = () => { setIsVisible(false)} - onConfirm={(list, values) => setChooseValueCustom(list, values)} - onChange={( - options: PickerOption[], - value: (string | number)[], - columnIndex: number - ) => console.log(asyncData, '多级联动', columnIndex, value, options)} + onConfirm={setChooseValueCustom} + onChange={({ value, index, selectedOptions }) => + console.log('多级联动', value, index, selectedOptions) + } /> ) diff --git a/src/packages/picker/demos/taro/demo7.tsx b/src/packages/picker/demos/taro/demo7.tsx index c46c44fad2..03d5ca162a 100644 --- a/src/packages/picker/demos/taro/demo7.tsx +++ b/src/packages/picker/demos/taro/demo7.tsx @@ -1,77 +1,98 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react-taro' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo7 = () => { const [isVisible, setIsVisible] = useState(false) - const [asyncDesc, setasyncDesc] = useState('') + const [value, setValue] = useState([1]) + const [asyncDesc, setasyncDesc] = useState('北京') const [asyncData, setAsyncData] = useState([ - { - value: 1, - label: '北京市', - children: [ - { value: 1, label: '朝阳区' }, - { value: 2, label: '海淀区' }, - { value: 3, label: '大兴区' }, - { value: 4, label: '东城区' }, - { value: 5, label: '西城区' }, - { value: 6, label: '丰台区' }, - ], - }, - { - value: 2, - label: '上海市', - children: [], - }, + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [], + }, + ], ]) - const updateChooseValueCustmer = ( - options: PickerOption[], - values: (string | number)[], - columnIndex: number - ) => { - console.log('updateChooseValueCustmer', columnIndex, values, options) - if (columnIndex === 0 && values[0] === 2) { + const updateChooseValueCustmer = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + if (value[0] === 2 && asyncData[0]?.[1].children.length === 0) { + console.log('updateChooseValueCustmer', index, value, selectedOptions) setTimeout(() => { - if (asyncData[1].children.length === 0) { - asyncData[1].children = [ - { - value: 1, - label: '黄埔区', - }, - { - value: 2, - label: '长宁区', - }, - { - value: 3, - label: '普陀区', - }, - { - value: 4, - label: '杨浦区', - }, - { - value: 5, - label: '浦东新区', - }, - ] - setAsyncData([...asyncData]) - } - }, 100) + asyncData[0][1].children = [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ] + setValue(value) + setAsyncData([...[...asyncData]]) + }, 0) } } const setAsyncConfirm = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - const str = options.map((item) => item.text).join('-') - setasyncDesc(str) + console.log('onconfirm', selectedOptions, selectedValue) + const city = selectedOptions.map((item) => item.label).join('-') + setasyncDesc(city) + setValue(selectedValue) } return ( @@ -81,18 +102,14 @@ const Demo7 = () => { description={asyncDesc} onClick={() => setIsVisible(!isVisible)} /> + setIsVisible(false)} - onConfirm={(list, values) => setAsyncConfirm(list, values)} - onChange={( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => - updateChooseValueCustmer(selectedOptions, selectedValue, columnIndex) - } + onConfirm={setAsyncConfirm} + onChange={updateChooseValueCustmer} /> ) diff --git a/src/packages/picker/demos/taro/demo8.tsx b/src/packages/picker/demos/taro/demo8.tsx index 0f0e87c6be..b61a9b599b 100644 --- a/src/packages/picker/demos/taro/demo8.tsx +++ b/src/packages/picker/demos/taro/demo8.tsx @@ -1,15 +1,16 @@ import React, { useState } from 'react' -import { Picker, Cell, ConfigProvider } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + ConfigProvider, + PickerOptionItem, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - label: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo8 = () => { - const [isVisible, setIsVisible] = useState(false) + const [visible, setVisible] = useState(false) + const [baseDesc, setBaseDesc] = useState('') const options = [ [ { value: 1, label: '南京市' }, @@ -24,27 +25,23 @@ const Demo8 = () => { ], ] - const [baseDesc, setBaseDesc] = useState('') - const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - console.log('demo 确定', options, values) + console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: PickerOptionItem) => { + description += ` ${option.label}` }) setBaseDesc(description) - setIsVisible(false) } - return ( <> setIsVisible(!isVisible)} + onClick={() => setVisible(!visible)} /> { }} > confirmPicker(list, values)} - onClose={() => { - setIsVisible(false) - console.log('onclose') - }} + onConfirm={confirmPicker} + onClose={() => setVisible(false)} /> diff --git a/src/packages/picker/index.taro.ts b/src/packages/picker/index.taro.ts index edc33812b3..ff0b062781 100644 --- a/src/packages/picker/index.taro.ts +++ b/src/packages/picker/index.taro.ts @@ -1,5 +1,4 @@ import Picker from './picker.taro' -export type { PickerOption } from './types' export type { PickerProps } from './picker.taro' export default Picker diff --git a/src/packages/picker/index.ts b/src/packages/picker/index.ts index 2f320e8d33..212b25cb51 100644 --- a/src/packages/picker/index.ts +++ b/src/packages/picker/index.ts @@ -1,5 +1,4 @@ import Picker from './picker' -export type { PickerOption } from './types' export type { PickerProps } from './picker' export default Picker diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 0eae715b2a..3f1fcb555f 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -6,69 +6,67 @@ import React, { ForwardRefRenderFunction, useImperativeHandle, } from 'react' +import { View } from '@tarojs/components' import classNames from 'classnames' -import Taro from '@tarojs/taro' -import { View, PickerView, PickerViewColumn } from '@tarojs/components' -import { Popup, PopupProps } from '@/packages/popup/popup.taro' -import PickerPanel from './pickerpanel.taro' +import isEqual from 'react-fast-compare' +import { + PickerView, + PickerOptions, + PickerValue, + PickerOptionItem, + PickerOnChangeCallbackParameter, + SafeArea, + Popup, + PopupProps, +} from '@nutui/nutui-react-taro' import useRefs from '@/utils/use-refs' import { useConfig } from '@/packages/configprovider/index.taro' -import { PickerOption } from './types' import { usePropsValue } from '@/utils/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' - -export type PickerActions = { - open: () => void - close: () => void -} +import { PickerActions, PickerRef } from './types' export interface PickerProps extends Omit { visible?: boolean | undefined title?: string - options: (PickerOption | PickerOption[])[] - value?: (number | string)[] - defaultValue?: (number | string)[] + options: PickerOptions[] + value?: PickerValue[] + defaultValue?: PickerValue[] threeDimensional?: boolean duration: number | string closeOnOverlayClick: boolean + renderLabel?: (item: PickerOptionItem) => React.ReactNode + popupProps: Partial< Omit > onConfirm?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void onCancel?: () => void onClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void afterClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedOptions: PickerOptions, + selectedValue: PickerOptions[], pickerRef: RefObject ) => void - onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => void + onChange?: (args0: PickerOnChangeCallbackParameter) => void children?: any } const defaultProps = { ...ComponentDefaults, - visible: false, title: '', options: [], - value: [], + value: undefined, defaultValue: [], - threeDimensional: true, closeOnOverlayClick: true, - duration: 1000, } as unknown as PickerProps const InternalPicker: ForwardRefRenderFunction< - unknown, + PickerRef, Partial > = (props, ref) => { const { locale } = useConfig() @@ -93,31 +91,24 @@ const InternalPicker: ForwardRefRenderFunction< } = { ...defaultProps, ...props } const classPrefix = 'nut-picker' const classes = classNames(classPrefix, className) - const [selectedValue, setSelectedValue] = usePropsValue< - Array - >({ + const [selectedValue, setSelectedValue] = usePropsValue({ value: props.value, defaultValue: [...defaultValue], finalValue: [...defaultValue], - onChange: (val) => { - props.onConfirm?.(setSelectedOptions(), val) + onChange: (value: PickerValue[]) => { + props.onConfirm?.(selectedOptionsRef.current, value) }, }) const [innerVisible, setInnerVisible] = usePropsValue({ value: props.visible, defaultValue: false, finalValue: false, - onChange: (val: boolean) => { - props.onClose?.(setSelectedOptions(), innerValue) + onChange: (v: boolean) => { + if (!v) { + props.onClose?.(selectedOptionsRef.current, innerValue) + } }, }) - const [innerValue, setInnerValue] = useState(selectedValue) - const [currentValue, setCurrentValue] = useState([]) - const [columnIndex, setColumnIndex] = useState(0) // 选中列 - const pickerRef = useRef(null) - const [refs, setRefs] = useRefs() - const [columnsList, setColumnsList] = useState([]) // 格式化后每一列的数据 - const isConfirmEvent = useRef(false) const actions: PickerActions = { open: () => { @@ -127,176 +118,51 @@ const InternalPicker: ForwardRefRenderFunction< setInnerVisible(false) }, } - useImperativeHandle(ref, () => actions) - // 级联数据格式化 - const formatCascade = ( - columns: PickerOption[], - values: (number | string)[] - ) => { - const formatted: PickerOption[][] = [] - let columnOptions: PickerOption = { - text: '', - value: '', - children: columns, - } - - let columnIndex = 0 - while (columnOptions && columnOptions.children) { - const options: PickerOption[] = columnOptions.children - const value = values[columnIndex] - let index = options.findIndex((columnItem) => columnItem.value === value) - if (index === -1) index = 0 - columnOptions = columnOptions.children[index] - columnIndex++ - formatted.push(options) - } - return formatted - } - - // 数据类型:多列、嵌套、单列 - const columnsType = () => { - const firstColumn: PickerOption | PickerOption[] = options[0] - if (firstColumn) { - if (Array.isArray(firstColumn)) { - return 'multiple' - } - if ('children' in firstColumn) { - return 'cascade' - } - } - return 'single' - } - - // 传入的数据格式化 - const normalListData = (innerValue: any) => { - const type = columnsType() - switch (type) { - case 'multiple': - return options - case 'cascade': - // 级联数据处理 - return formatCascade(options as PickerOption[], innerValue) - default: - return [options] - } - } - const init = () => { - const normalData: PickerOption[][] = normalListData( - innerValue - ) as PickerOption[][] - setColumnsList(normalData) - // 初始化默认选中数据 - const data: (string | number)[] = [] - normalData.length > 0 && - normalData.map((item) => { - item[0] && data.push(item[0].value) - return item - }) - if (!innerValue.length && innerValue.length === 0) { - setInnerValue([...data]) - } - } - - useEffect(() => { - setInnerValue(innerValue !== selectedValue ? selectedValue : innerValue) - }, [innerVisible, selectedValue]) + const [innerValue, setInnerValue] = useState([...selectedValue]) + const [innerOptions, setInnerOptions] = useState([]) + const selectedOptionsRef = useRef([] as PickerOptionItem[]) + const isConfirmEvent = useRef(false) + const pickerRef = useRef(null) + const [refs, setRefs] = useRefs() useEffect(() => { if (innerVisible) { - init() + console.log('selectedValue变更', selectedValue, innerValue) + setInnerValue(selectedValue) + setInnerOptions(options as PickerOptions[]) } - }, [options, innerVisible]) + }, [options, selectedValue, innerOptions, innerVisible]) - // 选中值进行修改 - useEffect(() => { - if (!innerVisible) { - return + const onChangeItem = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('onChangeItem1', value, index, selectedOptions) + if (selectedOptions?.length) { + selectedOptionsRef.current = selectedOptions } - Taro.getEnv() !== 'WEB' && setCurrentValue(defaultValuesConvert()) - onChange && onChange(setSelectedOptions(), innerValue, columnIndex) - }, [innerValue, columnsList, innerVisible]) - - const setSelectedOptions = () => { - const options: PickerOption[] = [] - let currOptions = [] - columnsList.forEach((columnOptions: PickerOption[], index: number) => { - currOptions = columnOptions.filter( - (item) => item.value === innerValue[index] - ) - if (currOptions[0]) { - options.push(currOptions[0]) - } else { - columnOptions[0] && options.push(columnOptions[0]) - } - }) - return options - } - - const defaultValuesConvert = () => { - const defaultIndexs: number[] = [] - if (innerValue.length > 0) { - innerValue.forEach((value, index) => { - for (let i = 0; i < columnsList?.[index]?.length; i++) { - if (columnsList[index][i].value === value) { - defaultIndexs.push(i) - break - } - } - }) - } else if (columnsList && columnsList.length > 0) { - columnsList.forEach((item) => { - defaultIndexs.push(0) - item.length > 0 && selectedValue.push(item[0].value) + if (isEqual(value, innerValue)) return + console.log( + 'onChangeItem2', + innerVisible, + innerValue, + value, + index, + selectedOptions + ) + setInnerValue(value) + innerVisible && + onChange?.({ + selectedOptions, + value, + index, }) - } - - return defaultIndexs } - // 更新已选择数据 - const chooseItem = (columnOptions: PickerOption, columnIndex: number) => { - const values: any = [] - const start = columnIndex - if (columnOptions && Object.keys(columnOptions).length) { - // 切换数据后,数据有变动才触发。 - if (values[columnIndex] !== columnOptions.value) { - if (columnsType() === 'cascade') { - values[columnIndex] = columnOptions.value || '' - while (columnOptions?.children?.[0]) { - values[columnIndex + 1] = columnOptions.children[0].value - columnIndex++ - columnOptions = columnOptions.children[0] - } - // 当前改变列的下一列 children 值为空 - if (columnOptions?.children?.length) { - values[columnIndex + 1] = '' - } - const combineResult = [ - ...innerValue.slice(0, start), - ...values.splice(start), - ] - setInnerValue(combineResult) - setColumnsList(normalListData(combineResult) as PickerOption[][]) - } else { - setInnerValue((data: (number | string)[]) => { - const cdata: (number | string)[] = [...data] - cdata[columnIndex] = Object.prototype.hasOwnProperty.call( - columnOptions, - 'value' - ) - ? columnOptions.value - : '' - return cdata - }) - } - setColumnIndex(columnIndex) - } - } - } - // 点击确定 - const confirm = () => { + const onConfirmEvent = () => { let moving = false refs.forEach((ref: any) => { if (ref.moving) moving = true @@ -313,6 +179,12 @@ const InternalPicker: ForwardRefRenderFunction< }, 0) } + const onCancelEvent = () => { + setInnerValue(selectedValue) + onCancel?.() + setInnerVisible(false) + } + const renderTitleBar = () => { return ( @@ -320,8 +192,7 @@ const InternalPicker: ForwardRefRenderFunction< className={`${classPrefix}-cancel-btn`} onClick={(e) => { e.stopPropagation() - onCancel?.() - setInnerVisible(false) + onCancelEvent() }} > {locale?.cancel} @@ -331,7 +202,7 @@ const InternalPicker: ForwardRefRenderFunction< className={`${classPrefix}-confirm-btn`} onClick={(e) => { e.stopPropagation() - confirm() + onConfirmEvent() }} > {locale.confirm} @@ -340,109 +211,51 @@ const InternalPicker: ForwardRefRenderFunction< ) } - const [, setPickingStatus] = useState(false) - - const pickerStart = () => { - setPickingStatus(true) - } - - const pickerEnd = () => { - setPickingStatus(false) - } - - const pickerChange = (data: any) => { - const prevDefaultValue = currentValue - let changeIndex = 0 - // 判断变化的是第几个 - const list = data.detail.value - for (let i = 0, len = list.length; i < len; i++) { - if (prevDefaultValue[i] !== list[i]) { - changeIndex = i - break - } - } - - // 选择的是哪个 option - chooseItem( - columnsList[changeIndex][data.detail.value[changeIndex]], - changeIndex + const renderPickerElement = () => { + return ( + + {renderTitleBar()} + {typeof children !== 'function' && children} + + { + onChangeItem({ value, index, selectedOptions }) + }} + /> + + ) } return ( <> {typeof children === 'function' && children(selectedValue)} - { - if (closeOnOverlayClick) { - props.onCancel?.() - setInnerVisible(false) - } - }} - afterClose={() => { - afterClose?.(setSelectedOptions(), innerValue, pickerRef) - }} - > - - {renderTitleBar()} - {typeof children !== 'function' && children} - - {Taro.getEnv() === 'WEB' ? ( - columnsList?.map((item, index) => { - return ( - - chooseItem(value, index) - } - duration={duration} - key={index} - keyIndex={index} - itemShow={visible} - /> - ) - }) - ) : ( - - {columnsList?.map((columnOptions, index) => { - return ( - - {columnOptions.map((item, index) => { - return ( - - <>{item.text} - - ) - })} - - ) - })} - - )} - - - + {innerVisible ? ( + { + if (!closeOnOverlayClick) return + onCancelEvent() + }} + > + {renderPickerElement()} + + + ) : null} ) } -const Picker = React.forwardRef>(InternalPicker) +const Picker = React.forwardRef>(InternalPicker) export default Picker diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 1c97234e54..f06de99b78 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -14,9 +14,10 @@ import { PickerValue, PickerOptionItem, PickerOnChangeCallbackParameter, + SafeArea, + Popup, + PopupProps, } from '@nutui/nutui-react' -import { Popup, PopupProps } from '@/packages/popup/popup' -import { SafeArea } from '@/packages/safearea/safearea' import useRefs from '@/utils/use-refs' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/utils/use-props-value' @@ -118,60 +119,49 @@ const InternalPicker: ForwardRefRenderFunction< } useImperativeHandle(ref, () => actions) - const [innerValue, setInnerValue] = useState(selectedValue) + const [innerValue, setInnerValue] = useState([...selectedValue]) const [innerOptions, setInnerOptions] = useState([]) const selectedOptionsRef = useRef([] as PickerOptionItem[]) - const changeIndex = useRef(-1) const isConfirmEvent = useRef(false) const pickerRef = useRef(null) const [refs, setRefs] = useRefs() useEffect(() => { if (innerVisible) { - console.log('selectedValue变更', selectedValue) + console.log('selectedValue变更', selectedValue, innerValue) setInnerValue(selectedValue) setInnerOptions(options as PickerOptions[]) } - }, [options, innerVisible, selectedValue, innerOptions]) - - useEffect(() => { - console.log( - 'innerValue变更onChange', - innerValue, - innerVisible, - 'innerVisible为false不执行' - ) - innerVisible && - onChange && - onChange({ - selectedOptions: selectedOptionsRef.current, - value: innerValue, - index: changeIndex.current, - }) - }, [innerValue, innerVisible]) - - // const selectedOptions = useMemo(() => { - // console.log('selectedOptions.current', selectedOptionsObj.current) - // return selectedOptionsObj.current - // }, [selectedOptionsObj.current]) + }, [options, selectedValue, innerOptions, innerVisible]) const onChangeItem = ({ value, index, selectedOptions, }: PickerOnChangeCallbackParameter) => { + console.log('onChangeItem1', value, index, selectedOptions) if (selectedOptions?.length) { selectedOptionsRef.current = selectedOptions } - - console.log('selectedOptions', selectedOptions) if (isEqual(value, innerValue)) return - console.log('onChangeItem', value, innerValue, index, selectedOptions) + console.log( + 'onChangeItem2', + innerVisible, + innerValue, + value, + index, + selectedOptions + ) setInnerValue(value) - changeIndex.current = index + innerVisible && + onChange?.({ + selectedOptions, + value, + index, + }) } - const confirm = () => { + const onConfirmEvent = () => { let moving = false refs.forEach((ref: any) => { if (ref.moving) moving = true @@ -188,6 +178,12 @@ const InternalPicker: ForwardRefRenderFunction< }, 0) } + const onCancelEvent = () => { + setInnerValue(selectedValue) + onCancel?.() + setInnerVisible(false) + } + const renderTitleBar = () => { return (
@@ -195,8 +191,7 @@ const InternalPicker: ForwardRefRenderFunction< className={`${classPrefix}-cancel-btn`} onClick={(e) => { e.stopPropagation() - onCancel?.() - setInnerVisible(false) + onCancelEvent() }} > {locale?.cancel} @@ -206,7 +201,7 @@ const InternalPicker: ForwardRefRenderFunction< className={`${classPrefix}-confirm-btn`} onClick={(e) => { e.stopPropagation() - confirm() + onConfirmEvent() }} > {locale.confirm} @@ -222,8 +217,9 @@ const InternalPicker: ForwardRefRenderFunction< {typeof children !== 'function' && children}
) } - return ( <> {typeof children === 'function' && children(selectedValue)} - { - if (!closeOnOverlayClick) return - props.onCancel?.() - setInnerVisible(false) - }} - afterClose={() => { - // afterClose?.(setSelectedOptions(), innerValue, pickerRef) - }} - > - {renderPickerElement()} - - + {innerVisible ? ( + { + if (!closeOnOverlayClick) return + onCancelEvent() + }} + > + {renderPickerElement()} + + + ) : null} ) } diff --git a/src/packages/picker/pickerpanel.taro.tsx b/src/packages/picker/pickerpanel.taro.tsx deleted file mode 100644 index 8a5975c1c6..0000000000 --- a/src/packages/picker/pickerpanel.taro.tsx +++ /dev/null @@ -1,322 +0,0 @@ -import React, { - useState, - useEffect, - useRef, - ForwardRefRenderFunction, - useImperativeHandle, -} from 'react' -import { View } from '@tarojs/components' -import { PickerOption } from './types' -import { useTouch } from '@/utils/use-touch' -import { getRectByTaro } from '@/utils/get-rect-by-taro' -import { passiveSupported } from '@/utils/supports-passive' - -interface PickerPanelProps { - keyIndex?: number - defaultValue?: string | number - options?: PickerOption[] - threeDimensional: boolean - duration: number | string - itemShow: boolean - chooseItem?: (val: PickerOption, idx: number) => void -} - -const InternalPickerPanel: ForwardRefRenderFunction< - { stopMomentum: () => void; moving: boolean }, - Partial -> = (props, ref) => { - const { - keyIndex = 0, - defaultValue, - options = [], - threeDimensional = true, - duration = 1000, - itemShow = false, - chooseItem, - } = props - - const touch = useTouch() - const DEFAULT_DURATION = 200 - // 触发惯性滑动条件: - // 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move - // 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动 - const INERTIA_TIME = 300 - const INERTIA_DISTANCE = 15 - const [currIndex, setCurrIndex] = useState(1) - const lineSpacing = useRef(36) - const [touchTime, setTouchTime] = useState(0) - const [touchDeg, setTouchDeg] = useState('0deg') - const rotation = 20 - const moving = useRef(false) - let timer: number | undefined - - const listRef = useRef(null) - const rollerRef = useRef(null) - const pickerPanelRef = useRef(null) - - const [startTime, setStartTime] = useState(0) - const [startY, setStartY] = useState(0) - - const transformY = useRef(0) - const [scrollDistance, setScrollDistance] = useState(0) - - const isHidden = (index: number) => { - if (index >= currIndex + 8 || index <= currIndex - 8) { - return true - } - return false - } - - const setTransform = ( - type: string, - deg: string, - time = DEFAULT_DURATION, - translateY = 0 - ) => { - let nTime = time - if (type !== 'end') { - nTime = 0 - } - setTouchTime(nTime) - setTouchDeg(deg) - setScrollDistance(translateY) - } - - const setMove = (move: number, type?: string, time?: number) => { - let updateMove = move + transformY.current - if (type === 'end') { - // 限定滚动距离 - if (updateMove > 0) { - updateMove = 0 - } - if (updateMove < -(options.length - 1) * lineSpacing.current) { - updateMove = -(options.length - 1) * lineSpacing.current - } - - // 设置滚动距离为lineSpacing的倍数值 - const endMove = - Math.round(updateMove / lineSpacing.current) * lineSpacing.current - const deg = `${ - (Math.abs(Math.round(endMove / lineSpacing.current)) + 1) * rotation - }deg` - - setTransform(type, deg, time, endMove) - setCurrIndex(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) - } else { - let deg = 0 - const currentDeg = (-updateMove / lineSpacing.current + 1) * rotation - - // picker 滚动的最大角度 - const maxDeg = (options.length + 1) * rotation - const minDeg = 0 - deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - - if (minDeg < deg && deg < maxDeg) { - setTransform('', `${deg}deg`, undefined, updateMove) - setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) - } - } - } - - const setChooseValue = (move: number) => { - chooseItem?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex) - } - - // 开始滚动 - const touchStart = (event: React.TouchEvent) => { - touch.start(event) - setStartY(touch.deltaY.current) - setStartTime(Date.now()) - transformY.current = scrollDistance - } - - const touchMove = (event: React.TouchEvent) => { - touch.move(event) - if ((touch as any).isVertical) { - moving.current = true - preventDefault(event, true) - } - const move = touch.deltaY.current - startY - setMove(move) - } - - const touchEnd = () => { - if (!moving.current) return - const move = touch.deltaY.current - startY - const moveTime = Date.now() - startTime - // 区分是否为惯性滚动 - if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { - // 惯性滚动 - const distance = momentum(move, moveTime) - setMove(distance, 'end', +duration) - } else { - setMove(move, 'end') - } - setTimeout(() => { - touch.reset() - }, 0) - } - - // 惯性滚动 距离 - const momentum = (distance: number, duration: number) => { - let nDistance = distance - // 惯性滚动的速度 - const speed = Math.abs(nDistance / duration) - // 惯性滚动的距离 - nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) - return nDistance - } - - const modifyStatus = (type?: boolean, val?: string | number) => { - const value = val || defaultValue - let index = -1 - if (value) { - options.some((item, idx) => { - if (item.value === value) { - index = idx - return true - } - return false - }) - } else { - options.forEach((item, i) => { - if (item.value === defaultValue) { - index = i - } - }) - } - - setCurrIndex(index === -1 ? 1 : index + 1) - const move = index === -1 ? 0 : index * lineSpacing.current - type && setChooseValue(-move) - setMove(-move) - } - - // 惯性滚动结束 - const stopMomentum = () => { - moving.current = false - setTouchTime(0) - setChooseValue(scrollDistance) - } - // 阻止默认事件 - const preventDefault = ( - event: React.TouchEvent, - isStopPropagation?: boolean - ) => { - event.preventDefault() - if (isStopPropagation) { - event.stopPropagation() - } - } - - const getReference = async () => { - const refe = await getRectByTaro(listRef?.current) - lineSpacing.current = refe.height ? refe.height : 36 - } - - const touchRollerStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `rotate3d(1, 0, 0, ${touchDeg})`, - } - } - const touchTileStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `translate3d(0, ${scrollDistance}px, 0)`, - } - } - - useEffect(() => { - setScrollDistance(0) - transformY.current = 0 - modifyStatus(false) - return () => { - clearTimeout(timer) - } - }, [options]) - - useEffect(() => { - if (itemShow) { - setTimeout(() => { - getReference() - }, 200) - } - }, [itemShow]) - - useImperativeHandle(ref, () => ({ - stopMomentum, - moving: moving.current, - })) - - useEffect(() => { - const eventOptions = passiveSupported - ? { passive: false, once: true } - : false - const element = pickerPanelRef.current - element.addEventListener('touchstart', touchStart, eventOptions) - element.addEventListener('touchmove', touchMove, eventOptions) - element.addEventListener('touchend', touchEnd, eventOptions) - return () => { - element.removeEventListener('touchstart', touchStart, eventOptions) - element.removeEventListener('touchmove', touchMove, eventOptions) - element.removeEventListener('touchend', touchEnd, eventOptions) - } - }) - - return ( - - - {/* 3D 效果 */} - {threeDimensional && - options.map((item, index) => { - return ( - - <>{item.text} - - ) - })} - {/* 平铺 */} - {!threeDimensional && - options.map((item, index) => { - return ( - - <>{item.text} - - ) - })} - - - - - ) -} -const PickerPanel = React.forwardRef< - { stopMomentum: () => void; moving: boolean }, - Partial ->(InternalPickerPanel) -export default PickerPanel diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 99fda23f72..dc0b2b56a9 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -55,7 +55,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< const currentLineSpacing = computedStyle.getPropertyValue( '--nutui-picker-item-height' ) - console.log('currentLineSpacing', currentLineSpacing) currentLineSpacing && (lineSpacing.current = parseFloat(currentLineSpacing)) } diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index 9bc0ed45e9..5a5e71ecc5 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -59,12 +59,12 @@ const InternalPickerView: ForwardRefRenderFunction< * 数据类型:级联、多列 */ const columnsType = useMemo(() => { - const [firstColumn] = options + const [firstColumn] = props.options as PickerOptions[] if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { return 'cascade' } return 'multiple' - }, [options]) + }, [props.options]) const formatCascadeOptions = ( options: PickerOptions, @@ -104,20 +104,20 @@ const InternalPickerView: ForwardRefRenderFunction< } const formatOptions = useMemo(() => { - if (columnsType === 'multiple') { - return options - } if (columnsType === 'cascade') { - return formatCascadeOptions(options[0] as PickerOptions, innerValue) + return formatCascadeOptions( + props?.options?.[0] as PickerOptions, + innerValue + ) } - return options + return props.options }, [innerValue, options, columnsType]) useEffect(() => { - if (options !== innerOptions) { - setInnerOptions(formatOptions) + if (props.options !== innerOptions) { + setInnerOptions(formatOptions as PickerOptions[]) } - }, [options, innerValue]) + }, [props.options, innerValue]) useEffect(() => { if (selectedValue !== innerValue) { @@ -127,7 +127,6 @@ const InternalPickerView: ForwardRefRenderFunction< const handleSelect = useCallback( (option: PickerOptionItem, index: number) => { - console.log(innerValue, options, columnsType, innerOptions) const newValue = option?.value if (!newValue || innerValue[index] === newValue) return changeIndex.current = index @@ -154,20 +153,19 @@ const InternalPickerView: ForwardRefRenderFunction< ...innerValue.slice(0, startIndex), ...values.splice(startIndex), ] - console.log('combineResult', combineResult) setInnerValue([...combineResult]) - + const optionFirst = props?.options?.[0] as PickerOptionItem[] if ( !isEqual( - formatCascadeOptions(options[0], combineResult), + formatCascadeOptions(optionFirst, combineResult), innerOptions ) ) { - setInnerOptions(formatCascadeOptions(options[0], combineResult)) + setInnerOptions(formatCascadeOptions(optionFirst, combineResult)) } } }, - [innerValue, options, columnsType, innerOptions] + [innerValue, props.options, columnsType, innerOptions] ) const selectedOptions = useMemo(() => { @@ -187,18 +185,20 @@ const InternalPickerView: ForwardRefRenderFunction< value: innerValue, index: changeIndex.current, selectedOptions, + innerOptions, }) onChange?.({ value: innerValue, index: changeIndex.current, selectedOptions, }) - }, [innerValue, onChange]) + }, [innerValue, selectedOptions, onChange]) return ( {innerOptions.map((item, index) => ( { - const [firstColumn] = options + const [firstColumn] = props.options as PickerOptions[] if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { return 'cascade' } return 'multiple' - }, [options]) + }, [props.options]) const formatCascadeOptions = ( options: PickerOptions, @@ -103,20 +103,20 @@ const InternalPickerView: ForwardRefRenderFunction< } const formatOptions = useMemo(() => { - if (columnsType === 'multiple') { - return options - } if (columnsType === 'cascade') { - return formatCascadeOptions(options[0] as PickerOptions, innerValue) + return formatCascadeOptions( + props?.options?.[0] as PickerOptions, + innerValue + ) } - return options + return props.options }, [innerValue, options, columnsType]) useEffect(() => { - if (options !== innerOptions) { - setInnerOptions(formatOptions) + if (props.options !== innerOptions) { + setInnerOptions(formatOptions as PickerOptions[]) } - }, [options, innerValue]) + }, [props.options, innerValue]) useEffect(() => { if (selectedValue !== innerValue) { @@ -126,7 +126,6 @@ const InternalPickerView: ForwardRefRenderFunction< const handleSelect = useCallback( (option: PickerOptionItem, index: number) => { - console.log(innerValue, options, columnsType, innerOptions) const newValue = option?.value if (!newValue || innerValue[index] === newValue) return changeIndex.current = index @@ -153,20 +152,19 @@ const InternalPickerView: ForwardRefRenderFunction< ...innerValue.slice(0, startIndex), ...values.splice(startIndex), ] - console.log('combineResult', combineResult) setInnerValue([...combineResult]) - + const optionFirst = props?.options?.[0] as PickerOptionItem[] if ( !isEqual( - formatCascadeOptions(options[0], combineResult), + formatCascadeOptions(optionFirst, combineResult), innerOptions ) ) { - setInnerOptions(formatCascadeOptions(options[0], combineResult)) + setInnerOptions(formatCascadeOptions(optionFirst, combineResult)) } } }, - [innerValue, options, columnsType, innerOptions] + [innerValue, props.options, columnsType, innerOptions] ) const selectedOptions = useMemo(() => { @@ -186,18 +184,20 @@ const InternalPickerView: ForwardRefRenderFunction< value: innerValue, index: changeIndex.current, selectedOptions, + innerOptions, }) onChange?.({ value: innerValue, index: changeIndex.current, selectedOptions, }) - }, [innerValue, onChange]) + }, [innerValue, selectedOptions, onChange]) return (
{innerOptions.map((item, index) => ( any options: PickerOptions[] value?: PickerValue[] defaultValue?: PickerValue[] From b0b635cd3414d05f858dd22ed481b7bfa92d9916 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 11 Feb 2025 20:18:40 +0800 Subject: [PATCH 10/54] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E5=86=97?= =?UTF-8?q?=E4=BD=99=E4=BB=A3=E7=A0=81+=E8=A7=A3=E5=86=B3form=E4=B8=AD?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E6=9C=AA=E7=BB=93=E6=9D=9F=E6=AD=BB=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/picker/picker.taro.tsx | 21 +++++---------------- src/packages/picker/picker.tsx | 22 +++++----------------- 2 files changed, 10 insertions(+), 33 deletions(-) diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 3f1fcb555f..b0b5e1d945 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -2,7 +2,6 @@ import React, { useState, useEffect, useRef, - RefObject, ForwardRefRenderFunction, useImperativeHandle, } from 'react' @@ -48,11 +47,6 @@ export interface PickerProps extends Omit { selectedOptions: PickerOptions, selectedValue: PickerValue[] ) => void - afterClose?: ( - selectedOptions: PickerOptions, - selectedValue: PickerOptions[], - pickerRef: RefObject - ) => void onChange?: (args0: PickerOnChangeCallbackParameter) => void children?: any } @@ -121,10 +115,9 @@ const InternalPicker: ForwardRefRenderFunction< useImperativeHandle(ref, () => actions) const [innerValue, setInnerValue] = useState([...selectedValue]) + const innerValueRef = useRef(innerValue) const [innerOptions, setInnerOptions] = useState([]) const selectedOptionsRef = useRef([] as PickerOptionItem[]) - const isConfirmEvent = useRef(false) - const pickerRef = useRef(null) const [refs, setRefs] = useRefs() useEffect(() => { @@ -144,7 +137,7 @@ const InternalPicker: ForwardRefRenderFunction< if (selectedOptions?.length) { selectedOptionsRef.current = selectedOptions } - if (isEqual(value, innerValue)) return + if (isEqual(value, innerValueRef.current)) return console.log( 'onChangeItem2', innerVisible, @@ -153,6 +146,7 @@ const InternalPicker: ForwardRefRenderFunction< index, selectedOptions ) + innerValueRef.current = value setInnerValue(value) innerVisible && onChange?.({ @@ -168,15 +162,10 @@ const InternalPicker: ForwardRefRenderFunction< if (ref.moving) moving = true ref.stopMomentum() }) - if (moving) { - isConfirmEvent.current = true - } else { + if (!moving) { setSelectedValue(innerValue, true) setInnerVisible(false) } - setTimeout(() => { - isConfirmEvent.current = false - }, 0) } const onCancelEvent = () => { @@ -216,7 +205,7 @@ const InternalPicker: ForwardRefRenderFunction< {renderTitleBar()} {typeof children !== 'function' && children} - + { selectedOptions: PickerOptions, selectedValue: PickerValue[] ) => void - afterClose?: ( - selectedOptions: PickerOptions, - selectedValue: PickerOptions[], - pickerRef: RefObject - ) => void onChange?: (args0: PickerOnChangeCallbackParameter) => void children?: any } @@ -84,7 +78,6 @@ const InternalPicker: ForwardRefRenderFunction< onConfirm, onCancel, onClose, - afterClose, onChange, ...rest } = { ...defaultProps, ...props } @@ -120,10 +113,9 @@ const InternalPicker: ForwardRefRenderFunction< useImperativeHandle(ref, () => actions) const [innerValue, setInnerValue] = useState([...selectedValue]) + const innerValueRef = useRef(innerValue) const [innerOptions, setInnerOptions] = useState([]) const selectedOptionsRef = useRef([] as PickerOptionItem[]) - const isConfirmEvent = useRef(false) - const pickerRef = useRef(null) const [refs, setRefs] = useRefs() useEffect(() => { @@ -143,7 +135,7 @@ const InternalPicker: ForwardRefRenderFunction< if (selectedOptions?.length) { selectedOptionsRef.current = selectedOptions } - if (isEqual(value, innerValue)) return + if (isEqual(value, innerValueRef.current)) return console.log( 'onChangeItem2', innerVisible, @@ -152,6 +144,7 @@ const InternalPicker: ForwardRefRenderFunction< index, selectedOptions ) + innerValueRef.current = value setInnerValue(value) innerVisible && onChange?.({ @@ -167,15 +160,10 @@ const InternalPicker: ForwardRefRenderFunction< if (ref.moving) moving = true ref.stopMomentum() }) - if (moving) { - isConfirmEvent.current = true - } else { + if (!moving) { setSelectedValue(innerValue, true) setInnerVisible(false) } - setTimeout(() => { - isConfirmEvent.current = false - }, 0) } const onCancelEvent = () => { @@ -215,7 +203,7 @@ const InternalPicker: ForwardRefRenderFunction<
{renderTitleBar()} {typeof children !== 'function' && children} -
+
Date: Wed, 12 Feb 2025 11:11:13 +0800 Subject: [PATCH 11/54] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4console?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/picker/picker.scss | 130 -------------------- src/packages/picker/picker.taro.tsx | 10 -- src/packages/picker/picker.tsx | 10 -- src/packages/pickerview/pickerview.taro.tsx | 6 - src/packages/pickerview/pickerview.tsx | 6 - 5 files changed, 162 deletions(-) diff --git a/src/packages/picker/picker.scss b/src/packages/picker/picker.scss index 6516efc1bf..f73182d5d8 100644 --- a/src/packages/picker/picker.scss +++ b/src/packages/picker/picker.scss @@ -36,135 +36,5 @@ &-panel { display: flex; - position: relative; } - - &-indicator { - position: absolute; - top: 108px; - height: $picker-item-height; - width: 100%; - border: $picker-item-active-line-border; - border-left: 0; - border-right: 0; - color: $picker-item-text-color; - font-size: $picker-item-text-font-size; - z-index: 3; - - &-taro { - height: $picker-item-height; - border: 0; - - &::before, - &::after { - border: $picker-item-active-line-border; - border-left: 0; - border-right: 0; - } - } - } - - &-list { - flex: 1; - position: relative; - height: $picker-list-height; - overflow: hidden; - text-align: center; - } - - &-list-panel { - transform-style: preserve-3d; - } - - &-mask { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: $picker-mask-background; - background-position: top, bottom; - background-size: 100% 108px; - background-repeat: no-repeat; - transform: translateZ(0); - z-index: 3; - } - - &-view-panel { - height: $picker-list-height; - flex-grow: 1; - } - - &-content, - &-roller { - position: absolute; - top: 108px; - width: 100%; - height: $picker-item-height; - } - - &-content { - background: #fff; - z-index: 2; - overflow: hidden; - } - - &-item, - &-roller-item { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - height: $picker-item-height; - line-height: $picker-item-height; - color: $picker-item-text-color; - font-size: $picker-item-text-font-size; - text-align: center; - } - - &-item { - font-size: 16px; - background: #fff; - } - - &-roller { - z-index: 1; - transform-style: preserve-3d; - } - - &-roller-item { - backface-visibility: hidden; - -moz-backface-visibility: hidden; - -webkit-backface-visibility: hidden; - position: absolute; - top: 0; - width: 100%; - } - - &-roller-item-title { - display: block; - width: 100%; - height: $picker-item-height; - line-height: $picker-item-height; - text-align: center; - color: $color-title; - font-size: $font-size-base; - } - - &-roller-item-hidden { - visibility: hidden; - opacity: 0; - } - - &-placeholder { - height: 1px; - } -} - -[dir='rtl'] .nut-picker-mask, -.nut-rtl .nut-picker-mask { - background-image: var( - --picker-mask-background, - linear-gradient(-180deg, var(--nutui-white-12), var(--nutui-white-7)), - linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7)) - ); } diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index b0b5e1d945..27c8d782c2 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -122,7 +122,6 @@ const InternalPicker: ForwardRefRenderFunction< useEffect(() => { if (innerVisible) { - console.log('selectedValue变更', selectedValue, innerValue) setInnerValue(selectedValue) setInnerOptions(options as PickerOptions[]) } @@ -133,19 +132,10 @@ const InternalPicker: ForwardRefRenderFunction< index, selectedOptions, }: PickerOnChangeCallbackParameter) => { - console.log('onChangeItem1', value, index, selectedOptions) if (selectedOptions?.length) { selectedOptionsRef.current = selectedOptions } if (isEqual(value, innerValueRef.current)) return - console.log( - 'onChangeItem2', - innerVisible, - innerValue, - value, - index, - selectedOptions - ) innerValueRef.current = value setInnerValue(value) innerVisible && diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 5e75bc3968..c0f295ffa8 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -120,7 +120,6 @@ const InternalPicker: ForwardRefRenderFunction< useEffect(() => { if (innerVisible) { - console.log('selectedValue变更', selectedValue, innerValue) setInnerValue(selectedValue) setInnerOptions(options as PickerOptions[]) } @@ -131,19 +130,10 @@ const InternalPicker: ForwardRefRenderFunction< index, selectedOptions, }: PickerOnChangeCallbackParameter) => { - console.log('onChangeItem1', value, index, selectedOptions) if (selectedOptions?.length) { selectedOptionsRef.current = selectedOptions } if (isEqual(value, innerValueRef.current)) return - console.log( - 'onChangeItem2', - innerVisible, - innerValue, - value, - index, - selectedOptions - ) innerValueRef.current = value setInnerValue(value) innerVisible && diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index 5a5e71ecc5..06f20401f6 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -181,12 +181,6 @@ const InternalPickerView: ForwardRefRenderFunction< }, [innerOptions, innerValue]) useEffect(() => { - console.log('onChange', { - value: innerValue, - index: changeIndex.current, - selectedOptions, - innerOptions, - }) onChange?.({ value: innerValue, index: changeIndex.current, diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index e6793e036f..daeff9c23c 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -180,12 +180,6 @@ const InternalPickerView: ForwardRefRenderFunction< }, [innerOptions, innerValue]) useEffect(() => { - console.log('onChange', { - value: innerValue, - index: changeIndex.current, - selectedOptions, - innerOptions, - }) onChange?.({ value: innerValue, index: changeIndex.current, From 7a279340c5187046714ed5a8182616329bef214b Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 11:16:23 +0800 Subject: [PATCH 12/54] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0pickerview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/pickerview/demo.taro.tsx | 6 + src/packages/pickerview/demo.tsx | 6 + src/packages/pickerview/demos/h5/demo1.tsx | 4 +- src/packages/pickerview/demos/h5/demo2.tsx | 4 +- src/packages/pickerview/demos/h5/demo3.tsx | 4 +- src/packages/pickerview/demos/h5/demo4.tsx | 4 +- src/packages/pickerview/demos/h5/demo5.tsx | 4 +- src/packages/pickerview/demos/h5/demo6.tsx | 80 +++++++++ src/packages/pickerview/demos/taro/demo1.tsx | 4 +- src/packages/pickerview/demos/taro/demo2.tsx | 4 +- src/packages/pickerview/demos/taro/demo3.tsx | 4 +- src/packages/pickerview/demos/taro/demo4.tsx | 4 +- src/packages/pickerview/demos/taro/demo5.tsx | 4 +- src/packages/pickerview/demos/taro/demo6.tsx | 80 +++++++++ src/packages/pickerview/doc.en-US.md | 94 +++++++++++ src/packages/pickerview/doc.md | 10 +- src/packages/pickerview/doc.taro.md | 93 +++++++++++ src/packages/pickerview/doc.zh-TW.md | 93 +++++++++++ src/packages/pickerview/index.taro.ts | 9 +- src/packages/pickerview/index.ts | 9 +- src/packages/pickerview/pickerroller.taro.tsx | 10 +- src/packages/pickerview/pickerroller.tsx | 7 +- src/packages/pickerview/pickerview.taro.tsx | 157 +++++++++++++++--- src/packages/pickerview/pickerview.tsx | 157 +++++++++++++++--- src/packages/pickerview/types.ts | 11 +- 25 files changed, 771 insertions(+), 91 deletions(-) create mode 100644 src/packages/pickerview/demos/h5/demo6.tsx create mode 100644 src/packages/pickerview/demos/taro/demo6.tsx create mode 100644 src/packages/pickerview/doc.en-US.md create mode 100644 src/packages/pickerview/doc.taro.md create mode 100644 src/packages/pickerview/doc.zh-TW.md diff --git a/src/packages/pickerview/demo.taro.tsx b/src/packages/pickerview/demo.taro.tsx index d05584575b..854ebb089d 100644 --- a/src/packages/pickerview/demo.taro.tsx +++ b/src/packages/pickerview/demo.taro.tsx @@ -8,6 +8,7 @@ import Demo2 from './demos/taro/demo2' import Demo3 from './demos/taro/demo3' import Demo4 from './demos/taro/demo4' import Demo5 from './demos/taro/demo5' +import Demo6 from './demos/taro/demo6' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -17,6 +18,7 @@ const PickerViewDemo = () => { multiColumn: '多列', controlled: '受控', tiled: '平铺', + cascade: '级联', }, 'en-US': { title: 'Basic Usage', @@ -24,6 +26,7 @@ const PickerViewDemo = () => { multiColumn: 'Multi Column', controlled: 'Controlled', tiled: 'Tiled', + cascade: 'Cascade', }, 'zh-TW': { title: '基礎用法', @@ -31,6 +34,7 @@ const PickerViewDemo = () => { multiColumn: '多列', controlled: '受控', tiled: '平鋪', + cascade: '級聯', }, }) return ( @@ -47,6 +51,8 @@ const PickerViewDemo = () => { {translated.tiled} +

{translated.cascade}

+ ) diff --git a/src/packages/pickerview/demo.tsx b/src/packages/pickerview/demo.tsx index 99de463ecd..b94f91de80 100644 --- a/src/packages/pickerview/demo.tsx +++ b/src/packages/pickerview/demo.tsx @@ -5,6 +5,7 @@ import Demo2 from './demos/h5/demo2' import Demo3 from './demos/h5/demo3' import Demo4 from './demos/h5/demo4' import Demo5 from './demos/h5/demo5' +import Demo6 from './demos/h5/demo6' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -14,6 +15,7 @@ const PickerViewDemo = () => { multiColumn: '多列', controlled: '受控', tiled: '平铺', + cascade: '级联', }, 'en-US': { title: 'Basic Usage', @@ -21,6 +23,7 @@ const PickerViewDemo = () => { multiColumn: 'Multi Column', controlled: 'Controlled', tiled: 'Tiled', + cascade: 'Cascade', }, 'zh-TW': { title: '基礎用法', @@ -28,6 +31,7 @@ const PickerViewDemo = () => { multiColumn: '多列', controlled: '受控', tiled: '平鋪', + cascade: '級聯', }, }) return ( @@ -42,6 +46,8 @@ const PickerViewDemo = () => {

{translated.tiled}

+

{translated.cascade}

+
) } diff --git a/src/packages/pickerview/demos/h5/demo1.tsx b/src/packages/pickerview/demos/h5/demo1.tsx index cacf341036..dbadb8c5a7 100644 --- a/src/packages/pickerview/demos/h5/demo1.tsx +++ b/src/packages/pickerview/demos/h5/demo1.tsx @@ -22,8 +22,8 @@ const Demo1 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/demos/h5/demo2.tsx b/src/packages/pickerview/demos/h5/demo2.tsx index dd16c8a92e..dff4a404b0 100644 --- a/src/packages/pickerview/demos/h5/demo2.tsx +++ b/src/packages/pickerview/demos/h5/demo2.tsx @@ -23,8 +23,8 @@ const Demo1 = () => { style={{ '--nutui-picker-item-height': '28px' }} options={listData} defaultValue={[1]} - onChange={(value, selectOptions) => { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/demos/h5/demo3.tsx b/src/packages/pickerview/demos/h5/demo3.tsx index 555c655e39..d23f719786 100644 --- a/src/packages/pickerview/demos/h5/demo3.tsx +++ b/src/packages/pickerview/demos/h5/demo3.tsx @@ -25,8 +25,8 @@ const Demo3 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) if (isEqual(value, ['Tuesday', 'Afternoon'])) { setValue(['Monday', 'Evening']) } diff --git a/src/packages/pickerview/demos/h5/demo4.tsx b/src/packages/pickerview/demos/h5/demo4.tsx index e205b5513a..320c7cc39e 100644 --- a/src/packages/pickerview/demos/h5/demo4.tsx +++ b/src/packages/pickerview/demos/h5/demo4.tsx @@ -24,8 +24,8 @@ const Demo1 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) if (value[0] === 3) { setValue([1]) } diff --git a/src/packages/pickerview/demos/h5/demo5.tsx b/src/packages/pickerview/demos/h5/demo5.tsx index 27741464db..4bf68a2261 100644 --- a/src/packages/pickerview/demos/h5/demo5.tsx +++ b/src/packages/pickerview/demos/h5/demo5.tsx @@ -24,8 +24,8 @@ const Demo1 = () => { options={listData} threeDimensional={false} duration={500} - onChange={(value, selectOptions) => { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/demos/h5/demo6.tsx b/src/packages/pickerview/demos/h5/demo6.tsx new file mode 100644 index 0000000000..44aa46d8ae --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo6.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import { PickerView, Cell } from '@nutui/nutui-react' + +const Demo6 = () => { + const listData = [ + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], + ] + + return ( + <> + + { + console.log('onChange', value, selectedOptions) + }} + /> + + + ) +} +export default Demo6 diff --git a/src/packages/pickerview/demos/taro/demo1.tsx b/src/packages/pickerview/demos/taro/demo1.tsx index 4d3d363bc2..44c4bc6f3b 100644 --- a/src/packages/pickerview/demos/taro/demo1.tsx +++ b/src/packages/pickerview/demos/taro/demo1.tsx @@ -22,8 +22,8 @@ const Demo1 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/demos/taro/demo2.tsx b/src/packages/pickerview/demos/taro/demo2.tsx index f717632ce1..72a79c0a84 100644 --- a/src/packages/pickerview/demos/taro/demo2.tsx +++ b/src/packages/pickerview/demos/taro/demo2.tsx @@ -23,8 +23,8 @@ const Demo1 = () => { style={{ '--nutui-picker-item-height': '28px' }} options={listData} defaultValue={[1]} - onChange={(value, selectOptions) => { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/demos/taro/demo3.tsx b/src/packages/pickerview/demos/taro/demo3.tsx index ae6954258d..9c7b9cd1f9 100644 --- a/src/packages/pickerview/demos/taro/demo3.tsx +++ b/src/packages/pickerview/demos/taro/demo3.tsx @@ -25,8 +25,8 @@ const Demo3 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) if (isEqual(value, ['Tuesday', 'Afternoon'])) { setValue(['Monday', 'Evening']) } diff --git a/src/packages/pickerview/demos/taro/demo4.tsx b/src/packages/pickerview/demos/taro/demo4.tsx index c0234cdb03..2716cd35fd 100644 --- a/src/packages/pickerview/demos/taro/demo4.tsx +++ b/src/packages/pickerview/demos/taro/demo4.tsx @@ -24,8 +24,8 @@ const Demo1 = () => { { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) if (value[0] === 3) { setValue([1]) } diff --git a/src/packages/pickerview/demos/taro/demo5.tsx b/src/packages/pickerview/demos/taro/demo5.tsx index 98cfbdbeca..a8d43a86b0 100644 --- a/src/packages/pickerview/demos/taro/demo5.tsx +++ b/src/packages/pickerview/demos/taro/demo5.tsx @@ -24,8 +24,8 @@ const Demo1 = () => { options={listData} threeDimensional={false} duration={500} - onChange={(value, selectOptions) => { - console.log('onChange', value, selectOptions) + onChange={({ value, selectedOptions }) => { + console.log('onChange', value, selectedOptions) }} /> diff --git a/src/packages/pickerview/demos/taro/demo6.tsx b/src/packages/pickerview/demos/taro/demo6.tsx new file mode 100644 index 0000000000..9cf8ae0ac2 --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo6.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import { PickerView, Cell } from '@nutui/nutui-react-taro' + +const Demo6 = () => { + const listData = [ + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], + ] + + return ( + <> + + { + console.log('onChange', value, selectedOptions) + }} + /> + + + ) +} +export default Demo6 diff --git a/src/packages/pickerview/doc.en-US.md b/src/packages/pickerview/doc.en-US.md new file mode 100644 index 0000000000..6ec4aa0aa8 --- /dev/null +++ b/src/packages/pickerview/doc.en-US.md @@ -0,0 +1,94 @@ +# PickerView + +The PickerView is the content area of the Picker. + +## Import + +```tsx +import { Picker } from '@nutui/nutui-react' +``` + +## Demo + +### Basic Usage + +:::demo + + + +::: + +### Controlled + +:::demo + + + +::: + +### Adjust Height + +:::demo + + + +::: + +### Multi Column + +:::demo + + + +::: + +### Tiled + +:::demo + + + +::: + +### Cascade + +:::demo + + + +::: + +## PickerView + +### Props + +| Property | Description | Type | Default | +| --- | --- | --- | --- | +| options | Tabular data | `Array` | `[]` | +| value | Selected value, controlled | `Array` | `[]` | +| defaultValue | Default value | `Array` | `[]` | +| threeDimensional | Whether to enable 3D effect | `boolean` | `true` | +| duration | The duration of inertial rolling during rapid sliding, in ms | `string` \| `number` | `1000` | +| onChange | Called when the value of each column changes | `({value, index, selectedOptions}) => void` | `-` | + +### options + +| Property | Description | Type | Default | +| --- | --- | --- | --- | +| label | Text of column | `string` \| `number` | `-` | +| value | Value of column | `string` \| `number` | `-` | +| children | Cascader Option | `Array` | `-` | + +## Theming + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. Please refer to [ConfigProvider component](#/en-US/component/configprovider). + +| Name | Description | Default | +| --- | --- | --- | +| \--nutui-picker-item-height | Height of each data item on the panel | `36px` | +| \--nutui-picker-item-text-color | The color of each piece of data in the panel | `$color-title` | +| \--nutui-picker-item-text-font-size | The font size of each piece of data in the panel | `$font-size-base` | +| \--nutui-picker-item-active-line-border | The border value currently selected by the panel | `1px solid $color-border` | +| \--nut-picker-mask-background | Panel shade gradient value | `linear-gradient(180deg, var(--nutui-white-12), var(--nutui-white-7)),linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7))` | diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index bb230e0346..ad1777daa9 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -50,6 +50,14 @@ import { name } from '@nutui/nutui-react' ::: +### 级联 + +:::demo + + + +::: + ## PickerView ### Props @@ -61,7 +69,7 @@ import { name } from '@nutui/nutui-react' | defaultValue | 默认选中 | `Array` | `[]` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | -| onChange | 每一列值变更时调用 | `(options, value) => void` | `-` | +| onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | ### options 数据结构 diff --git a/src/packages/pickerview/doc.taro.md b/src/packages/pickerview/doc.taro.md new file mode 100644 index 0000000000..f8e186b1be --- /dev/null +++ b/src/packages/pickerview/doc.taro.md @@ -0,0 +1,93 @@ +# PickerView 选择器视图 + +PickerView 是 Picker 的内容区域。 + +## 引入 + +```tsx +import { name } from '@nutui/nutui-react-taro' +``` + +## 示例代码 + +### 基础用法 + +:::demo + + + +::: + +### 受控 + +:::demo + + + +::: + +### 自定义高度 + +:::demo + + + +::: + +### 多列 + +:::demo + + + +::: + +### 平铺 + +:::demo + + + +::: + +### 级联 + +:::demo + + + +::: + +## PickerView + +### Props + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| options | 列表数据 | `Array` | `[]` | +| value | 选中值,受控 | `Array` | `[]` | +| defaultValue | 默认选中 | `Array` | `[]` | +| threeDimensional | 是否开启3D效果 | `boolean` | `true` | +| duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | +| onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | + +### options 数据结构 + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| label | 选项的文字内容 | `string` \| `number` | `-` | +| value | 选项对应的值,且唯一 | `string` \| `number` | `-` | + +## 主题定制 + +### 样式变量 + +组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。 + +| 名称 | 说明 | 默认值 | +| --- | --- | --- | +| \--nutui-picker-item-height | 面板每条数据高度 | `36px` | +| \--nutui-picker-item-text-color | 面板每条数据的字色 | `$color-title` | +| \--nutui-picker-item-text-font-size | 面板每条数据的字号 | `$font-size-base` | +| \--nutui-picker-item-active-line-border | 面板当前选中的border值 | `1px solid $color-border` | +| \--nut-picker-mask-background | 面板遮挡区渐变值 | `linear-gradient(180deg, var(--nutui-white-12), var(--nutui-white-7)),linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7))` | diff --git a/src/packages/pickerview/doc.zh-TW.md b/src/packages/pickerview/doc.zh-TW.md new file mode 100644 index 0000000000..c944d61291 --- /dev/null +++ b/src/packages/pickerview/doc.zh-TW.md @@ -0,0 +1,93 @@ +# PickerView 選擇器視圖 + +PickerView 是 Picker 的內容區域。 + +## 引入 + +```tsx +import { name } from '@nutui/nutui-react-taro' +``` + +## 示例代碼 + +### 基礎用法 + +:::demo + + + +::: + +### 受控 + +:::demo + + + +::: + +### 自定義高度 + +:::demo + + + +::: + +### 多列 + +:::demo + + + +::: + +### 平鋪 + +:::demo + + + +::: + +### 級聯 + +:::demo + + + +::: + +## PickerView + +### Props + +| 屬性 | 說明 | 類型 | 默認值 | +| --- | --- | --- | --- | +| options | 列錶數據 | `Array` | `[]` | +| value | 選中值,受控 | `Array` | `[]` | +| defaultValue | 默認選中 | `Array` | `[]` | +| threeDimensional | 是否開啟3D效果 | `boolean` | `true` | +| duration | 快速滑動時慣性滾動的時長,單位 ms | `string` \| `number` | `1000` | +| onChange | 每一列值變更時調用 | `({value, index, selectedOptions}) => void` | `-` | + +### options 數據結構 + +| 屬性 | 說明 | 類型 | 默認值 | +| --- | --- | --- | --- | +| label | 選項的文字內容 | `string` \| `number` | `-` | +| value | 選項對應的值,且唯一 | `string` \| `number` | `-` | + +## 主題定制 + +### 樣式變量 + +組件提供了下列 CSS 變量,可用於自定義樣式,使用方法請參考 [ConfigProvider 組件](#/zh-CN/component/configprovider)。 + +| 名稱 | 說明 | 默認值 | +| --- | --- | --- | +| \--nutui-picker-item-height | 面闆每條數據高度 | `36px` | +| \--nutui-picker-item-text-color | 面闆每條數據的字色 | `$color-title` | +| \--nutui-picker-item-text-font-size | 面闆每條數據的字號 | `$font-size-base` | +| \--nutui-picker-item-active-line-border | 面闆當前選中的border值 | `1px solid $color-border` | +| \--nut-picker-mask-background | 面闆遮擋區漸變值 | `linear-gradient(180deg, var(--nutui-white-12), var(--nutui-white-7)),linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7))` | diff --git a/src/packages/pickerview/index.taro.ts b/src/packages/pickerview/index.taro.ts index b42ce6bc9a..1bd5f2a397 100644 --- a/src/packages/pickerview/index.taro.ts +++ b/src/packages/pickerview/index.taro.ts @@ -1,4 +1,11 @@ import PickerView from './pickerview.taro' -export type { PickerViewProps } from './types' +export type { + PickerViewProps, + PickerOptionItem, + PickerRollerProps, + PickerValue, + PickerOptions, + PickerOnChangeCallbackParameter, +} from './types' export default PickerView diff --git a/src/packages/pickerview/index.ts b/src/packages/pickerview/index.ts index 47b8834afe..15c781a272 100644 --- a/src/packages/pickerview/index.ts +++ b/src/packages/pickerview/index.ts @@ -1,4 +1,11 @@ import PickerView from './pickerview' -export type { PickerViewProps } from './types' +export type { + PickerViewProps, + PickerOptionItem, + PickerRollerProps, + PickerValue, + PickerOptions, + PickerOnChangeCallbackParameter, +} from './types' export default PickerView diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 4c5b1493bd..dc0b2b56a9 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -9,6 +9,7 @@ import { View } from '@tarojs/components' import { useTouch } from '@/utils/use-touch' import { passiveSupported } from '@/utils/supports-passive' import { PickerRollerProps, PickerOptionItem } from './types' +import { web } from '@/utils/platform-taro' const InternalPickerRoller: ForwardRefRenderFunction< { stopMomentum: () => void; moving: boolean }, @@ -49,12 +50,11 @@ const InternalPickerRoller: ForwardRefRenderFunction< // 获取 lineSpacing.current CSS变量 useEffect(() => { const element = pickerRollerRef.current - if (element) { + if (element && web()) { const computedStyle = getComputedStyle(element) const currentLineSpacing = computedStyle.getPropertyValue( '--nutui-picker-item-height' ) - console.log('currentLineSpacing', currentLineSpacing) currentLineSpacing && (lineSpacing.current = parseFloat(currentLineSpacing)) } @@ -111,8 +111,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< const minDeg = 0 deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - - if (minDeg < deg && deg < maxDeg) { + if (minDeg <= deg && deg < maxDeg) { setTransform('', `${deg}deg`, undefined, updateMove) setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) } @@ -190,7 +189,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< setCurrIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current type && setChooseValue(-move) - console.log(index, move, 'props.value.index') setMove(-move) } @@ -216,7 +214,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< setScrollDistance(0) transformY.current = 0 modifyStatus(false) - }, [options]) + }, [options, props.value]) useImperativeHandle(ref, () => ({ stopMomentum, diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 604cf97476..47b66ef808 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -108,8 +108,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< const minDeg = 0 deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - - if (minDeg < deg && deg < maxDeg) { + if (minDeg <= deg && deg < maxDeg) { setTransform('', `${deg}deg`, undefined, updateMove) setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) } @@ -187,7 +186,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< setCurrIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current type && setChooseValue(-move) - console.log(index, move, 'props.value.index') setMove(-move) } @@ -215,8 +213,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< setScrollDistance(0) transformY.current = 0 modifyStatus(false) - console.log('modifyStatus', props.value) - }, [options]) + }, [options, props.value]) useImperativeHandle(ref, () => ({ stopMomentum, diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index 251a8c4d1d..06f20401f6 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -3,12 +3,19 @@ import React, { useCallback, useEffect, useMemo, + useRef, useState, } from 'react' import classNames from 'classnames' import { View } from '@tarojs/components' +import isEqual from 'react-fast-compare' import { ComponentDefaults } from '@/utils/typings' -import { PickerViewProps, PickerOptionItem, PickerValue } from './types' +import { + PickerViewProps, + PickerOptionItem, + PickerValue, + PickerOptions, +} from './types' import PickerRoller from './pickerroller.taro' import { usePropsValue } from '@/utils/use-props-value' @@ -26,7 +33,7 @@ const InternalPickerView: ForwardRefRenderFunction< > = (props, ref) => { const { options, - defaultValue, + defaultValue = [], value, duration, threeDimensional, @@ -40,56 +47,152 @@ const InternalPickerView: ForwardRefRenderFunction< const [selectedValue] = usePropsValue({ value, - defaultValue, - finalValue: defaultValue, + defaultValue: [...defaultValue], + finalValue: [...defaultValue], }) const [innerValue, setInnerValue] = useState(selectedValue) - const [innerOptions, setInnerOptions] = useState(options) + const [innerOptions, setInnerOptions] = useState([] as PickerOptions[]) + const changeIndex = useRef(0) + + /** + * 数据类型:级联、多列 + */ + const columnsType = useMemo(() => { + const [firstColumn] = props.options as PickerOptions[] + if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { + return 'cascade' + } + return 'multiple' + }, [props.options]) + + const formatCascadeOptions = ( + options: PickerOptions, + value: PickerValue[] + ) => { + if (!options.length) return [] // 如果 options 为空,直接返回空数组 + + const formatted: PickerOptions[] = [] + let columnOptions: PickerOptionItem = { + label: '', + value: '', + children: options, + } + + let columnIndex = 0 + while (columnOptions && columnOptions.children) { + const currentOptions: PickerOptions = columnOptions.children + formatted.push(currentOptions) + + const currentValue = value?.[columnIndex] + if (currentValue === 0) { + // 如果 currentValue 为 0,返回第一个 children + columnOptions = currentOptions[0] + } else if (currentValue) { + // 如果 currentValue 存在,查找匹配的项 + const index = currentOptions.findIndex( + (columnItem) => columnItem.value === currentValue + ) + columnOptions = currentOptions[index === -1 ? 0 : index] // 如果未找到,默认取第一个 + } else { + break // 如果 currentValue 不存在,终止循环 + } + + columnIndex++ + } + return formatted + } + + const formatOptions = useMemo(() => { + if (columnsType === 'cascade') { + return formatCascadeOptions( + props?.options?.[0] as PickerOptions, + innerValue + ) + } + return props.options + }, [innerValue, options, columnsType]) useEffect(() => { - if (selectedValue !== innerValue) { - setInnerValue(selectedValue) + if (props.options !== innerOptions) { + setInnerOptions(formatOptions as PickerOptions[]) } - }, [selectedValue]) + }, [props.options, innerValue]) useEffect(() => { - if (options !== innerOptions) { - setInnerOptions(options) + if (selectedValue !== innerValue) { + setInnerValue(selectedValue) } - }, [options]) + }, [selectedValue]) const handleSelect = useCallback( (option: PickerOptionItem, index: number) => { const newValue = option?.value - if (!newValue) return - setInnerValue((prev) => { - if (prev[index] === newValue) return prev - const next = [...prev] - next[index] = newValue - return next - }) + if (!newValue || innerValue[index] === newValue) return + changeIndex.current = index + if (columnsType === 'multiple') { + setInnerValue((prev) => { + const next = [...prev] + next[index] = newValue + return next + }) + } else { + const startIndex = index + const values: PickerValue[] = [] + values[index] = option.value + while (option?.children?.[0]) { + values[index + 1] = option.children[0].value + index++ + option = option.children[0] + } + // 当前改变列的下一列 children 值为空 + if (option?.children?.length) { + values[index + 1] = '' + } + const combineResult = [ + ...innerValue.slice(0, startIndex), + ...values.splice(startIndex), + ] + setInnerValue([...combineResult]) + const optionFirst = props?.options?.[0] as PickerOptionItem[] + if ( + !isEqual( + formatCascadeOptions(optionFirst, combineResult), + innerOptions + ) + ) { + setInnerOptions(formatCascadeOptions(optionFirst, combineResult)) + } + } }, - [] + [innerValue, props.options, columnsType, innerOptions] ) const selectedOptions = useMemo(() => { - return options.map((columnOptions, index) => { - const selectedOption = columnOptions.find( - (item) => item.value === innerValue[index] - ) - return selectedOption || columnOptions[0] - }) - }, [options, innerValue]) + return innerOptions + .map((columnOptions, index) => { + const selectedOption = columnOptions.find( + (item) => item.value === innerValue[index] + ) + return selectedOption + // return selectedOption || columnOptions[0] + }) + .filter(Boolean) as PickerOptionItem[] + }, [innerOptions, innerValue]) useEffect(() => { - onChange?.(innerValue, selectedOptions) + onChange?.({ + value: innerValue, + index: changeIndex.current, + selectedOptions, + }) }, [innerValue, selectedOptions, onChange]) return ( {innerOptions.map((item, index) => ( = (props, ref) => { const { options, - defaultValue, + defaultValue = [], value, duration, threeDimensional, @@ -39,56 +46,152 @@ const InternalPickerView: ForwardRefRenderFunction< const [selectedValue] = usePropsValue({ value, - defaultValue, - finalValue: defaultValue, + defaultValue: [...defaultValue], + finalValue: [...defaultValue], }) const [innerValue, setInnerValue] = useState(selectedValue) - const [innerOptions, setInnerOptions] = useState(options) + const [innerOptions, setInnerOptions] = useState([] as PickerOptions[]) + const changeIndex = useRef(0) + + /** + * 数据类型:级联、多列 + */ + const columnsType = useMemo(() => { + const [firstColumn] = props.options as PickerOptions[] + if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { + return 'cascade' + } + return 'multiple' + }, [props.options]) + + const formatCascadeOptions = ( + options: PickerOptions, + value: PickerValue[] + ) => { + if (!options.length) return [] // 如果 options 为空,直接返回空数组 + + const formatted: PickerOptions[] = [] + let columnOptions: PickerOptionItem = { + label: '', + value: '', + children: options, + } + + let columnIndex = 0 + while (columnOptions && columnOptions.children) { + const currentOptions: PickerOptions = columnOptions.children + formatted.push(currentOptions) + + const currentValue = value?.[columnIndex] + if (currentValue === 0) { + // 如果 currentValue 为 0,返回第一个 children + columnOptions = currentOptions[0] + } else if (currentValue) { + // 如果 currentValue 存在,查找匹配的项 + const index = currentOptions.findIndex( + (columnItem) => columnItem.value === currentValue + ) + columnOptions = currentOptions[index === -1 ? 0 : index] // 如果未找到,默认取第一个 + } else { + break // 如果 currentValue 不存在,终止循环 + } + + columnIndex++ + } + return formatted + } + + const formatOptions = useMemo(() => { + if (columnsType === 'cascade') { + return formatCascadeOptions( + props?.options?.[0] as PickerOptions, + innerValue + ) + } + return props.options + }, [innerValue, options, columnsType]) useEffect(() => { - if (selectedValue !== innerValue) { - setInnerValue(selectedValue) + if (props.options !== innerOptions) { + setInnerOptions(formatOptions as PickerOptions[]) } - }, [selectedValue]) + }, [props.options, innerValue]) useEffect(() => { - if (options !== innerOptions) { - setInnerOptions(options) + if (selectedValue !== innerValue) { + setInnerValue(selectedValue) } - }, [options]) + }, [selectedValue]) const handleSelect = useCallback( (option: PickerOptionItem, index: number) => { const newValue = option?.value - if (!newValue) return - setInnerValue((prev) => { - if (prev[index] === newValue) return prev - const next = [...prev] - next[index] = newValue - return next - }) + if (!newValue || innerValue[index] === newValue) return + changeIndex.current = index + if (columnsType === 'multiple') { + setInnerValue((prev) => { + const next = [...prev] + next[index] = newValue + return next + }) + } else { + const startIndex = index + const values: PickerValue[] = [] + values[index] = option.value + while (option?.children?.[0]) { + values[index + 1] = option.children[0].value + index++ + option = option.children[0] + } + // 当前改变列的下一列 children 值为空 + if (option?.children?.length) { + values[index + 1] = '' + } + const combineResult = [ + ...innerValue.slice(0, startIndex), + ...values.splice(startIndex), + ] + setInnerValue([...combineResult]) + const optionFirst = props?.options?.[0] as PickerOptionItem[] + if ( + !isEqual( + formatCascadeOptions(optionFirst, combineResult), + innerOptions + ) + ) { + setInnerOptions(formatCascadeOptions(optionFirst, combineResult)) + } + } }, - [] + [innerValue, props.options, columnsType, innerOptions] ) const selectedOptions = useMemo(() => { - return options.map((columnOptions, index) => { - const selectedOption = columnOptions.find( - (item) => item.value === innerValue[index] - ) - return selectedOption || columnOptions[0] - }) - }, [options, innerValue]) + return innerOptions + .map((columnOptions, index) => { + const selectedOption = columnOptions.find( + (item) => item.value === innerValue[index] + ) + return selectedOption + // return selectedOption || columnOptions[0] + }) + .filter(Boolean) as PickerOptionItem[] + }, [innerOptions, innerValue]) useEffect(() => { - onChange?.(innerValue, selectedOptions) + onChange?.({ + value: innerValue, + index: changeIndex.current, + selectedOptions, + }) }, [innerValue, selectedOptions, onChange]) return (
{innerOptions.map((item, index) => ( React.ReactNode } +export interface PickerOnChangeCallbackParameter { + value: PickerValue[] + index: number + selectedOptions: PickerOptionItem[] +} + export interface PickerViewProps extends BasicComponent { + setRefs?: (ref: any) => any options: PickerOptions[] value?: PickerValue[] defaultValue?: PickerValue[] threeDimensional?: boolean duration?: number | string renderLabel: (item: PickerOptionItem) => React.ReactNode - onChange?: (value: PickerValue[], selectOptions: PickerOptionItem[]) => void + onChange?: (arg0: PickerOnChangeCallbackParameter) => void } From a030ff49d096d7972990427565585e6319d53a0f Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 11:32:36 +0800 Subject: [PATCH 13/54] fix: update test --- .../__snapshots__/pickerview.spec.tsx.snap | 148 ++++++++++++++++++ .../pickerview/__test__/pickerview.spec.tsx | 29 +++- 2 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap diff --git a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap new file mode 100644 index 0000000000..b738806b33 --- /dev/null +++ b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap @@ -0,0 +1,148 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`should match snapshot 1`] = ` +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+`; + +exports[`should render tiled 1`] = ` +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+`; diff --git a/src/packages/pickerview/__test__/pickerview.spec.tsx b/src/packages/pickerview/__test__/pickerview.spec.tsx index 089e995187..f4f9701505 100644 --- a/src/packages/pickerview/__test__/pickerview.spec.tsx +++ b/src/packages/pickerview/__test__/pickerview.spec.tsx @@ -3,7 +3,34 @@ import { render } from '@testing-library/react' import '@testing-library/jest-dom' import PickerView from '../pickerview' +const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], +] + test('should match snapshot', () => { - const { container } = render() + const { container } = render( + + ) + expect(container).toMatchSnapshot() +}) + +test('should render tiled', () => { + const { container } = render( + + ) expect(container).toMatchSnapshot() }) From f436c51e119faefd2cd54a1e8a348c11aee2a845 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 11:39:30 +0800 Subject: [PATCH 14/54] fix: update error --- src/packages/pickerview/demo.taro.tsx | 2 +- src/packages/pickerview/doc.md | 2 +- src/packages/pickerview/doc.taro.md | 2 +- src/packages/pickerview/doc.zh-TW.md | 2 +- src/packages/pickerview/pickerroller.taro.tsx | 2 +- src/packages/pickerview/pickerroller.tsx | 2 +- src/packages/pickerview/pickerview.taro.tsx | 8 ++++++-- src/packages/pickerview/pickerview.tsx | 8 ++++++-- 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/packages/pickerview/demo.taro.tsx b/src/packages/pickerview/demo.taro.tsx index 854ebb089d..73487d67b0 100644 --- a/src/packages/pickerview/demo.taro.tsx +++ b/src/packages/pickerview/demo.taro.tsx @@ -51,7 +51,7 @@ const PickerViewDemo = () => { {translated.tiled} -

{translated.cascade}

+ {translated.cascade} diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index ad1777daa9..ba0979c327 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -5,7 +5,7 @@ PickerView 是 Picker 的内容区域。 ## 引入 ```tsx -import { name } from '@nutui/nutui-react' +import { PickerView } from '@nutui/nutui-react' ``` ## 示例代码 diff --git a/src/packages/pickerview/doc.taro.md b/src/packages/pickerview/doc.taro.md index f8e186b1be..03fdff98cc 100644 --- a/src/packages/pickerview/doc.taro.md +++ b/src/packages/pickerview/doc.taro.md @@ -5,7 +5,7 @@ PickerView 是 Picker 的内容区域。 ## 引入 ```tsx -import { name } from '@nutui/nutui-react-taro' +import { PickerView } from '@nutui/nutui-react-taro' ``` ## 示例代码 diff --git a/src/packages/pickerview/doc.zh-TW.md b/src/packages/pickerview/doc.zh-TW.md index c944d61291..155b4fc341 100644 --- a/src/packages/pickerview/doc.zh-TW.md +++ b/src/packages/pickerview/doc.zh-TW.md @@ -5,7 +5,7 @@ PickerView 是 Picker 的內容區域。 ## 引入 ```tsx -import { name } from '@nutui/nutui-react-taro' +import { PickerView } from '@nutui/nutui-react-taro' ``` ## 示例代碼 diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index dc0b2b56a9..476e5da967 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -257,7 +257,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< eventOptions ) } - }) + }, []) const touchRollerStyle = () => { return { diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 47b66ef808..e4a485b3ae 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -230,7 +230,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< pickerRollerRef.current?.removeEventListener('touchmove', touchMove) pickerRollerRef.current?.removeEventListener('touchend', touchEnd) } - }) + }, []) const touchRollerStyle = () => { return { diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index 06f20401f6..c13a3c052b 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -59,8 +59,12 @@ const InternalPickerView: ForwardRefRenderFunction< * 数据类型:级联、多列 */ const columnsType = useMemo(() => { - const [firstColumn] = props.options as PickerOptions[] - if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { + const firstColumn = (props.options as PickerOptions[])[0] || [] + if ( + Array.isArray(firstColumn) && + firstColumn.length > 0 && + 'children' in firstColumn[0] + ) { return 'cascade' } return 'multiple' diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index daeff9c23c..60b98efe85 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -58,8 +58,12 @@ const InternalPickerView: ForwardRefRenderFunction< * 数据类型:级联、多列 */ const columnsType = useMemo(() => { - const [firstColumn] = props.options as PickerOptions[] - if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { + const firstColumn = (props.options as PickerOptions[])[0] || [] + if ( + Array.isArray(firstColumn) && + firstColumn.length > 0 && + 'children' in firstColumn[0] + ) { return 'cascade' } return 'multiple' From 6a6bc39190df5de4a639f4ad56d70b137a093f73 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 11:42:13 +0800 Subject: [PATCH 15/54] fix: update error --- src/packages/pickerview/pickerroller.taro.tsx | 2 +- src/packages/pickerview/pickerroller.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 476e5da967..9f7768459f 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -257,7 +257,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< eventOptions ) } - }, []) + }, [pickerRollerRef.current, touchStart, touchMove, touchEnd]) const touchRollerStyle = () => { return { diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index e4a485b3ae..02ff4f535b 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -230,7 +230,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< pickerRollerRef.current?.removeEventListener('touchmove', touchMove) pickerRollerRef.current?.removeEventListener('touchend', touchEnd) } - }, []) + }, [pickerRollerRef.current, touchStart, touchMove, touchEnd]) const touchRollerStyle = () => { return { From 10be5cd5bebd6d257f83bf7affbff2dfa5e79ecd Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 11:46:09 +0800 Subject: [PATCH 16/54] fix: update demo --- src/packages/pickerview/demos/h5/demo2.tsx | 4 ++-- src/packages/pickerview/demos/h5/demo4.tsx | 4 ++-- src/packages/pickerview/demos/h5/demo5.tsx | 4 ++-- src/packages/pickerview/demos/taro/demo2.tsx | 4 ++-- src/packages/pickerview/demos/taro/demo4.tsx | 4 ++-- src/packages/pickerview/demos/taro/demo5.tsx | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/packages/pickerview/demos/h5/demo2.tsx b/src/packages/pickerview/demos/h5/demo2.tsx index dff4a404b0..894b633b01 100644 --- a/src/packages/pickerview/demos/h5/demo2.tsx +++ b/src/packages/pickerview/demos/h5/demo2.tsx @@ -1,7 +1,7 @@ import React from 'react' import { PickerView, Cell } from '@nutui/nutui-react' -const Demo1 = () => { +const Demo2 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -31,4 +31,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo2 diff --git a/src/packages/pickerview/demos/h5/demo4.tsx b/src/packages/pickerview/demos/h5/demo4.tsx index 320c7cc39e..cfa2ab063e 100644 --- a/src/packages/pickerview/demos/h5/demo4.tsx +++ b/src/packages/pickerview/demos/h5/demo4.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react' import { PickerView, Cell } from '@nutui/nutui-react' -const Demo1 = () => { +const Demo4 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -35,4 +35,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo4 diff --git a/src/packages/pickerview/demos/h5/demo5.tsx b/src/packages/pickerview/demos/h5/demo5.tsx index 4bf68a2261..e9795b79f4 100644 --- a/src/packages/pickerview/demos/h5/demo5.tsx +++ b/src/packages/pickerview/demos/h5/demo5.tsx @@ -1,7 +1,7 @@ import React from 'react' import { PickerView, Cell } from '@nutui/nutui-react' -const Demo1 = () => { +const Demo5 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -32,4 +32,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo5 diff --git a/src/packages/pickerview/demos/taro/demo2.tsx b/src/packages/pickerview/demos/taro/demo2.tsx index 72a79c0a84..838e0ea546 100644 --- a/src/packages/pickerview/demos/taro/demo2.tsx +++ b/src/packages/pickerview/demos/taro/demo2.tsx @@ -1,7 +1,7 @@ import React from 'react' import { PickerView, Cell } from '@nutui/nutui-react-taro' -const Demo1 = () => { +const Demo2 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -31,4 +31,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo2 diff --git a/src/packages/pickerview/demos/taro/demo4.tsx b/src/packages/pickerview/demos/taro/demo4.tsx index 2716cd35fd..e6c5666a3e 100644 --- a/src/packages/pickerview/demos/taro/demo4.tsx +++ b/src/packages/pickerview/demos/taro/demo4.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react' import { PickerView, Cell } from '@nutui/nutui-react-taro' -const Demo1 = () => { +const Demo4 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -35,4 +35,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo4 diff --git a/src/packages/pickerview/demos/taro/demo5.tsx b/src/packages/pickerview/demos/taro/demo5.tsx index a8d43a86b0..f313af7bb5 100644 --- a/src/packages/pickerview/demos/taro/demo5.tsx +++ b/src/packages/pickerview/demos/taro/demo5.tsx @@ -1,7 +1,7 @@ import React from 'react' import { PickerView, Cell } from '@nutui/nutui-react-taro' -const Demo1 = () => { +const Demo5 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -32,4 +32,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo5 From 8ad73921297a66d681f966dd7917bf9bdede432c Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 12:32:23 +0800 Subject: [PATCH 17/54] fix: update test --- .../__snapshots__/pickerview.spec.tsx.snap | 371 ++++++++++++++++++ .../pickerview/__test__/pickerview.spec.tsx | 113 +++++- 2 files changed, 483 insertions(+), 1 deletion(-) diff --git a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap index b738806b33..58558b86b4 100644 --- a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap +++ b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap @@ -1,5 +1,295 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`should match cascade 1`] = ` +
+
+
+
+
+ 北京 | 测试 +
+
+ 上海 | 测试 +
+
+
+
+
+
+ 朝阳区 | 测试 +
+
+ 海淀区 | 测试 +
+
+ 大兴区 | 测试 +
+
+ 东城区 | 测试 +
+
+ 西城区 | 测试 +
+
+ 丰台区 | 测试 +
+
+
+
+
+
+
+`; + +exports[`should match onchange 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+ , + "container":
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + exports[`should match snapshot 1`] = `
`; + +exports[`should render with Multi Column 1`] = ` +
+
+
+
+
+ 周一 +
+
+ 周二 +
+
+ 周三 +
+
+ 周四 +
+
+ 周五 +
+
+
+
+
+
+ 上午 +
+
+ 下午 +
+
+ 晚上 +
+
+
+
+
+
+
+`; diff --git a/src/packages/pickerview/__test__/pickerview.spec.tsx b/src/packages/pickerview/__test__/pickerview.spec.tsx index f4f9701505..dc758e7088 100644 --- a/src/packages/pickerview/__test__/pickerview.spec.tsx +++ b/src/packages/pickerview/__test__/pickerview.spec.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState } from 'react' import { render } from '@testing-library/react' import '@testing-library/jest-dom' import PickerView from '../pickerview' @@ -17,6 +17,82 @@ const listData = [ ], ] +const MultiColumnData = [ + [ + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, + ], + [ + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, + ], +] + +const cascadeData = [ + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], +] + test('should match snapshot', () => { const { container } = render( @@ -34,3 +110,38 @@ test('should render tiled', () => { ) expect(container).toMatchSnapshot() }) + +test('should render with Multi Column', () => { + const { container } = render( + + ) + expect(container.querySelectorAll('.nut-pickerview-list').length).toBe(2) + expect(container).toMatchSnapshot() +}) + +test('should match onchange', async () => { + const PenderContent = () => { + const [value, setValue] = useState([1]) + + setTimeout(() => { + setValue([2]) + }, 1000) + + return + } + const container = render() + + expect(container).toMatchSnapshot() +}) + +test('should match cascade', () => { + const { container } = render( + `${item.label} | 测试`} + options={cascadeData} + onChange={() => {}} + /> + ) + expect(container).toMatchSnapshot() +}) From 95b5423d24b23c84959cfbbb07dba81d0328b2bc Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 14:37:13 +0800 Subject: [PATCH 18/54] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BC=82?= =?UTF-8?q?=E6=AD=A5demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__snapshots__/pickerview.spec.tsx.snap | 171 ++++++++++++++++++ .../pickerview/__test__/pickerview.spec.tsx | 41 ++++- src/packages/pickerview/demo.taro.tsx | 6 + src/packages/pickerview/demo.tsx | 6 + src/packages/pickerview/demos/h5/demo7.tsx | 38 ++++ src/packages/pickerview/demos/taro/demo7.tsx | 38 ++++ src/packages/pickerview/pickerview.taro.tsx | 11 +- src/packages/pickerview/pickerview.tsx | 11 +- 8 files changed, 308 insertions(+), 14 deletions(-) create mode 100644 src/packages/pickerview/demos/h5/demo7.tsx create mode 100644 src/packages/pickerview/demos/taro/demo7.tsx diff --git a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap index 58558b86b4..0ab994d585 100644 --- a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap +++ b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap @@ -1,5 +1,98 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`should handle empty options 1`] = ` +
+
+
+
+
+
+`; + +exports[`should handle invalid defaultValue 1`] = ` +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+`; + exports[`should match cascade 1`] = `
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+`; + exports[`should match snapshot 1`] = `
{ ) expect(container).toMatchSnapshot() + expect(screen.getByText('南京市')).toBeInTheDocument() }) test('should render tiled', () => { @@ -115,7 +116,17 @@ test('should render with Multi Column', () => { const { container } = render( ) - expect(container.querySelectorAll('.nut-pickerview-list').length).toBe(2) + const columns = container.querySelectorAll('.nut-pickerview-list') + expect(columns.length).toBe(2) + + // 检查列内容 + const firstColumn = columns[0] + expect(firstColumn.textContent).toContain('周一') + expect(firstColumn.textContent).toContain('周二') + + const secondColumn = columns[1] + expect(secondColumn.textContent).toContain('上午') + expect(secondColumn.textContent).toContain('下午') expect(container).toMatchSnapshot() }) @@ -123,15 +134,29 @@ test('should match onchange', async () => { const PenderContent = () => { const [value, setValue] = useState([1]) - setTimeout(() => { - setValue([2]) - }, 1000) + useEffect(() => { + setTimeout(() => { + setValue([3]) + }, 1000) + }, []) - return + return ( + { + if (value[0] === 3) { + setValue([1]) + } + }} + /> + ) } const container = render() - expect(container).toMatchSnapshot() + await waitFor(() => { + expect(container).toMatchSnapshot() + }) }) test('should match cascade', () => { diff --git a/src/packages/pickerview/demo.taro.tsx b/src/packages/pickerview/demo.taro.tsx index 73487d67b0..8f34000096 100644 --- a/src/packages/pickerview/demo.taro.tsx +++ b/src/packages/pickerview/demo.taro.tsx @@ -9,6 +9,7 @@ import Demo3 from './demos/taro/demo3' import Demo4 from './demos/taro/demo4' import Demo5 from './demos/taro/demo5' import Demo6 from './demos/taro/demo6' +import Demo7 from './demos/taro/demo7' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -19,6 +20,7 @@ const PickerViewDemo = () => { controlled: '受控', tiled: '平铺', cascade: '级联', + asynchronous: '异步数据', }, 'en-US': { title: 'Basic Usage', @@ -27,6 +29,7 @@ const PickerViewDemo = () => { controlled: 'Controlled', tiled: 'Tiled', cascade: 'Cascade', + asynchronous: 'Asynchronous', }, 'zh-TW': { title: '基礎用法', @@ -35,6 +38,7 @@ const PickerViewDemo = () => { controlled: '受控', tiled: '平鋪', cascade: '級聯', + asynchronous: '異步數據', }, }) return ( @@ -53,6 +57,8 @@ const PickerViewDemo = () => { {translated.cascade} + {translated.asynchronous} + ) diff --git a/src/packages/pickerview/demo.tsx b/src/packages/pickerview/demo.tsx index b94f91de80..8d95022679 100644 --- a/src/packages/pickerview/demo.tsx +++ b/src/packages/pickerview/demo.tsx @@ -6,6 +6,7 @@ import Demo3 from './demos/h5/demo3' import Demo4 from './demos/h5/demo4' import Demo5 from './demos/h5/demo5' import Demo6 from './demos/h5/demo6' +import Demo7 from './demos/h5/demo7' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -16,6 +17,7 @@ const PickerViewDemo = () => { controlled: '受控', tiled: '平铺', cascade: '级联', + asynchronous: '异步数据', }, 'en-US': { title: 'Basic Usage', @@ -24,6 +26,7 @@ const PickerViewDemo = () => { controlled: 'Controlled', tiled: 'Tiled', cascade: 'Cascade', + asynchronous: 'Asynchronous', }, 'zh-TW': { title: '基礎用法', @@ -32,6 +35,7 @@ const PickerViewDemo = () => { controlled: '受控', tiled: '平鋪', cascade: '級聯', + asynchronous: '異步數據', }, }) return ( @@ -48,6 +52,8 @@ const PickerViewDemo = () => {

{translated.cascade}

+

{translated.asynchronous}

+
) } diff --git a/src/packages/pickerview/demos/h5/demo7.tsx b/src/packages/pickerview/demos/h5/demo7.tsx new file mode 100644 index 0000000000..d245a189c5 --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo7.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useState } from 'react' +import { PickerView, Cell, PickerOptions } from '@nutui/nutui-react' + +const Demo7 = () => { + const [columnsList, setColumnsList] = useState([] as PickerOptions[]) + + useEffect(() => { + setTimeout(() => { + setColumnsList([ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ]) + }, 3000) + }, []) + return ( + <> + + { + console.log('onChange', value, selectedOptions) + }} + /> + + + ) +} +export default Demo7 diff --git a/src/packages/pickerview/demos/taro/demo7.tsx b/src/packages/pickerview/demos/taro/demo7.tsx new file mode 100644 index 0000000000..a32defecfc --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo7.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useState } from 'react' +import { PickerView, Cell, PickerOptions } from '@nutui/nutui-react-taro' + +const Demo7 = () => { + const [columnsList, setColumnsList] = useState([] as PickerOptions[]) + + useEffect(() => { + setTimeout(() => { + setColumnsList([ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ]) + }, 3000) + }, []) + return ( + <> + + { + console.log('onChange', value, selectedOptions) + }} + /> + + + ) +} +export default Demo7 diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index c13a3c052b..a1ba018910 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -118,7 +118,8 @@ const InternalPickerView: ForwardRefRenderFunction< }, [innerValue, options, columnsType]) useEffect(() => { - if (props.options !== innerOptions) { + const options = props.options + if (Array.isArray(options) && options.length && options !== innerOptions) { setInnerOptions(formatOptions as PickerOptions[]) } }, [props.options, innerValue]) @@ -207,8 +208,12 @@ const InternalPickerView: ForwardRefRenderFunction< threeDimensional={threeDimensional} /> ))} - - + {innerOptions?.length ? ( + <> +
+
+ + ) : null} ) } diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index 60b98efe85..66770248c0 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -117,7 +117,8 @@ const InternalPickerView: ForwardRefRenderFunction< }, [innerValue, options, columnsType]) useEffect(() => { - if (props.options !== innerOptions) { + const options = props.options + if (Array.isArray(options) && options.length && options !== innerOptions) { setInnerOptions(formatOptions as PickerOptions[]) } }, [props.options, innerValue]) @@ -206,8 +207,12 @@ const InternalPickerView: ForwardRefRenderFunction< threeDimensional={threeDimensional} /> ))} -
-
+ {innerOptions?.length ? ( + <> +
+
+ + ) : null}
) } From fe66362d4d9bd81e29569e9591a9e48d385aafaf Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 14:51:56 +0800 Subject: [PATCH 19/54] fix: update doc --- src/packages/pickerview/doc.en-US.md | 6 +++--- src/packages/pickerview/doc.md | 5 +++-- src/packages/pickerview/doc.taro.md | 5 +++-- src/packages/pickerview/doc.zh-TW.md | 5 +++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/packages/pickerview/doc.en-US.md b/src/packages/pickerview/doc.en-US.md index 6ec4aa0aa8..a66464c311 100644 --- a/src/packages/pickerview/doc.en-US.md +++ b/src/packages/pickerview/doc.en-US.md @@ -64,20 +64,20 @@ import { Picker } from '@nutui/nutui-react' | Property | Description | Type | Default | | --- | --- | --- | --- | -| options | Tabular data | `Array` | `[]` | +| options | Tabular data | `PickerOptionItem[][]` | `[]` | | value | Selected value, controlled | `Array` | `[]` | | defaultValue | Default value | `Array` | `[]` | | threeDimensional | Whether to enable 3D effect | `boolean` | `true` | | duration | The duration of inertial rolling during rapid sliding, in ms | `string` \| `number` | `1000` | | onChange | Called when the value of each column changes | `({value, index, selectedOptions}) => void` | `-` | -### options +### PickerOptionItem | Property | Description | Type | Default | | --- | --- | --- | --- | | label | Text of column | `string` \| `number` | `-` | | value | Value of column | `string` \| `number` | `-` | -| children | Cascader Option | `Array` | `-` | +| children | Cascader Option | `PickerOptionItem[]` | `-` | ## Theming diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index ba0979c327..0ba9af80dc 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -64,19 +64,20 @@ import { PickerView } from '@nutui/nutui-react' | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| options | 列表数据 | `Array` | `[]` | +| options | 列表数据 | `PickerOptionItem[][]` | `[]` | | value | 选中值,受控 | `Array` | `[]` | | defaultValue | 默认选中 | `Array` | `[]` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | -### options 数据结构 +### PickerOptionItem | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | label | 选项的文字内容 | `string` \| `number` | `-` | | value | 选项对应的值,且唯一 | `string` \| `number` | `-` | +| children | 用于级联选项 | `PickerOptionItem[]` | `-` | ## 主题定制 diff --git a/src/packages/pickerview/doc.taro.md b/src/packages/pickerview/doc.taro.md index 03fdff98cc..fe1a838aca 100644 --- a/src/packages/pickerview/doc.taro.md +++ b/src/packages/pickerview/doc.taro.md @@ -64,19 +64,20 @@ import { PickerView } from '@nutui/nutui-react-taro' | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| options | 列表数据 | `Array` | `[]` | +| options | 列表数据 | `PickerOptionItem[][]` | `[]` | | value | 选中值,受控 | `Array` | `[]` | | defaultValue | 默认选中 | `Array` | `[]` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | -### options 数据结构 +### PickerOptionItem | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | label | 选项的文字内容 | `string` \| `number` | `-` | | value | 选项对应的值,且唯一 | `string` \| `number` | `-` | +| children | 用于级联选项 | `PickerOptionItem[]` | `-` | ## 主题定制 diff --git a/src/packages/pickerview/doc.zh-TW.md b/src/packages/pickerview/doc.zh-TW.md index 155b4fc341..aecd67eaff 100644 --- a/src/packages/pickerview/doc.zh-TW.md +++ b/src/packages/pickerview/doc.zh-TW.md @@ -64,19 +64,20 @@ import { PickerView } from '@nutui/nutui-react-taro' | 屬性 | 說明 | 類型 | 默認值 | | --- | --- | --- | --- | -| options | 列錶數據 | `Array` | `[]` | +| options | 列錶數據 | `PickerOptionItem[][]` | `[]` | | value | 選中值,受控 | `Array` | `[]` | | defaultValue | 默認選中 | `Array` | `[]` | | threeDimensional | 是否開啟3D效果 | `boolean` | `true` | | duration | 快速滑動時慣性滾動的時長,單位 ms | `string` \| `number` | `1000` | | onChange | 每一列值變更時調用 | `({value, index, selectedOptions}) => void` | `-` | -### options 數據結構 +### PickerOptionItem | 屬性 | 說明 | 類型 | 默認值 | | --- | --- | --- | --- | | label | 選項的文字內容 | `string` \| `number` | `-` | | value | 選項對應的值,且唯一 | `string` \| `number` | `-` | +| children | 用於級聯選項 | `PickerOptionItem[]` | `-` | ## 主題定制 From aa5f2ea93320cf5cb252609ea37269fdb40c151e Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 15:23:37 +0800 Subject: [PATCH 20/54] =?UTF-8?q?fix:=20=E5=90=8C=E6=AD=A5pickerview?= =?UTF-8?q?=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__snapshots__/pickerview.spec.tsx.snap | 690 ++++++++++++++++++ .../pickerview/__test__/pickerview.spec.tsx | 169 ++++- src/packages/pickerview/demo.taro.tsx | 8 +- src/packages/pickerview/demo.tsx | 6 + src/packages/pickerview/demos/h5/demo2.tsx | 4 +- src/packages/pickerview/demos/h5/demo4.tsx | 4 +- src/packages/pickerview/demos/h5/demo5.tsx | 4 +- src/packages/pickerview/demos/h5/demo7.tsx | 38 + src/packages/pickerview/demos/taro/demo2.tsx | 4 +- src/packages/pickerview/demos/taro/demo4.tsx | 4 +- src/packages/pickerview/demos/taro/demo5.tsx | 4 +- src/packages/pickerview/demos/taro/demo7.tsx | 38 + src/packages/pickerview/doc.en-US.md | 6 +- src/packages/pickerview/doc.md | 7 +- src/packages/pickerview/doc.taro.md | 7 +- src/packages/pickerview/doc.zh-TW.md | 7 +- src/packages/pickerview/pickerroller.taro.tsx | 2 +- src/packages/pickerview/pickerroller.tsx | 2 +- src/packages/pickerview/pickerview.taro.tsx | 19 +- src/packages/pickerview/pickerview.tsx | 19 +- 20 files changed, 1002 insertions(+), 40 deletions(-) create mode 100644 src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap create mode 100644 src/packages/pickerview/demos/h5/demo7.tsx create mode 100644 src/packages/pickerview/demos/taro/demo7.tsx diff --git a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap new file mode 100644 index 0000000000..0ab994d585 --- /dev/null +++ b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap @@ -0,0 +1,690 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`should handle empty options 1`] = ` +
+
+
+
+
+
+`; + +exports[`should handle invalid defaultValue 1`] = ` +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+`; + +exports[`should match cascade 1`] = ` +
+
+
+
+
+ 北京 | 测试 +
+
+ 上海 | 测试 +
+
+
+
+
+
+ 朝阳区 | 测试 +
+
+ 海淀区 | 测试 +
+
+ 大兴区 | 测试 +
+
+ 东城区 | 测试 +
+
+ 西城区 | 测试 +
+
+ 丰台区 | 测试 +
+
+
+
+
+
+
+`; + +exports[`should match onchange 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+ , + "container":
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`should match onchange 2`] = ` +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+`; + +exports[`should match snapshot 1`] = ` +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+`; + +exports[`should render tiled 1`] = ` +
+
+
+
+
+ 南京市 +
+
+ 无锡市 +
+
+ 海北藏族自治区 +
+
+ 北京市 +
+
+ 连云港市 +
+
+ 大庆市 +
+
+ 绥化市 +
+
+ 潍坊市 +
+
+ 乌鲁木齐市 +
+
+
+
+
+
+
+`; + +exports[`should render with Multi Column 1`] = ` +
+
+
+
+
+ 周一 +
+
+ 周二 +
+
+ 周三 +
+
+ 周四 +
+
+ 周五 +
+
+
+
+
+
+ 上午 +
+
+ 下午 +
+
+ 晚上 +
+
+
+
+
+
+
+`; diff --git a/src/packages/pickerview/__test__/pickerview.spec.tsx b/src/packages/pickerview/__test__/pickerview.spec.tsx index 089e995187..85c48a0cfd 100644 --- a/src/packages/pickerview/__test__/pickerview.spec.tsx +++ b/src/packages/pickerview/__test__/pickerview.spec.tsx @@ -1,9 +1,172 @@ -import React from 'react' -import { render } from '@testing-library/react' +import React, { useEffect, useState } from 'react' +import { render, waitFor, screen } from '@testing-library/react' import '@testing-library/jest-dom' import PickerView from '../pickerview' +const listData = [ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], +] + +const MultiColumnData = [ + [ + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, + ], + [ + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, + ], +] + +const cascadeData = [ + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], +] + test('should match snapshot', () => { - const { container } = render() + const { container } = render( + + ) + expect(container).toMatchSnapshot() + expect(screen.getByText('南京市')).toBeInTheDocument() +}) + +test('should render tiled', () => { + const { container } = render( + + ) + expect(container).toMatchSnapshot() +}) + +test('should render with Multi Column', () => { + const { container } = render( + + ) + const columns = container.querySelectorAll('.nut-pickerview-list') + expect(columns.length).toBe(2) + + // 检查列内容 + const firstColumn = columns[0] + expect(firstColumn.textContent).toContain('周一') + expect(firstColumn.textContent).toContain('周二') + + const secondColumn = columns[1] + expect(secondColumn.textContent).toContain('上午') + expect(secondColumn.textContent).toContain('下午') + expect(container).toMatchSnapshot() +}) + +test('should match onchange', async () => { + const PenderContent = () => { + const [value, setValue] = useState([1]) + + useEffect(() => { + setTimeout(() => { + setValue([3]) + }, 1000) + }, []) + + return ( + { + if (value[0] === 3) { + setValue([1]) + } + }} + /> + ) + } + const container = render() + + await waitFor(() => { + expect(container).toMatchSnapshot() + }) +}) + +test('should match cascade', () => { + const { container } = render( + `${item.label} | 测试`} + options={cascadeData} + onChange={() => {}} + /> + ) expect(container).toMatchSnapshot() }) diff --git a/src/packages/pickerview/demo.taro.tsx b/src/packages/pickerview/demo.taro.tsx index 854ebb089d..8f34000096 100644 --- a/src/packages/pickerview/demo.taro.tsx +++ b/src/packages/pickerview/demo.taro.tsx @@ -9,6 +9,7 @@ import Demo3 from './demos/taro/demo3' import Demo4 from './demos/taro/demo4' import Demo5 from './demos/taro/demo5' import Demo6 from './demos/taro/demo6' +import Demo7 from './demos/taro/demo7' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -19,6 +20,7 @@ const PickerViewDemo = () => { controlled: '受控', tiled: '平铺', cascade: '级联', + asynchronous: '异步数据', }, 'en-US': { title: 'Basic Usage', @@ -27,6 +29,7 @@ const PickerViewDemo = () => { controlled: 'Controlled', tiled: 'Tiled', cascade: 'Cascade', + asynchronous: 'Asynchronous', }, 'zh-TW': { title: '基礎用法', @@ -35,6 +38,7 @@ const PickerViewDemo = () => { controlled: '受控', tiled: '平鋪', cascade: '級聯', + asynchronous: '異步數據', }, }) return ( @@ -51,8 +55,10 @@ const PickerViewDemo = () => { {translated.tiled} -

{translated.cascade}

+ {translated.cascade} + {translated.asynchronous} + ) diff --git a/src/packages/pickerview/demo.tsx b/src/packages/pickerview/demo.tsx index b94f91de80..8d95022679 100644 --- a/src/packages/pickerview/demo.tsx +++ b/src/packages/pickerview/demo.tsx @@ -6,6 +6,7 @@ import Demo3 from './demos/h5/demo3' import Demo4 from './demos/h5/demo4' import Demo5 from './demos/h5/demo5' import Demo6 from './demos/h5/demo6' +import Demo7 from './demos/h5/demo7' const PickerViewDemo = () => { const [translated] = useTranslate({ @@ -16,6 +17,7 @@ const PickerViewDemo = () => { controlled: '受控', tiled: '平铺', cascade: '级联', + asynchronous: '异步数据', }, 'en-US': { title: 'Basic Usage', @@ -24,6 +26,7 @@ const PickerViewDemo = () => { controlled: 'Controlled', tiled: 'Tiled', cascade: 'Cascade', + asynchronous: 'Asynchronous', }, 'zh-TW': { title: '基礎用法', @@ -32,6 +35,7 @@ const PickerViewDemo = () => { controlled: '受控', tiled: '平鋪', cascade: '級聯', + asynchronous: '異步數據', }, }) return ( @@ -48,6 +52,8 @@ const PickerViewDemo = () => {

{translated.cascade}

+

{translated.asynchronous}

+
) } diff --git a/src/packages/pickerview/demos/h5/demo2.tsx b/src/packages/pickerview/demos/h5/demo2.tsx index dff4a404b0..894b633b01 100644 --- a/src/packages/pickerview/demos/h5/demo2.tsx +++ b/src/packages/pickerview/demos/h5/demo2.tsx @@ -1,7 +1,7 @@ import React from 'react' import { PickerView, Cell } from '@nutui/nutui-react' -const Demo1 = () => { +const Demo2 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -31,4 +31,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo2 diff --git a/src/packages/pickerview/demos/h5/demo4.tsx b/src/packages/pickerview/demos/h5/demo4.tsx index 320c7cc39e..cfa2ab063e 100644 --- a/src/packages/pickerview/demos/h5/demo4.tsx +++ b/src/packages/pickerview/demos/h5/demo4.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react' import { PickerView, Cell } from '@nutui/nutui-react' -const Demo1 = () => { +const Demo4 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -35,4 +35,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo4 diff --git a/src/packages/pickerview/demos/h5/demo5.tsx b/src/packages/pickerview/demos/h5/demo5.tsx index 4bf68a2261..e9795b79f4 100644 --- a/src/packages/pickerview/demos/h5/demo5.tsx +++ b/src/packages/pickerview/demos/h5/demo5.tsx @@ -1,7 +1,7 @@ import React from 'react' import { PickerView, Cell } from '@nutui/nutui-react' -const Demo1 = () => { +const Demo5 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -32,4 +32,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo5 diff --git a/src/packages/pickerview/demos/h5/demo7.tsx b/src/packages/pickerview/demos/h5/demo7.tsx new file mode 100644 index 0000000000..d245a189c5 --- /dev/null +++ b/src/packages/pickerview/demos/h5/demo7.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useState } from 'react' +import { PickerView, Cell, PickerOptions } from '@nutui/nutui-react' + +const Demo7 = () => { + const [columnsList, setColumnsList] = useState([] as PickerOptions[]) + + useEffect(() => { + setTimeout(() => { + setColumnsList([ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ]) + }, 3000) + }, []) + return ( + <> + + { + console.log('onChange', value, selectedOptions) + }} + /> + + + ) +} +export default Demo7 diff --git a/src/packages/pickerview/demos/taro/demo2.tsx b/src/packages/pickerview/demos/taro/demo2.tsx index 72a79c0a84..838e0ea546 100644 --- a/src/packages/pickerview/demos/taro/demo2.tsx +++ b/src/packages/pickerview/demos/taro/demo2.tsx @@ -1,7 +1,7 @@ import React from 'react' import { PickerView, Cell } from '@nutui/nutui-react-taro' -const Demo1 = () => { +const Demo2 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -31,4 +31,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo2 diff --git a/src/packages/pickerview/demos/taro/demo4.tsx b/src/packages/pickerview/demos/taro/demo4.tsx index 2716cd35fd..e6c5666a3e 100644 --- a/src/packages/pickerview/demos/taro/demo4.tsx +++ b/src/packages/pickerview/demos/taro/demo4.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react' import { PickerView, Cell } from '@nutui/nutui-react-taro' -const Demo1 = () => { +const Demo4 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -35,4 +35,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo4 diff --git a/src/packages/pickerview/demos/taro/demo5.tsx b/src/packages/pickerview/demos/taro/demo5.tsx index a8d43a86b0..f313af7bb5 100644 --- a/src/packages/pickerview/demos/taro/demo5.tsx +++ b/src/packages/pickerview/demos/taro/demo5.tsx @@ -1,7 +1,7 @@ import React from 'react' import { PickerView, Cell } from '@nutui/nutui-react-taro' -const Demo1 = () => { +const Demo5 = () => { const listData = [ [ { value: 1, label: '南京市' }, @@ -32,4 +32,4 @@ const Demo1 = () => { ) } -export default Demo1 +export default Demo5 diff --git a/src/packages/pickerview/demos/taro/demo7.tsx b/src/packages/pickerview/demos/taro/demo7.tsx new file mode 100644 index 0000000000..a32defecfc --- /dev/null +++ b/src/packages/pickerview/demos/taro/demo7.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useState } from 'react' +import { PickerView, Cell, PickerOptions } from '@nutui/nutui-react-taro' + +const Demo7 = () => { + const [columnsList, setColumnsList] = useState([] as PickerOptions[]) + + useEffect(() => { + setTimeout(() => { + setColumnsList([ + [ + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, + ], + ]) + }, 3000) + }, []) + return ( + <> + + { + console.log('onChange', value, selectedOptions) + }} + /> + + + ) +} +export default Demo7 diff --git a/src/packages/pickerview/doc.en-US.md b/src/packages/pickerview/doc.en-US.md index 6ec4aa0aa8..a66464c311 100644 --- a/src/packages/pickerview/doc.en-US.md +++ b/src/packages/pickerview/doc.en-US.md @@ -64,20 +64,20 @@ import { Picker } from '@nutui/nutui-react' | Property | Description | Type | Default | | --- | --- | --- | --- | -| options | Tabular data | `Array` | `[]` | +| options | Tabular data | `PickerOptionItem[][]` | `[]` | | value | Selected value, controlled | `Array` | `[]` | | defaultValue | Default value | `Array` | `[]` | | threeDimensional | Whether to enable 3D effect | `boolean` | `true` | | duration | The duration of inertial rolling during rapid sliding, in ms | `string` \| `number` | `1000` | | onChange | Called when the value of each column changes | `({value, index, selectedOptions}) => void` | `-` | -### options +### PickerOptionItem | Property | Description | Type | Default | | --- | --- | --- | --- | | label | Text of column | `string` \| `number` | `-` | | value | Value of column | `string` \| `number` | `-` | -| children | Cascader Option | `Array` | `-` | +| children | Cascader Option | `PickerOptionItem[]` | `-` | ## Theming diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index ad1777daa9..0ba9af80dc 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -5,7 +5,7 @@ PickerView 是 Picker 的内容区域。 ## 引入 ```tsx -import { name } from '@nutui/nutui-react' +import { PickerView } from '@nutui/nutui-react' ``` ## 示例代码 @@ -64,19 +64,20 @@ import { name } from '@nutui/nutui-react' | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| options | 列表数据 | `Array` | `[]` | +| options | 列表数据 | `PickerOptionItem[][]` | `[]` | | value | 选中值,受控 | `Array` | `[]` | | defaultValue | 默认选中 | `Array` | `[]` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | -### options 数据结构 +### PickerOptionItem | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | label | 选项的文字内容 | `string` \| `number` | `-` | | value | 选项对应的值,且唯一 | `string` \| `number` | `-` | +| children | 用于级联选项 | `PickerOptionItem[]` | `-` | ## 主题定制 diff --git a/src/packages/pickerview/doc.taro.md b/src/packages/pickerview/doc.taro.md index f8e186b1be..fe1a838aca 100644 --- a/src/packages/pickerview/doc.taro.md +++ b/src/packages/pickerview/doc.taro.md @@ -5,7 +5,7 @@ PickerView 是 Picker 的内容区域。 ## 引入 ```tsx -import { name } from '@nutui/nutui-react-taro' +import { PickerView } from '@nutui/nutui-react-taro' ``` ## 示例代码 @@ -64,19 +64,20 @@ import { name } from '@nutui/nutui-react-taro' | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| options | 列表数据 | `Array` | `[]` | +| options | 列表数据 | `PickerOptionItem[][]` | `[]` | | value | 选中值,受控 | `Array` | `[]` | | defaultValue | 默认选中 | `Array` | `[]` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | -### options 数据结构 +### PickerOptionItem | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | label | 选项的文字内容 | `string` \| `number` | `-` | | value | 选项对应的值,且唯一 | `string` \| `number` | `-` | +| children | 用于级联选项 | `PickerOptionItem[]` | `-` | ## 主题定制 diff --git a/src/packages/pickerview/doc.zh-TW.md b/src/packages/pickerview/doc.zh-TW.md index c944d61291..aecd67eaff 100644 --- a/src/packages/pickerview/doc.zh-TW.md +++ b/src/packages/pickerview/doc.zh-TW.md @@ -5,7 +5,7 @@ PickerView 是 Picker 的內容區域。 ## 引入 ```tsx -import { name } from '@nutui/nutui-react-taro' +import { PickerView } from '@nutui/nutui-react-taro' ``` ## 示例代碼 @@ -64,19 +64,20 @@ import { name } from '@nutui/nutui-react-taro' | 屬性 | 說明 | 類型 | 默認值 | | --- | --- | --- | --- | -| options | 列錶數據 | `Array` | `[]` | +| options | 列錶數據 | `PickerOptionItem[][]` | `[]` | | value | 選中值,受控 | `Array` | `[]` | | defaultValue | 默認選中 | `Array` | `[]` | | threeDimensional | 是否開啟3D效果 | `boolean` | `true` | | duration | 快速滑動時慣性滾動的時長,單位 ms | `string` \| `number` | `1000` | | onChange | 每一列值變更時調用 | `({value, index, selectedOptions}) => void` | `-` | -### options 數據結構 +### PickerOptionItem | 屬性 | 說明 | 類型 | 默認值 | | --- | --- | --- | --- | | label | 選項的文字內容 | `string` \| `number` | `-` | | value | 選項對應的值,且唯一 | `string` \| `number` | `-` | +| children | 用於級聯選項 | `PickerOptionItem[]` | `-` | ## 主題定制 diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index dc0b2b56a9..9f7768459f 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -257,7 +257,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< eventOptions ) } - }) + }, [pickerRollerRef.current, touchStart, touchMove, touchEnd]) const touchRollerStyle = () => { return { diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 47b66ef808..02ff4f535b 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -230,7 +230,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< pickerRollerRef.current?.removeEventListener('touchmove', touchMove) pickerRollerRef.current?.removeEventListener('touchend', touchEnd) } - }) + }, [pickerRollerRef.current, touchStart, touchMove, touchEnd]) const touchRollerStyle = () => { return { diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index 06f20401f6..a1ba018910 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -59,8 +59,12 @@ const InternalPickerView: ForwardRefRenderFunction< * 数据类型:级联、多列 */ const columnsType = useMemo(() => { - const [firstColumn] = props.options as PickerOptions[] - if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { + const firstColumn = (props.options as PickerOptions[])[0] || [] + if ( + Array.isArray(firstColumn) && + firstColumn.length > 0 && + 'children' in firstColumn[0] + ) { return 'cascade' } return 'multiple' @@ -114,7 +118,8 @@ const InternalPickerView: ForwardRefRenderFunction< }, [innerValue, options, columnsType]) useEffect(() => { - if (props.options !== innerOptions) { + const options = props.options + if (Array.isArray(options) && options.length && options !== innerOptions) { setInnerOptions(formatOptions as PickerOptions[]) } }, [props.options, innerValue]) @@ -203,8 +208,12 @@ const InternalPickerView: ForwardRefRenderFunction< threeDimensional={threeDimensional} /> ))} - - + {innerOptions?.length ? ( + <> +
+
+ + ) : null} ) } diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index daeff9c23c..66770248c0 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -58,8 +58,12 @@ const InternalPickerView: ForwardRefRenderFunction< * 数据类型:级联、多列 */ const columnsType = useMemo(() => { - const [firstColumn] = props.options as PickerOptions[] - if (Array.isArray(firstColumn) && 'children' in firstColumn[0]) { + const firstColumn = (props.options as PickerOptions[])[0] || [] + if ( + Array.isArray(firstColumn) && + firstColumn.length > 0 && + 'children' in firstColumn[0] + ) { return 'cascade' } return 'multiple' @@ -113,7 +117,8 @@ const InternalPickerView: ForwardRefRenderFunction< }, [innerValue, options, columnsType]) useEffect(() => { - if (props.options !== innerOptions) { + const options = props.options + if (Array.isArray(options) && options.length && options !== innerOptions) { setInnerOptions(formatOptions as PickerOptions[]) } }, [props.options, innerValue]) @@ -202,8 +207,12 @@ const InternalPickerView: ForwardRefRenderFunction< threeDimensional={threeDimensional} /> ))} -
-
+ {innerOptions?.length ? ( + <> +
+
+ + ) : null}
) } From b5987b948831b1b38804b27de38b2877b7d36727 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 15:42:36 +0800 Subject: [PATCH 21/54] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E6=8E=A7=E5=88=B6=E6=9D=A1=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E9=97=AE=E9=A2=98+demo7=E5=8F=96=E6=B6=88=E4=B8=8D=E8=BF=98?= =?UTF-8?q?=E5=8E=9F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/demos/h5/demo1.tsx | 18 ++++++++++++------ src/packages/picker/demos/h5/demo7.tsx | 1 - src/packages/picker/demos/taro/demo7.tsx | 1 - src/packages/picker/picker.scss | 5 +++-- src/packages/picker/picker.taro.tsx | 3 +-- src/packages/picker/picker.tsx | 2 +- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index f227a06824..fd7ab73bc3 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react' const Demo1 = () => { const defaultValue = new Date() @@ -12,13 +17,14 @@ const Demo1 = () => { const [value, setValue] = useState('2023/01/01') const [show2, setShow2] = useState(false) const [desc2, setDesc2] = useState('') - const confirm1 = (values: (string | number)[], options: PickerOption[]) => { - setDesc1(options.map((option) => option.text).join(' ')) + const confirm1 = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc1(options.map((option) => option.label).join(' ')) } - const change = (options: PickerOption[], values: (string | number)[]) => { + const change = (options: PickerOptionItem[], values: PickerValue[]) => { const v = values.join('/') + console.log('values', v, values, options) setValue(v) - setDesc2(options.map((option) => option.text).join(' ')) + setDesc2(options.map((option) => option.label).join(' ')) } return ( <> @@ -39,7 +45,7 @@ const Demo1 = () => { onConfirm={(options, values) => { setShow1(false) confirm1(values, options) - console.log('onconfirm') + console.log('onconfirm', values, options) }} /> { label: '浦东新区', }, ] - setValue(value) setAsyncData([...[...asyncData]]) }, 0) } diff --git a/src/packages/picker/demos/taro/demo7.tsx b/src/packages/picker/demos/taro/demo7.tsx index 03d5ca162a..f217879e9d 100644 --- a/src/packages/picker/demos/taro/demo7.tsx +++ b/src/packages/picker/demos/taro/demo7.tsx @@ -80,7 +80,6 @@ const Demo7 = () => { label: '浦东新区', }, ] - setValue(value) setAsyncData([...[...asyncData]]) }, 0) } diff --git a/src/packages/picker/picker.scss b/src/packages/picker/picker.scss index f73182d5d8..a95a838b80 100644 --- a/src/packages/picker/picker.scss +++ b/src/packages/picker/picker.scss @@ -6,11 +6,12 @@ &-control { display: flex; - height: $popup-title-height; - font-size: $popup-title-font-size; align-items: center; justify-content: space-between; + height: $popup-title-height; padding: $popup-title-padding; + box-sizing: border-box; + font-size: $popup-title-font-size; } &-cancel-btn { diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 27c8d782c2..fc1a85a4a7 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -79,7 +79,6 @@ const InternalPicker: ForwardRefRenderFunction< onConfirm, onCancel, onClose, - afterClose, onChange, ...rest } = { ...defaultProps, ...props } @@ -125,7 +124,7 @@ const InternalPicker: ForwardRefRenderFunction< setInnerValue(selectedValue) setInnerOptions(options as PickerOptions[]) } - }, [options, selectedValue, innerOptions, innerVisible]) + }, [selectedValue, innerOptions, innerVisible]) const onChangeItem = ({ value, diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index c0f295ffa8..badca709ce 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -123,7 +123,7 @@ const InternalPicker: ForwardRefRenderFunction< setInnerValue(selectedValue) setInnerOptions(options as PickerOptions[]) } - }, [options, selectedValue, innerOptions, innerVisible]) + }, [selectedValue, innerOptions, innerVisible]) const onChangeItem = ({ value, From 0ae1a26962fa3ce90d94ed8ae7fbff50d0175943 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 18:45:22 +0800 Subject: [PATCH 22/54] =?UTF-8?q?fix:=20datepicker=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.json | 2 +- src/packages/datepicker/datepicker.taro.tsx | 48 +++++++++---------- src/packages/datepicker/datepicker.tsx | 48 +++++++++---------- src/packages/datepicker/demos/h5/demo1.tsx | 2 +- src/packages/datepicker/demos/h5/demo2.tsx | 12 +++-- src/packages/datepicker/demos/h5/demo3.tsx | 9 +++- src/packages/datepicker/demos/h5/demo4.tsx | 11 +++-- src/packages/datepicker/demos/h5/demo5.tsx | 11 +++-- src/packages/datepicker/demos/h5/demo6.tsx | 27 ++++++----- src/packages/datepicker/demos/h5/demo7.tsx | 11 +++-- src/packages/datepicker/demos/h5/demo8.tsx | 25 ++++++---- src/packages/datepicker/demos/taro/demo1.tsx | 16 +++++-- src/packages/datepicker/demos/taro/demo2.tsx | 11 +++-- src/packages/datepicker/demos/taro/demo3.tsx | 9 +++- src/packages/datepicker/demos/taro/demo4.tsx | 11 +++-- src/packages/datepicker/demos/taro/demo5.tsx | 11 +++-- src/packages/datepicker/demos/taro/demo6.tsx | 27 ++++++----- src/packages/datepicker/demos/taro/demo7.tsx | 11 +++-- src/packages/datepicker/demos/taro/demo8.tsx | 25 ++++++---- src/packages/pickerview/pickerroller.taro.tsx | 1 + src/packages/pickerview/pickerroller.tsx | 1 + 21 files changed, 204 insertions(+), 125 deletions(-) diff --git a/src/config.json b/src/config.json index 515f48c690..4c4248dcfa 100644 --- a/src/config.json +++ b/src/config.json @@ -582,7 +582,7 @@ "show": true, "taro": true, "author": "dsj", - "dd": false + "dd": true }, { "version": "2.0.0", diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 2d14f28e42..29f8ab3ad6 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -1,11 +1,12 @@ import React, { FunctionComponent, useState, useEffect } from 'react' import { View } from '@tarojs/components' -import Picker, { PickerOption, PickerProps } from '@/packages/picker/index.taro' +import Picker, { PickerProps } from '@/packages/picker/index.taro' import { useConfig } from '@/packages/configprovider/index.taro' import { usePropsValue } from '@/utils/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' import { isDate } from '@/utils/is-date' import { padZero } from '@/utils/pad-zero' +import { PickerOptionItem, PickerValue } from '@/packages/pickerview/index.taro' export interface DatePickerProps extends BasicComponent { value?: Date @@ -38,17 +39,17 @@ export interface DatePickerProps extends BasicComponent { | 'onChange' > > - formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] + formatter: (type: string, option: PickerOptionItem) => PickerOptionItem + filter: (type: string, option: PickerOptionItem[]) => PickerOptionItem[] onClose: () => void onCancel: () => void onConfirm: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptionItem[], + selectedValue: PickerValue[] ) => void onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedOptions: PickerOptionItem[], + selectedValue: PickerValue[], columnIndex: number ) => void } @@ -104,8 +105,8 @@ export const DatePicker: FunctionComponent< minute: lang.min, seconds: lang.seconds, } - const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) - const [pickerOptions, setPickerOptions] = useState([]) + const [pickerValue, setPickerValue] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) const formatValue = (value: Date | null) => { if (!value || (value && !isDate(value))) { value = startDate @@ -218,7 +219,7 @@ export const DatePicker: FunctionComponent< const compareDateChange = ( currentDate: number, newDate: Date | null, - selectedOptions: PickerOption[], + selectedOptions: PickerOptionItem[], index: number ) => { const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() @@ -238,8 +239,8 @@ export const DatePicker: FunctionComponent< } } const handlePickerChange = ( - selectedOptions: PickerOption[], - selectedValue: (number | string)[], + selectedOptions: PickerOptionItem[], + selectedValue: PickerValue[], index: number ) => { const rangeType = type.toLocaleLowerCase() @@ -248,7 +249,7 @@ export const DatePicker: FunctionComponent< rangeType ) ) { - const formatDate: (number | string)[] = [] + const formatDate: PickerValue[] = [] selectedValue.forEach((item) => { formatDate.push(item) }) @@ -313,13 +314,13 @@ export const DatePicker: FunctionComponent< const formatOption = (type: string, value: string | number) => { if (formatter) { return formatter(type, { - text: padZero(value, 2), + label: padZero(value, 2), value: padZero(value, 2), }) } const padMin = padZero(value, 2) const fatter = showChinese ? zhCNType[type] : '' - return { text: padMin + fatter, value: padMin } + return { label: padMin + fatter, value: padMin } } const generateColumn = ( @@ -330,7 +331,7 @@ export const DatePicker: FunctionComponent< columnIndex: number ) => { let cmin = min - const arr: Array = [] + const arr: Array = [] let index = 0 while (cmin <= max) { arr.push(formatOption(type, cmin)) @@ -412,14 +413,13 @@ export const DatePicker: FunctionComponent< onClose={onClose} onCancel={onCancel} value={pickerValue} - onConfirm={(options: PickerOption[], value: (string | number)[]) => - onConfirm && onConfirm(options, value) - } - onChange={( - options: PickerOption[], - value: (number | string)[], - index: number - ) => handlePickerChange(options, value, index)} + onConfirm={( + selectedOptions: PickerOptionItem[], + selectedValue: PickerValue[] + ) => onConfirm && onConfirm(selectedOptions, selectedValue)} + onChange={({ value, index, selectedOptions }) => { + handlePickerChange(selectedOptions, value, index) + }} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 7a7f818b20..a247c450a9 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -1,11 +1,12 @@ import React, { FunctionComponent, useState, useEffect } from 'react' import Picker from '@/packages/picker' -import { PickerOption, PickerProps } from '@/packages/picker/index' +import { PickerProps } from '@/packages/picker/index' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/utils/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' import { isDate } from '@/utils/is-date' import { padZero } from '@/utils/pad-zero' +import { PickerOptionItem, PickerValue } from '@/packages/pickerview/index' export interface DatePickerProps extends BasicComponent { value?: Date @@ -38,17 +39,17 @@ export interface DatePickerProps extends BasicComponent { | 'onChange' > > - formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] + formatter: (type: string, option: PickerOptionItem) => PickerOptionItem + filter: (type: string, option: PickerOptionItem[]) => PickerOptionItem[] onClose: () => void onCancel: () => void onConfirm: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptionItem[], + selectedValue: PickerValue[] ) => void onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedOptions: PickerOptionItem[], + selectedValue: PickerValue[], columnIndex: number ) => void } @@ -104,8 +105,8 @@ export const DatePicker: FunctionComponent< minute: lang.min, seconds: lang.seconds, } - const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) - const [pickerOptions, setPickerOptions] = useState([]) + const [pickerValue, setPickerValue] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) const formatValue = (value: Date | null) => { if (!value || (value && !isDate(value))) { value = startDate @@ -218,7 +219,7 @@ export const DatePicker: FunctionComponent< const compareDateChange = ( currentDate: number, newDate: Date | null, - selectedOptions: PickerOption[], + selectedOptions: PickerOptionItem[], index: number ) => { const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() @@ -238,8 +239,8 @@ export const DatePicker: FunctionComponent< } } const handlePickerChange = ( - selectedOptions: PickerOption[], - selectedValue: (number | string)[], + selectedOptions: PickerOptionItem[], + selectedValue: PickerValue[], index: number ) => { const rangeType = type.toLocaleLowerCase() @@ -248,7 +249,7 @@ export const DatePicker: FunctionComponent< rangeType ) ) { - const formatDate: (number | string)[] = [] + const formatDate: PickerValue[] = [] selectedValue.forEach((item) => { formatDate.push(item) }) @@ -313,13 +314,13 @@ export const DatePicker: FunctionComponent< const formatOption = (type: string, value: string | number) => { if (formatter) { return formatter(type, { - text: padZero(value, 2), + label: padZero(value, 2), value: padZero(value, 2), }) } const padMin = padZero(value, 2) const fatter = showChinese ? zhCNType[type] : '' - return { text: padMin + fatter, value: padMin } + return { label: padMin + fatter, value: padMin } } const generateColumn = ( @@ -330,7 +331,7 @@ export const DatePicker: FunctionComponent< columnIndex: number ) => { let cmin = min - const arr: Array = [] + const arr: Array = [] let index = 0 while (cmin <= max) { arr.push(formatOption(type, cmin)) @@ -407,14 +408,13 @@ export const DatePicker: FunctionComponent< onClose={onClose} onCancel={onCancel} value={pickerValue} - onConfirm={(options: PickerOption[], value: (string | number)[]) => - onConfirm && onConfirm(options, value) - } - onChange={( - options: PickerOption[], - value: (number | string)[], - index: number - ) => handlePickerChange(options, value, index)} + onConfirm={( + selectedOptions: PickerOptionItem[], + selectedValue: PickerValue[] + ) => onConfirm && onConfirm(selectedOptions, selectedValue)} + onChange={({ value, index, selectedOptions }) => { + handlePickerChange(selectedOptions, value, index) + }} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index fd7ab73bc3..154dbe8a0a 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -22,7 +22,7 @@ const Demo1 = () => { } const change = (options: PickerOptionItem[], values: PickerValue[]) => { const v = values.join('/') - console.log('values', v, values, options) + setValue(v) setDesc2(options.map((option) => option.label).join(' ')) } diff --git a/src/packages/datepicker/demos/h5/demo2.tsx b/src/packages/datepicker/demos/h5/demo2.tsx index bd4350dcde..03641ee437 100644 --- a/src/packages/datepicker/demos/h5/demo2.tsx +++ b/src/packages/datepicker/demos/h5/demo2.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerOptionItem, + PickerValue, +} from '@nutui/nutui-react' const Demo2 = () => { const defaultValue = new Date() @@ -8,8 +13,9 @@ const Demo2 = () => { `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` ) - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join('-')) + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + console.log('values', values, options) + setDesc(options.map((option) => option.label).join('-')) } return ( <> diff --git a/src/packages/datepicker/demos/h5/demo3.tsx b/src/packages/datepicker/demos/h5/demo3.tsx index 1143b67773..37f6f241cb 100644 --- a/src/packages/datepicker/demos/h5/demo3.tsx +++ b/src/packages/datepicker/demos/h5/demo3.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerOptionItem, + PickerValue, +} from '@nutui/nutui-react' const Demo3 = () => { const defaultValue = new Date() @@ -11,7 +16,7 @@ const Demo3 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 11:08`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { const date = values.slice(0, 3).join('-') const time = values.slice(3).join(':') setDesc(`${date} ${time}`) diff --git a/src/packages/datepicker/demos/h5/demo4.tsx b/src/packages/datepicker/demos/h5/demo4.tsx index fefb4b317d..67b8494727 100644 --- a/src/packages/datepicker/demos/h5/demo4.tsx +++ b/src/packages/datepicker/demos/h5/demo4.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react' const Demo4 = () => { const defaultValue = new Date() @@ -10,8 +15,8 @@ const Demo4 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/h5/demo5.tsx b/src/packages/datepicker/demos/h5/demo5.tsx index 0ab54a263e..e2a7a3414e 100644 --- a/src/packages/datepicker/demos/h5/demo5.tsx +++ b/src/packages/datepicker/demos/h5/demo5.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerOptionItem, + PickerValue, +} from '@nutui/nutui-react' const Demo5 = () => { const defaultValue = new Date() @@ -10,8 +15,8 @@ const Demo5 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10') - const confirm8 = (options: PickerOption[], values: (string | number)[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm8 = (options: PickerOptionItem[], values: PickerValue[]) => { + setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/h5/demo6.tsx b/src/packages/datepicker/demos/h5/demo6.tsx index 09e831821c..e3e9d9bf20 100644 --- a/src/packages/datepicker/demos/h5/demo6.tsx +++ b/src/packages/datepicker/demos/h5/demo6.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react' const Demo6 = () => { const defaultValue = new Date() @@ -9,36 +14,36 @@ const Demo6 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 10:10`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { const date = options .slice(1, 3) - .map((op) => op.text) + .map((op) => op.label) .join('') const time = options .slice(3) .map((op) => op.value) .join(':') - setDesc(`${options[0].text}年${date} ${time}`) + setDesc(`${options[0].label}年${date} ${time}`) } - const formatter = (type: string, option: PickerOption) => { + const formatter = (type: string, option: PickerOptionItem) => { switch (type) { case 'year': - option.text += '' + option.label += '' break case 'month': - option.text += '月' + option.label += '月' break case 'day': - option.text += '日' + option.label += '日' break case 'hour': - option.text += '时' + option.label += '时' break case 'minute': - option.text += '分' + option.label += '分' break default: - option.text += '' + option.label += '' } return option } diff --git a/src/packages/datepicker/demos/h5/demo7.tsx b/src/packages/datepicker/demos/h5/demo7.tsx index a447fa27f5..afd222d8ae 100644 --- a/src/packages/datepicker/demos/h5/demo7.tsx +++ b/src/packages/datepicker/demos/h5/demo7.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react' const Demo7 = () => { const defaultValue = new Date() @@ -11,8 +16,8 @@ const Demo7 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm6 = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm6 = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc(options.map((option) => option.label).join(':')) } return ( <> diff --git a/src/packages/datepicker/demos/h5/demo8.tsx b/src/packages/datepicker/demos/h5/demo8.tsx index a5cce7f3eb..709cea3193 100644 --- a/src/packages/datepicker/demos/h5/demo8.tsx +++ b/src/packages/datepicker/demos/h5/demo8.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react' const Demo8 = () => { const startDate = new Date(2020, 0, 1) @@ -11,31 +16,31 @@ const Demo8 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 00`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(' ')) + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc(options.map((option) => option.label).join(' ')) } - const filter = (type: string, options: PickerOption[]) => { + const filter = (type: string, options: PickerOptionItem[]) => { if (type === 'hour') { return options.filter((option) => Number(option.value) % 6 === 0) } return options } - const formatter1 = (type: string, option: PickerOption) => { + const formatter1 = (type: string, option: PickerOptionItem) => { switch (type) { case 'year': - option.text += `年` + option.label += `年` break case 'month': - option.text += `月` + option.label += `月` break case 'day': - option.text += `日` + option.label += `日` break case 'hour': - option.text += `时` + option.label += `时` break default: - option.text += '' + option.label += '' } return option } diff --git a/src/packages/datepicker/demos/taro/demo1.tsx b/src/packages/datepicker/demos/taro/demo1.tsx index dadd406cb0..23ff5f1c36 100644 --- a/src/packages/datepicker/demos/taro/demo1.tsx +++ b/src/packages/datepicker/demos/taro/demo1.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react-taro' const Demo1 = () => { const defaultValue = new Date() @@ -12,13 +17,14 @@ const Demo1 = () => { const [value, setValue] = useState('2023/01/01') const [show2, setShow2] = useState(false) const [desc2, setDesc2] = useState('') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc1(options.map((option) => option.text).join(' ')) + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc1(options.map((option) => option.label).join(' ')) } - const change = (options: PickerOption[], values: (string | number)[]) => { + const change = (options: PickerOptionItem[], values: PickerValue[]) => { const v = values.join('/') + setValue(v) - setDesc2(options.map((option) => option.text).join(' ')) + setDesc2(options.map((option) => option.label).join(' ')) } return ( <> diff --git a/src/packages/datepicker/demos/taro/demo2.tsx b/src/packages/datepicker/demos/taro/demo2.tsx index 7d3b42edaf..52b47fd8d3 100644 --- a/src/packages/datepicker/demos/taro/demo2.tsx +++ b/src/packages/datepicker/demos/taro/demo2.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerOptionItem, + PickerValue, +} from '@nutui/nutui-react-taro' const Demo2 = () => { const defaultValue = new Date() @@ -7,8 +12,8 @@ const Demo2 = () => { const [desc, setDesc] = useState( `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` ) - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join('-')) + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc(options.map((option) => option.label).join('-')) } return ( <> diff --git a/src/packages/datepicker/demos/taro/demo3.tsx b/src/packages/datepicker/demos/taro/demo3.tsx index 0334291b40..2d1c33e401 100644 --- a/src/packages/datepicker/demos/taro/demo3.tsx +++ b/src/packages/datepicker/demos/taro/demo3.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react-taro' const Demo3 = () => { const defaultValue = new Date() @@ -11,7 +16,7 @@ const Demo3 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 11:08`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { const date = values.slice(0, 3).join('-') const time = values.slice(3).join(':') setDesc(`${date} ${time}`) diff --git a/src/packages/datepicker/demos/taro/demo4.tsx b/src/packages/datepicker/demos/taro/demo4.tsx index 99f4ca3a55..69de439a51 100644 --- a/src/packages/datepicker/demos/taro/demo4.tsx +++ b/src/packages/datepicker/demos/taro/demo4.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react-taro' const Demo4 = () => { const defaultValue = new Date() @@ -10,8 +15,8 @@ const Demo4 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/taro/demo5.tsx b/src/packages/datepicker/demos/taro/demo5.tsx index db834280b3..2ee4f868cd 100644 --- a/src/packages/datepicker/demos/taro/demo5.tsx +++ b/src/packages/datepicker/demos/taro/demo5.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react-taro' const Demo5 = () => { const defaultValue = new Date() @@ -10,8 +15,8 @@ const Demo5 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10') - const confirm = (options: PickerOption[], values: (string | number)[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm = (options: PickerOptionItem[], values: PickerValue[]) => { + setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/taro/demo6.tsx b/src/packages/datepicker/demos/taro/demo6.tsx index 037c396eec..7314893062 100644 --- a/src/packages/datepicker/demos/taro/demo6.tsx +++ b/src/packages/datepicker/demos/taro/demo6.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react-taro' const Demo6 = () => { const defaultValue = new Date() @@ -9,36 +14,36 @@ const Demo6 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 10:10`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { const date = options .slice(1, 3) - .map((op) => op.text) + .map((op) => op.label) .join('') const time = options .slice(3) .map((op) => op.value) .join(':') - setDesc(`${options[0].text}年${date} ${time}`) + setDesc(`${options[0].label}年${date} ${time}`) } - const formatter = (type: string, option: PickerOption) => { + const formatter = (type: string, option: PickerOptionItem) => { switch (type) { case 'year': - option.text += '' + option.label += '' break case 'month': - option.text += '月' + option.label += '月' break case 'day': - option.text += '日' + option.label += '日' break case 'hour': - option.text += '时' + option.label += '时' break case 'minute': - option.text += '分' + option.label += '分' break default: - option.text += '' + option.label += '' } return option } diff --git a/src/packages/datepicker/demos/taro/demo7.tsx b/src/packages/datepicker/demos/taro/demo7.tsx index 24702e28bf..8934627df5 100644 --- a/src/packages/datepicker/demos/taro/demo7.tsx +++ b/src/packages/datepicker/demos/taro/demo7.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react-taro' const Demo7 = () => { const defaultValue = new Date() @@ -11,8 +16,8 @@ const Demo7 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc(options.map((option) => option.label).join(':')) } return ( <> diff --git a/src/packages/datepicker/demos/taro/demo8.tsx b/src/packages/datepicker/demos/taro/demo8.tsx index 31911a0f5a..d1e1d69be8 100644 --- a/src/packages/datepicker/demos/taro/demo8.tsx +++ b/src/packages/datepicker/demos/taro/demo8.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptionItem, +} from '@nutui/nutui-react-taro' const Demo8 = () => { const startDate = new Date(2020, 0, 1) @@ -11,31 +16,31 @@ const Demo8 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 00`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(' ')) + const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + setDesc(options.map((option) => option.label).join(' ')) } - const filter = (type: string, options: PickerOption[]) => { + const filter = (type: string, options: PickerOptionItem[]) => { if (type === 'hour') { return options.filter((option) => Number(option.value) % 6 === 0) } return options } - const formatter = (type: string, option: PickerOption) => { + const formatter = (type: string, option: PickerOptionItem) => { switch (type) { case 'year': - option.text += `年` + option.label += `年` break case 'month': - option.text += `月` + option.label += `月` break case 'day': - option.text += `日` + option.label += `日` break case 'hour': - option.text += `时` + option.label += `时` break default: - option.text += '' + option.label += '' } return option } diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 9f7768459f..279039abe3 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -211,6 +211,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< } useEffect(() => { + moving.current = false setScrollDistance(0) transformY.current = 0 modifyStatus(false) diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 02ff4f535b..da6eea6f28 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -210,6 +210,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< } useEffect(() => { + moving.current = false setScrollDistance(0) transformY.current = 0 modifyStatus(false) From 26f93195683633997b977ff07dc27a2ad8b55b05 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 12 Feb 2025 20:15:22 +0800 Subject: [PATCH 23/54] fix: update test --- .../datepicker/__test__/datepicker.spec.tsx | 32 ++--- src/packages/picker/__tests__/picker.spec.tsx | 128 ++++++++++-------- 2 files changed, 82 insertions(+), 78 deletions(-) diff --git a/src/packages/datepicker/__test__/datepicker.spec.tsx b/src/packages/datepicker/__test__/datepicker.spec.tsx index 2bfbba3dcc..562ed34c7e 100644 --- a/src/packages/datepicker/__test__/datepicker.spec.tsx +++ b/src/packages/datepicker/__test__/datepicker.spec.tsx @@ -21,7 +21,7 @@ test('Show Chinese', async () => { fireEvent.click(confirmBtn) await waitFor(() => { expect( - confirm.mock.calls[0][0].map((option: any) => option.text).join('') + confirm.mock.calls[0][0].map((option: any) => option.label).join('') ).toEqual(`${currentYear - 10}年01月01日`) }) }) @@ -40,13 +40,9 @@ test('Min date & Max date', async () => { /> ) - const columns = container.querySelectorAll('.nut-picker-list')[0] - const lists = columns.querySelectorAll('.nut-picker-roller-item-title') - const years = ['2020', '2021', '2022'] + const columns = container.querySelectorAll('.nut-pickerview-list')[0] + const lists = columns.querySelectorAll('.nut-pickerview-roller-item-title') expect(lists.length).toBe(3) - lists.forEach((list, i) => { - expect(list.textContent).toEqual(years[i]) - }) rerender( { const formatter = (type: string, option: any) => { switch (type) { case 'year': - option.text += '' + option.label += '' break case 'month': - option.text += 'M' + option.label += 'M' break case 'day': - option.text += 'D' + option.label += 'D' break case 'hour': - option.text += 'H' + option.label += 'H' break case 'minute': - option.text += 'M' + option.label += 'M' break default: - option.text += '' + option.label += '' } return option } @@ -148,7 +144,7 @@ test('should pick defaultValue', async () => { fireEvent.click(confirmBtn) await waitFor(() => expect( - confirm.mock.calls[0][0].map((option: any) => option.text).join('') + confirm.mock.calls[0][0].map((option: any) => option.label).join('') ).toEqual('20210301') ) }) @@ -166,8 +162,8 @@ test('Increment step setting', async () => { /> ) - const columns = container.querySelectorAll('.nut-picker-list')[1] - const lists = columns.querySelectorAll('.nut-picker-roller-item') + const columns = container.querySelectorAll('.nut-pickerview-list')[1] + const lists = columns.querySelectorAll('.nut-pickerview-roller-item') expect(lists.length).toBe(12) }) @@ -189,7 +185,7 @@ test('Filter Time', async () => { /> ) - const columns = container.querySelectorAll('.nut-picker-list')[3] - const lists = columns.querySelectorAll('.nut-picker-roller-item') + const columns = container.querySelectorAll('.nut-pickerview-list')[3] + const lists = columns.querySelectorAll('.nut-pickerview-roller-item') expect(lists.length).toBe(4) }) diff --git a/src/packages/picker/__tests__/picker.spec.tsx b/src/packages/picker/__tests__/picker.spec.tsx index 0c08207240..60fe500538 100644 --- a/src/packages/picker/__tests__/picker.spec.tsx +++ b/src/packages/picker/__tests__/picker.spec.tsx @@ -10,7 +10,7 @@ function sleep(delay = 0): Promise { } interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -18,72 +18,76 @@ interface PickerOption { } const simpleColumns = [ - { text: '南京市', value: 'NanJing' }, - { text: '无锡市', value: 'WuXi' }, - { text: '海北藏族自治区', value: 'ZangZu' }, - { text: '北京市', value: 'BeiJing' }, - { text: '连云港市', value: 'LianYunGang' }, + [ + { label: '南京市', value: 'NanJing' }, + { label: '无锡市', value: 'WuXi' }, + { label: '海北藏族自治区', value: 'ZangZu' }, + { label: '北京市', value: 'BeiJing' }, + { label: '连云港市', value: 'LianYunGang' }, + ], ] const multipleColumns = [ [ - { text: '周一', value: 'Monday' }, - { text: '周二', value: 'Tuesday' }, - { text: '周三', value: 'Wednesday' }, - { text: '周四', value: 'Thursday' }, - { text: '周五', value: 'Friday' }, + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, ], // 第二列 [ - { text: '上午', value: 'Morning' }, - { text: '下午', value: 'Afternoon' }, - { text: '晚上', value: 'Evening' }, + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, ], ] const multistageColumns = [ - { - text: '浙江', - value: 'ZheJiang', - children: [ - { - text: '杭州', - value: 'HangZhou', - children: [ - { text: '西湖区', value: 'XiHu' }, - { text: '余杭区', value: 'YuHang' }, - ], - }, - { - text: '温州', - value: 'WenZhou', - children: [ - { text: '鹿城区', value: 'LuCheng' }, - { text: '瓯海区', value: 'OuHai' }, - ], - }, - ], - }, - { - text: '福建', - value: 'FuJian', - children: [ - { - text: '福州', - value: 'FuZhou', - children: [ - { text: '鼓楼区', value: 'GuLou' }, - { text: '台江区', value: 'TaiJiang' }, - ], - }, - { - text: '厦门', - value: 'XiaMen', - children: [ - { text: '思明区', value: 'SiMing' }, - { text: '海沧区', value: 'HaiCang' }, - ], - }, - ], - }, + [ + { + label: '浙江', + value: 'ZheJiang', + children: [ + { + label: '杭州', + value: 'HangZhou', + children: [ + { label: '西湖区', value: 'XiHu' }, + { label: '余杭区', value: 'YuHang' }, + ], + }, + { + label: '温州', + value: 'WenZhou', + children: [ + { label: '鹿城区', value: 'LuCheng' }, + { label: '瓯海区', value: 'OuHai' }, + ], + }, + ], + }, + { + label: '福建', + value: 'FuJian', + children: [ + { + label: '福州', + value: 'FuZhou', + children: [ + { label: '鼓楼区', value: 'GuLou' }, + { label: '台江区', value: 'TaiJiang' }, + ], + }, + { + label: '厦门', + value: 'XiaMen', + children: [ + { label: '思明区', value: 'SiMing' }, + { label: '海沧区', value: 'HaiCang' }, + ], + }, + ], + }, + ], ] test('renderLabel works', async () => { @@ -98,8 +102,9 @@ test('simple list-data confirm event', async () => { const { container } = render( confirm(value)} + onConfirm={(selectedOptions, value) => confirm(value)} /> ) const confirmBtn = container.querySelectorAll('.nut-picker-confirm-btn')[0] @@ -137,6 +142,7 @@ test('multiple list-data render', async () => { const { container } = render( confirm(value)} /> @@ -154,6 +160,7 @@ test('multistageColumns list-data render', async () => { confirm(value)} /> ) @@ -167,7 +174,7 @@ test('multistageColumns list-data render', async () => { test('async list-data render', async () => { const confirm = vi.fn() const PenderContent = () => { - const [asyncColumns, setasyncColumns] = useState([]) + const [asyncColumns, setasyncColumns] = useState([]) setTimeout(() => { setasyncColumns(simpleColumns) @@ -176,6 +183,7 @@ test('async list-data render', async () => { return ( confirm(value)} /> From d5118f6f22e75f56dede15257aec3dcd01b08a62 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 09:39:17 +0800 Subject: [PATCH 24/54] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E6=A0=87?= =?UTF-8?q?=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/pickerview/pickerview.taro.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index a1ba018910..b6675f4dd1 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -210,8 +210,8 @@ const InternalPickerView: ForwardRefRenderFunction< ))} {innerOptions?.length ? ( <> -
-
+ + ) : null} From a787f7da9bbb2e4de3c59875a5fe029e38e8889b Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 11:58:58 +0800 Subject: [PATCH 25/54] feat: add test --- .../__snapshots__/pickerview.spec.tsx.snap | 298 +----------------- .../pickerview/__test__/pickerview.spec.tsx | 50 ++- 2 files changed, 42 insertions(+), 306 deletions(-) diff --git a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap index 0ab994d585..2374bb7a94 100644 --- a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap +++ b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap @@ -1,21 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`should handle empty options 1`] = ` -
-
-
-
-
-
-`; - -exports[`should handle invalid defaultValue 1`] = ` +exports[`should match base 1`] = `
-
-
-
-
-
- 南京市 -
-
- 无锡市 -
-
- 海北藏族自治区 -
-
- 北京市 -
-
- 连云港市 -
-
- 大庆市 -
-
- 绥化市 -
-
- 潍坊市 -
-
- 乌鲁木齐市 -
-
-
-
-
-
-
- , - "container":
-
-
-
-
- 南京市 -
-
- 无锡市 -
-
- 海北藏族自治区 -
-
- 北京市 -
-
- 连云港市 -
-
- 大庆市 -
-
- 绥化市 -
-
- 潍坊市 -
-
- 乌鲁木齐市 -
-
-
-
-
-
-
, - "debug": [Function], - "findAllByAltText": [Function], - "findAllByDisplayValue": [Function], - "findAllByLabelText": [Function], - "findAllByPlaceholderText": [Function], - "findAllByRole": [Function], - "findAllByTestId": [Function], - "findAllByText": [Function], - "findAllByTitle": [Function], - "findByAltText": [Function], - "findByDisplayValue": [Function], - "findByLabelText": [Function], - "findByPlaceholderText": [Function], - "findByRole": [Function], - "findByTestId": [Function], - "findByText": [Function], - "findByTitle": [Function], - "getAllByAltText": [Function], - "getAllByDisplayValue": [Function], - "getAllByLabelText": [Function], - "getAllByPlaceholderText": [Function], - "getAllByRole": [Function], - "getAllByTestId": [Function], - "getAllByText": [Function], - "getAllByTitle": [Function], - "getByAltText": [Function], - "getByDisplayValue": [Function], - "getByLabelText": [Function], - "getByPlaceholderText": [Function], - "getByRole": [Function], - "getByTestId": [Function], - "getByText": [Function], - "getByTitle": [Function], - "queryAllByAltText": [Function], - "queryAllByDisplayValue": [Function], - "queryAllByLabelText": [Function], - "queryAllByPlaceholderText": [Function], - "queryAllByRole": [Function], - "queryAllByTestId": [Function], - "queryAllByText": [Function], - "queryAllByTitle": [Function], - "queryByAltText": [Function], - "queryByDisplayValue": [Function], - "queryByLabelText": [Function], - "queryByPlaceholderText": [Function], - "queryByRole": [Function], - "queryByTestId": [Function], - "queryByText": [Function], - "queryByTitle": [Function], - "rerender": [Function], - "unmount": [Function], -} -`; - -exports[`should match onchange 2`] = `
-
-
-
- 南京市 -
-
- 无锡市 -
-
- 海北藏族自治区 -
-
- 北京市 -
-
- 连云港市 -
-
- 大庆市 -
-
- 绥化市 -
-
- 潍坊市 -
-
- 乌鲁木齐市 -
-
-
-
-
-
+ />
`; diff --git a/src/packages/pickerview/__test__/pickerview.spec.tsx b/src/packages/pickerview/__test__/pickerview.spec.tsx index 85c48a0cfd..4079631473 100644 --- a/src/packages/pickerview/__test__/pickerview.spec.tsx +++ b/src/packages/pickerview/__test__/pickerview.spec.tsx @@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react' import { render, waitFor, screen } from '@testing-library/react' import '@testing-library/jest-dom' import PickerView from '../pickerview' +import { PickerOptions } from '../types' +import useRefs from '@/utils/use-refs' const listData = [ [ @@ -93,7 +95,7 @@ const cascadeData = [ ], ] -test('should match snapshot', () => { +test('should match base', () => { const { container } = render( ) @@ -132,27 +134,32 @@ test('should render with Multi Column', () => { test('should match onchange', async () => { const PenderContent = () => { - const [value, setValue] = useState([1]) + const [value, setValue] = useState([]) + const [options, setInnerOptions] = useState([]) useEffect(() => { - setTimeout(() => { - setValue([3]) + const timer = setTimeout(() => { + setInnerOptions(listData) + setValue([1]) }, 1000) + + return () => clearTimeout(timer) // 清理定时器 }, []) return ( { - if (value[0] === 3) { - setValue([1]) + options={options} + onChange={({ value }) => { + if (value[0] === 1) { + setValue([3]) } }} /> ) } - const container = render() + + const { container } = render() await waitFor(() => { expect(container).toMatchSnapshot() @@ -162,7 +169,7 @@ test('should match onchange', async () => { test('should match cascade', () => { const { container } = render( `${item.label} | 测试`} options={cascadeData} onChange={() => {}} @@ -170,3 +177,26 @@ test('should match cascade', () => { ) expect(container).toMatchSnapshot() }) + +test('should match stopMomentum', async () => { + const PenderContent = () => { + const [refs, setRefs] = useRefs() + + return ( + { + refs.map((ref: any) => { + ref.stopMomentum() + return ref + }) + }} + /> + ) + } + const { container } = render() + const columns = container.querySelectorAll('.nut-pickerview-list') + expect(columns.length).toBe(2) +}) From c10fcc7922aa031db40c3e476ca0e1daca6b9c07 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 15:25:38 +0800 Subject: [PATCH 26/54] fix: update doc and test --- .../__snapshots__/pickerview.spec.tsx.snap | 82 +------------------ .../pickerview/__test__/pickerview.spec.tsx | 35 +++++--- src/packages/pickerview/doc.en-US.md | 2 +- src/packages/pickerview/pickerroller.taro.tsx | 2 +- src/packages/pickerview/pickerroller.tsx | 4 +- 5 files changed, 32 insertions(+), 93 deletions(-) diff --git a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap index 2374bb7a94..10f61a642c 100644 --- a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap +++ b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap @@ -10,7 +10,7 @@ exports[`should match base 1`] = ` >
`; -exports[`should match snapshot 1`] = ` -
-
-
-
-
- 南京市 -
-
- 无锡市 -
-
- 海北藏族自治区 -
-
- 北京市 -
-
- 连云港市 -
-
- 大庆市 -
-
- 绥化市 -
-
- 潍坊市 -
-
- 乌鲁木齐市 -
-
-
-
-
-
-
-`; - exports[`should render tiled 1`] = `
{ ) expect(container).toMatchSnapshot() }) - test('should match stopMomentum', async () => { const PenderContent = () => { + function useRefs() { + const refs = React.useRef([]) + + const setRefs = React.useCallback( + (index: number) => (el: HTMLDivElement) => { + if (el) refs.current[index] = el + }, + [] + ) + + const reset = React.useCallback(() => { + refs.current = [] + }, []) + + return [refs.current, setRefs as any, reset] + } + const [refs, setRefs] = useRefs() + const first = useRef(true) return ( { options={cascadeData} setRefs={setRefs} onChange={() => { - refs.map((ref: any) => { - ref.stopMomentum() - return ref - }) + if (!first.current) { + refs[0].stopMomentum() + } else { + first.current = false + } }} /> ) } - const { container } = render() - const columns = container.querySelectorAll('.nut-pickerview-list') - expect(columns.length).toBe(2) + render() }) diff --git a/src/packages/pickerview/doc.en-US.md b/src/packages/pickerview/doc.en-US.md index a66464c311..fb09497e26 100644 --- a/src/packages/pickerview/doc.en-US.md +++ b/src/packages/pickerview/doc.en-US.md @@ -5,7 +5,7 @@ The PickerView is the content area of the Picker. ## Import ```tsx -import { Picker } from '@nutui/nutui-react' +import { PickerView } from '@nutui/nutui-react' ``` ## Demo diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 9f7768459f..9a543abd91 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -55,7 +55,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< const currentLineSpacing = computedStyle.getPropertyValue( '--nutui-picker-item-height' ) - currentLineSpacing && + !!currentLineSpacing && (lineSpacing.current = parseFloat(currentLineSpacing)) } }, [pickerRollerRef.current]) diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 02ff4f535b..1cfd1f4b6a 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -53,7 +53,9 @@ const InternalPickerRoller: ForwardRefRenderFunction< const currentLineSpacing = computedStyle.getPropertyValue( '--nutui-picker-item-height' ) - lineSpacing.current = parseFloat(currentLineSpacing) + + !!currentLineSpacing && + (lineSpacing.current = parseFloat(currentLineSpacing)) } }, []) From 9e32bcdc7caba956d726f288706ccf87545ef0c7 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 15:31:32 +0800 Subject: [PATCH 27/54] fix: adjust the dependency package location --- src/packages/pickerview/pickerview.taro.tsx | 2 +- src/packages/pickerview/pickerview.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index b6675f4dd1..ffcf57760b 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -10,6 +10,7 @@ import classNames from 'classnames' import { View } from '@tarojs/components' import isEqual from 'react-fast-compare' import { ComponentDefaults } from '@/utils/typings' +import { usePropsValue } from '@/utils/use-props-value' import { PickerViewProps, PickerOptionItem, @@ -17,7 +18,6 @@ import { PickerOptions, } from './types' import PickerRoller from './pickerroller.taro' -import { usePropsValue } from '@/utils/use-props-value' const defaultProps = { ...ComponentDefaults, diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index 66770248c0..75d3bb3c14 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -9,6 +9,7 @@ import React, { import classNames from 'classnames' import isEqual from 'react-fast-compare' import { ComponentDefaults } from '@/utils/typings' +import { usePropsValue } from '@/utils/use-props-value' import { PickerViewProps, PickerOptionItem, @@ -16,7 +17,6 @@ import { PickerOptions, } from './types' import PickerRoller from './pickerroller' -import { usePropsValue } from '@/utils/use-props-value' const defaultProps = { ...ComponentDefaults, From f539b833f0427e639a22606b3c1ec0a7728fb7e7 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 15:33:25 +0800 Subject: [PATCH 28/54] fix: adjust the dependency package path --- src/packages/pickerview/pickerroller.taro.tsx | 2 +- src/packages/pickerview/pickerroller.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 9a543abd91..ab97d306ec 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -6,7 +6,7 @@ import React, { useImperativeHandle, } from 'react' import { View } from '@tarojs/components' -import { useTouch } from '@/utils/use-touch' +import { useTouch } from '@/hooks/use-touch' import { passiveSupported } from '@/utils/supports-passive' import { PickerRollerProps, PickerOptionItem } from './types' import { web } from '@/utils/platform-taro' diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 1cfd1f4b6a..75d443f5b4 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -5,7 +5,7 @@ import React, { ForwardRefRenderFunction, useImperativeHandle, } from 'react' -import { useTouch } from '@/utils/use-touch' +import { useTouch } from '@/hooks/use-touch' import { passiveSupported } from '@/utils/supports-passive' import { PickerRollerProps, PickerOptionItem } from './types' From f06ec00809d2f84fedf5903982e2629525641214 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 16:05:30 +0800 Subject: [PATCH 29/54] fix: adjust the dependency package path --- src/packages/pickerview/pickerview.taro.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index ffcf57760b..8a52e52e97 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -10,7 +10,7 @@ import classNames from 'classnames' import { View } from '@tarojs/components' import isEqual from 'react-fast-compare' import { ComponentDefaults } from '@/utils/typings' -import { usePropsValue } from '@/utils/use-props-value' +import { usePropsValue } from '@/hooks/use-props-value' import { PickerViewProps, PickerOptionItem, From b67dfcaf8df17d49095b64e1684435730940495d Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 16:09:03 +0800 Subject: [PATCH 30/54] fix: adjust the dependency package path --- src/packages/pickerview/pickerview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index 75d3bb3c14..0fef411bea 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -9,7 +9,7 @@ import React, { import classNames from 'classnames' import isEqual from 'react-fast-compare' import { ComponentDefaults } from '@/utils/typings' -import { usePropsValue } from '@/utils/use-props-value' +import { usePropsValue } from '@/hooks/use-props-value' import { PickerViewProps, PickerOptionItem, From 99055646d4468db60b5c82976c6f2cf16482eafd Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 16:18:02 +0800 Subject: [PATCH 31/54] fix: update doc props type --- src/packages/pickerview/doc.en-US.md | 6 +++--- src/packages/pickerview/doc.md | 6 +++--- src/packages/pickerview/doc.taro.md | 6 +++--- src/packages/pickerview/doc.zh-TW.md | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/packages/pickerview/doc.en-US.md b/src/packages/pickerview/doc.en-US.md index fb09497e26..42006732bd 100644 --- a/src/packages/pickerview/doc.en-US.md +++ b/src/packages/pickerview/doc.en-US.md @@ -64,9 +64,9 @@ import { PickerView } from '@nutui/nutui-react' | Property | Description | Type | Default | | --- | --- | --- | --- | -| options | Tabular data | `PickerOptionItem[][]` | `[]` | -| value | Selected value, controlled | `Array` | `[]` | -| defaultValue | Default value | `Array` | `[]` | +| options | Tabular data | `PickerOptions[]` | `[]` | +| value | Selected value, controlled | `PickerValue[]` | `[]` | +| defaultValue | Default value | `PickerValue[]` | `[]` | | threeDimensional | Whether to enable 3D effect | `boolean` | `true` | | duration | The duration of inertial rolling during rapid sliding, in ms | `string` \| `number` | `1000` | | onChange | Called when the value of each column changes | `({value, index, selectedOptions}) => void` | `-` | diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index 0ba9af80dc..ef3cb7dc18 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -64,9 +64,9 @@ import { PickerView } from '@nutui/nutui-react' | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| options | 列表数据 | `PickerOptionItem[][]` | `[]` | -| value | 选中值,受控 | `Array` | `[]` | -| defaultValue | 默认选中 | `Array` | `[]` | +| options | 列表数据 | `PickerOptions[]` | `[]` | +| value | 选中值,受控 | `PickerValue[]` | `[]` | +| defaultValue | 默认选中 | `PickerValue[]` | `[]` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | diff --git a/src/packages/pickerview/doc.taro.md b/src/packages/pickerview/doc.taro.md index fe1a838aca..344fc9dee3 100644 --- a/src/packages/pickerview/doc.taro.md +++ b/src/packages/pickerview/doc.taro.md @@ -64,9 +64,9 @@ import { PickerView } from '@nutui/nutui-react-taro' | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | -| options | 列表数据 | `PickerOptionItem[][]` | `[]` | -| value | 选中值,受控 | `Array` | `[]` | -| defaultValue | 默认选中 | `Array` | `[]` | +| options | 列表数据 | `PickerOptions[]` | `[]` | +| value | 选中值,受控 | `PickerValue[]` | `[]` | +| defaultValue | 默认选中 | `PickerValue[]` | `[]` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | diff --git a/src/packages/pickerview/doc.zh-TW.md b/src/packages/pickerview/doc.zh-TW.md index aecd67eaff..073f5b8e4f 100644 --- a/src/packages/pickerview/doc.zh-TW.md +++ b/src/packages/pickerview/doc.zh-TW.md @@ -64,9 +64,9 @@ import { PickerView } from '@nutui/nutui-react-taro' | 屬性 | 說明 | 類型 | 默認值 | | --- | --- | --- | --- | -| options | 列錶數據 | `PickerOptionItem[][]` | `[]` | -| value | 選中值,受控 | `Array` | `[]` | -| defaultValue | 默認選中 | `Array` | `[]` | +| options | 列錶數據 | `PickerOptions[]` | `[]` | +| value | 選中值,受控 | `PickerValue[]` | `[]` | +| defaultValue | 默認選中 | `PickerValue[]` | `[]` | | threeDimensional | 是否開啟3D效果 | `boolean` | `true` | | duration | 快速滑動時慣性滾動的時長,單位 ms | `string` \| `number` | `1000` | | onChange | 每一列值變更時調用 | `({value, index, selectedOptions}) => void` | `-` | From 0c448c8dc8b0bbee4bac7e01e63a3154aaf6ac6b Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 16:48:48 +0800 Subject: [PATCH 32/54] fix: adjust note --- src/packages/pickerview/pickerroller.taro.tsx | 38 ++++++------------ src/packages/pickerview/pickerroller.tsx | 39 ++++++------------- 2 files changed, 24 insertions(+), 53 deletions(-) diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index ab97d306ec..eb310acdb1 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -47,7 +47,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< const transformY = useRef(0) const [scrollDistance, setScrollDistance] = useState(0) - // 获取 lineSpacing.current CSS变量 + // lineSpacing.current CSS variable useEffect(() => { const element = pickerRollerRef.current if (element && web()) { @@ -55,6 +55,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< const currentLineSpacing = computedStyle.getPropertyValue( '--nutui-picker-item-height' ) + !!currentLineSpacing && (lineSpacing.current = parseFloat(currentLineSpacing)) } @@ -85,7 +86,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< const setMove = (move: number, type?: string, time?: number) => { let updateMove = move + transformY.current if (type === 'end') { - // 限定滚动距离 if (updateMove > 0) { updateMove = 0 } @@ -106,7 +106,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< let deg = 0 const currentDeg = (-updateMove / lineSpacing.current + 1) * rotation - // picker 滚动的最大角度 const maxDeg = (options.length + 1) * rotation const minDeg = 0 @@ -122,7 +121,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< onSelect?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex) } - // 开始滚动 const touchStart = (event: React.TouchEvent) => { touch.start(event) setStartY(touch.deltaY.current) @@ -144,9 +142,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< if (!moving.current) return const move = touch.deltaY.current - startY const moveTime = Date.now() - startTime - // 区分是否为惯性滚动 if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { - // 惯性滚动 const distance = momentum(move, moveTime) setMove(distance, 'end', +duration) } else { @@ -157,12 +153,10 @@ const InternalPickerRoller: ForwardRefRenderFunction< }, 0) } - // 惯性滚动 距离 + // inertial rolling distance const momentum = (distance: number, duration: number) => { let nDistance = distance - // 惯性滚动的速度 const speed = Math.abs(nDistance / duration) - // 惯性滚动的距离 nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) return nDistance } @@ -170,21 +164,13 @@ const InternalPickerRoller: ForwardRefRenderFunction< const modifyStatus = (type?: boolean, val?: string | number) => { const value = val || props.value let index = -1 - if (value) { - options.some((item, idx) => { - if (item.value === value) { - index = idx - return true - } - return false - }) - } else { - options.forEach((item, i) => { - if (item.value === props.value) { - index = i - } - }) - } + options.some((item, idx) => { + if (item.value === value) { + index = idx + return true // Stop iterating once the match is found + } + return false + }) setCurrIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current @@ -192,13 +178,13 @@ const InternalPickerRoller: ForwardRefRenderFunction< setMove(-move) } - // 惯性滚动结束 + // stop inertial rolling const stopMomentum = () => { moving.current = false setTouchTime(0) setChooseValue(scrollDistance) } - // 阻止默认事件 + const preventDefault = ( event: React.TouchEvent, isStopPropagation?: boolean diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 75d443f5b4..0a2dbae31f 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -45,7 +45,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< const transformY = useRef(0) const [scrollDistance, setScrollDistance] = useState(0) - // 获取 lineSpacing.current CSS变量 + // lineSpacing.current CSS variable useEffect(() => { const element = pickerRollerRef.current if (element) { @@ -57,7 +57,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< !!currentLineSpacing && (lineSpacing.current = parseFloat(currentLineSpacing)) } - }, []) + }, [pickerRollerRef.current]) const isHidden = (index: number) => { if (index >= currIndex + 8 || index <= currIndex - 8) { @@ -84,7 +84,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< const setMove = (move: number, type?: string, time?: number) => { let updateMove = move + transformY.current if (type === 'end') { - // 限定滚动距离 if (updateMove > 0) { updateMove = 0 } @@ -105,7 +104,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< let deg = 0 const currentDeg = (-updateMove / lineSpacing.current + 1) * rotation - // picker 滚动的最大角度 const maxDeg = (options.length + 1) * rotation const minDeg = 0 @@ -121,7 +119,6 @@ const InternalPickerRoller: ForwardRefRenderFunction< onSelect?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex) } - // 开始滚动 const touchStart = (event: React.TouchEvent) => { touch.start(event) setStartY(touch.deltaY.current) @@ -143,9 +140,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< if (!moving.current) return const move = touch.deltaY.current - startY const moveTime = Date.now() - startTime - // 区分是否为惯性滚动 if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { - // 惯性滚动 const distance = momentum(move, moveTime) setMove(distance, 'end', +duration) } else { @@ -156,12 +151,10 @@ const InternalPickerRoller: ForwardRefRenderFunction< }, 0) } - // 惯性滚动 距离 + // inertial rolling distance const momentum = (distance: number, duration: number) => { let nDistance = distance - // 惯性滚动的速度 const speed = Math.abs(nDistance / duration) - // 惯性滚动的距离 nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) return nDistance } @@ -169,21 +162,13 @@ const InternalPickerRoller: ForwardRefRenderFunction< const modifyStatus = (type?: boolean, val?: string | number) => { const value = val || props.value let index = -1 - if (value) { - options.some((item, idx) => { - if (item.value === value) { - index = idx - return true - } - return false - }) - } else { - options.forEach((item, i) => { - if (item.value === props.value) { - index = i - } - }) - } + options.some((item, idx) => { + if (item.value === value) { + index = idx + return true // Stop iterating once the match is found + } + return false + }) setCurrIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current @@ -191,13 +176,13 @@ const InternalPickerRoller: ForwardRefRenderFunction< setMove(-move) } - // 惯性滚动结束 + // stop inertial rolling const stopMomentum = () => { moving.current = false setTouchTime(0) setChooseValue(scrollDistance) } - // 阻止默认事件 + const preventDefault = ( event: React.TouchEvent, isStopPropagation?: boolean From 7760169385a4fb929f21f904b5af09e2e516cef6 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 18:11:43 +0800 Subject: [PATCH 33/54] fix: adjust the dependency package path --- src/packages/pickerview/pickerview.taro.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/pickerview/pickerview.taro.tsx b/src/packages/pickerview/pickerview.taro.tsx index a1ba018910..e9ee2c3410 100644 --- a/src/packages/pickerview/pickerview.taro.tsx +++ b/src/packages/pickerview/pickerview.taro.tsx @@ -17,7 +17,7 @@ import { PickerOptions, } from './types' import PickerRoller from './pickerroller.taro' -import { usePropsValue } from '@/utils/use-props-value' +import { usePropsValue } from '@/hooks/use-props-value' const defaultProps = { ...ComponentDefaults, From 88d4f804bb6493cd39f106d1533cadc1084be807 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 18:49:30 +0800 Subject: [PATCH 34/54] fix: optimize details --- src/packages/pickerview/hooks/useStyles.ts | 26 ++ src/packages/pickerview/pickerroller.taro.tsx | 234 ++++++--------- src/packages/pickerview/pickerroller.tsx | 267 ++++++++---------- src/packages/pickerview/pickerview.scss | 2 +- src/packages/pickerview/utils.ts | 4 + 5 files changed, 235 insertions(+), 298 deletions(-) create mode 100644 src/packages/pickerview/hooks/useStyles.ts create mode 100644 src/packages/pickerview/utils.ts diff --git a/src/packages/pickerview/hooks/useStyles.ts b/src/packages/pickerview/hooks/useStyles.ts new file mode 100644 index 0000000000..ecdb3e178e --- /dev/null +++ b/src/packages/pickerview/hooks/useStyles.ts @@ -0,0 +1,26 @@ +export const useStyles = ( + touchTime: number, + touchDeg: string, + scrollDistance: number, + lineSpacing: React.MutableRefObject, + rotation: number +) => { + const getTransitionStyle = (transformValue: string) => ({ + transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, + transform: transformValue, + }) + + const touchRollerStyle = () => + getTransitionStyle(`rotate3d(1, 0, 0, ${touchDeg})`) + + const touchTiledStyle = () => + getTransitionStyle(`translate3d(0, ${scrollDistance}px, 0)`) + + const rollerStyle = (index: number) => ({ + transform: `rotate3d(1, 0, 0, ${-rotation * (index + 1)}deg) translate3d(0px, 0px, ${Math.round( + lineSpacing.current * 3.2 + )}px)`, + }) + + return { touchRollerStyle, touchTiledStyle, rollerStyle } +} diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index eb310acdb1..c8dfe41a1a 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -10,6 +10,9 @@ import { useTouch } from '@/hooks/use-touch' import { passiveSupported } from '@/utils/supports-passive' import { PickerRollerProps, PickerOptionItem } from './types' import { web } from '@/utils/platform-taro' +import { preventDefault } from '@/utils' +import { momentum } from './utils' +import { useStyles } from './hooks/useStyles' const InternalPickerRoller: ForwardRefRenderFunction< { stopMomentum: () => void; moving: boolean }, @@ -21,190 +24,151 @@ const InternalPickerRoller: ForwardRefRenderFunction< threeDimensional = true, duration = 1000, onSelect, - renderLabel = (item: PickerOptionItem) => { - return item.label - }, + renderLabel = (item: PickerOptionItem) => item.label, } = props - const touch = useTouch() const DEFAULT_DURATION = 200 - // 触发惯性滑动条件: 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move, 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动 const INERTIA_TIME = 300 const INERTIA_DISTANCE = 15 - const [currIndex, setCurrIndex] = useState(1) + const ROTATION = 20 + const touch = useTouch() + const [currentIndex, setCurrentIndex] = useState(1) const lineSpacing = useRef(36) const [touchTime, setTouchTime] = useState(0) const [touchDeg, setTouchDeg] = useState('0deg') - const rotation = 20 - const moving = useRef(false) - + const isMoving = useRef(false) const rollerRef = useRef(null) const pickerRollerRef = useRef(null) - const [startTime, setStartTime] = useState(0) const [startY, setStartY] = useState(0) - const transformY = useRef(0) const [scrollDistance, setScrollDistance] = useState(0) - // lineSpacing.current CSS variable - useEffect(() => { - const element = pickerRollerRef.current - if (element && web()) { - const computedStyle = getComputedStyle(element) - const currentLineSpacing = computedStyle.getPropertyValue( - '--nutui-picker-item-height' - ) - - !!currentLineSpacing && - (lineSpacing.current = parseFloat(currentLineSpacing)) - } - }, [pickerRollerRef.current]) + const { touchRollerStyle, touchTiledStyle, rollerStyle } = useStyles( + touchTime, + touchDeg, + scrollDistance, + lineSpacing, + ROTATION + ) - const isHidden = (index: number) => { - if (index >= currIndex + 8 || index <= currIndex - 8) { - return true - } - return false - } + const isItemHidden = (index: number) => + index >= currentIndex + 8 || index <= currentIndex - 8 - const setTransform = ( + const applyTransform = ( type: string, deg: string, time = DEFAULT_DURATION, translateY = 0 ) => { - let nTime = time - if (type !== 'end') { - nTime = 0 - } - setTouchTime(nTime) + setTouchTime(type !== 'end' ? 0 : time) setTouchDeg(deg) setScrollDistance(translateY) } - const setMove = (move: number, type?: string, time?: number) => { - let updateMove = move + transformY.current + const handleMove = (move: number, type?: string, time?: number) => { + let updatedMove = move + transformY.current if (type === 'end') { - if (updateMove > 0) { - updateMove = 0 - } - if (updateMove < -(options.length - 1) * lineSpacing.current) { - updateMove = -(options.length - 1) * lineSpacing.current - } + updatedMove = Math.max( + Math.min(updatedMove, 0), + -(options.length - 1) * lineSpacing.current + ) - // 设置滚动距离为lineSpacing.current的倍数值 + // 滚动距离为lineSpacing.current的倍数值 const endMove = - Math.round(updateMove / lineSpacing.current) * lineSpacing.current - const deg = `${ - (Math.abs(Math.round(endMove / lineSpacing.current)) + 1) * rotation - }deg` - - setTransform(type, deg, time, endMove) - setCurrIndex(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) + Math.round(updatedMove / lineSpacing.current) * lineSpacing.current + const deg = `${(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) * ROTATION}deg` + applyTransform(type, deg, time, endMove) + setCurrentIndex(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) } else { - let deg = 0 - const currentDeg = (-updateMove / lineSpacing.current + 1) * rotation - - const maxDeg = (options.length + 1) * rotation - const minDeg = 0 - - deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - if (minDeg <= deg && deg < maxDeg) { - setTransform('', `${deg}deg`, undefined, updateMove) - setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) + const currentDeg = (-updatedMove / lineSpacing.current + 1) * ROTATION + const deg = Math.min( + Math.max(currentDeg, 0), + (options.length + 1) * ROTATION + ) + if (deg >= 0 && deg < (options.length + 1) * ROTATION) { + applyTransform('', `${deg}deg`, undefined, updatedMove) + setCurrentIndex( + Math.abs(Math.round(updatedMove / lineSpacing.current)) + 1 + ) } } } - const setChooseValue = (move: number) => { + const selectValue = (move: number) => { onSelect?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex) } - const touchStart = (event: React.TouchEvent) => { + const handleTouchStart = (event: React.TouchEvent) => { touch.start(event) setStartY(touch.deltaY.current) setStartTime(Date.now()) transformY.current = scrollDistance } - const touchMove = (event: React.TouchEvent) => { + const handleTouchMove = (event: React.TouchEvent) => { touch.move(event) if ((touch as any).isVertical) { - moving.current = true + isMoving.current = true preventDefault(event, true) } const move = touch.deltaY.current - startY - setMove(move) + handleMove(move) } - const touchEnd = () => { - if (!moving.current) return + const handleTouchEnd = () => { + if (!isMoving.current) return const move = touch.deltaY.current - startY const moveTime = Date.now() - startTime if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { const distance = momentum(move, moveTime) - setMove(distance, 'end', +duration) + handleMove(distance, 'end', +duration) } else { - setMove(move, 'end') + handleMove(move, 'end') } setTimeout(() => { touch.reset() }, 0) } - // inertial rolling distance - const momentum = (distance: number, duration: number) => { - let nDistance = distance - const speed = Math.abs(nDistance / duration) - nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) - return nDistance - } - - const modifyStatus = (type?: boolean, val?: string | number) => { - const value = val || props.value - let index = -1 - options.some((item, idx) => { - if (item.value === value) { - index = idx - return true // Stop iterating once the match is found - } - return false - }) - - setCurrIndex(index === -1 ? 1 : index + 1) + const updateStatus = (shouldSelect?: boolean, value?: string | number) => { + const selectedValue = value || props.value + const index = options.findIndex((item) => item.value === selectedValue) + setCurrentIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current - type && setChooseValue(-move) - setMove(-move) + shouldSelect && selectValue(-move) + handleMove(-move) } - // stop inertial rolling - const stopMomentum = () => { - moving.current = false + const stopMomentumScroll = () => { + isMoving.current = false setTouchTime(0) - setChooseValue(scrollDistance) + selectValue(scrollDistance) } - const preventDefault = ( - event: React.TouchEvent, - isStopPropagation?: boolean - ) => { - event.preventDefault() - - if (isStopPropagation) { - event.stopPropagation() + // lineSpacing.current CSS variable + useEffect(() => { + const element = pickerRollerRef.current + if (element && web()) { + const computedStyle = getComputedStyle(element) + const currentLineSpacing = computedStyle.getPropertyValue( + '--nutui-picker-item-height' + ) + !!currentLineSpacing && + (lineSpacing.current = parseFloat(currentLineSpacing)) } - } + }, [pickerRollerRef.current]) useEffect(() => { + isMoving.current = false setScrollDistance(0) transformY.current = 0 - modifyStatus(false) + updateStatus(false) }, [options, props.value]) useImperativeHandle(ref, () => ({ - stopMomentum, - moving: moving.current, + stopMomentum: stopMomentumScroll, + moving: isMoving.current, })) useEffect(() => { @@ -213,73 +177,57 @@ const InternalPickerRoller: ForwardRefRenderFunction< : false pickerRollerRef.current?.addEventListener( 'touchstart', - touchStart, + handleTouchStart, eventOptions ) pickerRollerRef.current?.addEventListener( 'touchmove', - touchMove, + handleTouchMove, eventOptions ) pickerRollerRef.current?.addEventListener( 'touchend', - touchEnd, + handleTouchEnd, eventOptions ) return () => { pickerRollerRef.current?.removeEventListener( 'touchstart', - touchStart, + handleTouchStart, eventOptions ) pickerRollerRef.current?.removeEventListener( 'touchmove', - touchMove, + handleTouchMove, eventOptions ) pickerRollerRef.current?.removeEventListener( 'touchend', - touchEnd, + handleTouchEnd, eventOptions ) } - }, [pickerRollerRef.current, touchStart, touchMove, touchEnd]) - - const touchRollerStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `rotate3d(1, 0, 0, ${touchDeg})`, - } - } - const touchTileStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `translate3d(0, ${scrollDistance}px, 0)`, - } - } - - const rollerStyle = (index: number) => { - return { - transform: `rotate3d(1, 0, 0, ${ - -rotation * (index + 1) - }deg) translate3d(0px, 0px, ${Math.round(lineSpacing.current * 3.2)}px)`, - } - } + }, [ + pickerRollerRef.current, + handleTouchStart, + handleTouchMove, + handleTouchEnd, + ]) return ( {/* 3D 效果 */} {threeDimensional && options.map((item, index) => ( ))} - {/* 平铺 */} + {/* Tiled */} {!threeDimensional && options.map((item, index) => { return ( {renderLabel(item)} diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 0a2dbae31f..60ff26ada5 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -8,6 +8,9 @@ import React, { import { useTouch } from '@/hooks/use-touch' import { passiveSupported } from '@/utils/supports-passive' import { PickerRollerProps, PickerOptionItem } from './types' +import { preventDefault } from '@/utils' +import { momentum } from './utils' +import { useStyles } from './hooks/useStyles' const InternalPickerRoller: ForwardRefRenderFunction< { stopMomentum: () => void; moving: boolean }, @@ -19,241 +22,199 @@ const InternalPickerRoller: ForwardRefRenderFunction< threeDimensional = true, duration = 1000, onSelect, - renderLabel = (item: PickerOptionItem) => { - return item.label - }, + renderLabel = (item: PickerOptionItem) => item.label, } = props - const touch = useTouch() const DEFAULT_DURATION = 200 - // 触发惯性滑动条件: 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move, 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动 const INERTIA_TIME = 300 const INERTIA_DISTANCE = 15 - const [currIndex, setCurrIndex] = useState(1) + const ROTATION = 20 + const touch = useTouch() + const [currentIndex, setCurrentIndex] = useState(1) const lineSpacing = useRef(36) const [touchTime, setTouchTime] = useState(0) const [touchDeg, setTouchDeg] = useState('0deg') - const rotation = 20 - const moving = useRef(false) - + const isMoving = useRef(false) const rollerRef = useRef(null) const pickerRollerRef = useRef(null) - const [startTime, setStartTime] = useState(0) const [startY, setStartY] = useState(0) - const transformY = useRef(0) const [scrollDistance, setScrollDistance] = useState(0) - // lineSpacing.current CSS variable - useEffect(() => { - const element = pickerRollerRef.current - if (element) { - const computedStyle = getComputedStyle(element) - const currentLineSpacing = computedStyle.getPropertyValue( - '--nutui-picker-item-height' - ) - - !!currentLineSpacing && - (lineSpacing.current = parseFloat(currentLineSpacing)) - } - }, [pickerRollerRef.current]) + const { touchRollerStyle, touchTiledStyle, rollerStyle } = useStyles( + touchTime, + touchDeg, + scrollDistance, + lineSpacing, + ROTATION + ) - const isHidden = (index: number) => { - if (index >= currIndex + 8 || index <= currIndex - 8) { - return true - } - return false - } + const isItemHidden = (index: number) => + index >= currentIndex + 8 || index <= currentIndex - 8 - const setTransform = ( + const applyTransform = ( type: string, deg: string, time = DEFAULT_DURATION, translateY = 0 ) => { - let nTime = time - if (type !== 'end') { - nTime = 0 - } - setTouchTime(nTime) + setTouchTime(type !== 'end' ? 0 : time) setTouchDeg(deg) setScrollDistance(translateY) } - const setMove = (move: number, type?: string, time?: number) => { - let updateMove = move + transformY.current + const handleMove = (move: number, type?: string, time?: number) => { + let updatedMove = move + transformY.current if (type === 'end') { - if (updateMove > 0) { - updateMove = 0 - } - if (updateMove < -(options.length - 1) * lineSpacing.current) { - updateMove = -(options.length - 1) * lineSpacing.current - } + updatedMove = Math.max( + Math.min(updatedMove, 0), + -(options.length - 1) * lineSpacing.current + ) - // 设置滚动距离为lineSpacing.current的倍数值 + // 滚动距离为lineSpacing.current的倍数值 const endMove = - Math.round(updateMove / lineSpacing.current) * lineSpacing.current - const deg = `${ - (Math.abs(Math.round(endMove / lineSpacing.current)) + 1) * rotation - }deg` - - setTransform(type, deg, time, endMove) - setCurrIndex(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) + Math.round(updatedMove / lineSpacing.current) * lineSpacing.current + const deg = `${(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) * ROTATION}deg` + applyTransform(type, deg, time, endMove) + setCurrentIndex(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) } else { - let deg = 0 - const currentDeg = (-updateMove / lineSpacing.current + 1) * rotation - - const maxDeg = (options.length + 1) * rotation - const minDeg = 0 - - deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - if (minDeg <= deg && deg < maxDeg) { - setTransform('', `${deg}deg`, undefined, updateMove) - setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) + const currentDeg = (-updatedMove / lineSpacing.current + 1) * ROTATION + const deg = Math.min( + Math.max(currentDeg, 0), + (options.length + 1) * ROTATION + ) + if (deg >= 0 && deg < (options.length + 1) * ROTATION) { + applyTransform('', `${deg}deg`, undefined, updatedMove) + setCurrentIndex( + Math.abs(Math.round(updatedMove / lineSpacing.current)) + 1 + ) } } } - const setChooseValue = (move: number) => { + const selectValue = (move: number) => { onSelect?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex) } - const touchStart = (event: React.TouchEvent) => { + const handleTouchStart = (event: React.TouchEvent) => { touch.start(event) setStartY(touch.deltaY.current) setStartTime(Date.now()) transformY.current = scrollDistance } - const touchMove = (event: React.TouchEvent) => { + const handleTouchMove = (event: React.TouchEvent) => { touch.move(event) if ((touch as any).isVertical) { - moving.current = true + isMoving.current = true preventDefault(event, true) } const move = touch.deltaY.current - startY - setMove(move) + handleMove(move) } - const touchEnd = () => { - if (!moving.current) return + const handleTouchEnd = () => { + if (!isMoving.current) return const move = touch.deltaY.current - startY const moveTime = Date.now() - startTime if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { const distance = momentum(move, moveTime) - setMove(distance, 'end', +duration) + handleMove(distance, 'end', +duration) } else { - setMove(move, 'end') + handleMove(move, 'end') } setTimeout(() => { touch.reset() }, 0) } - // inertial rolling distance - const momentum = (distance: number, duration: number) => { - let nDistance = distance - const speed = Math.abs(nDistance / duration) - nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) - return nDistance - } - - const modifyStatus = (type?: boolean, val?: string | number) => { - const value = val || props.value - let index = -1 - options.some((item, idx) => { - if (item.value === value) { - index = idx - return true // Stop iterating once the match is found - } - return false - }) - - setCurrIndex(index === -1 ? 1 : index + 1) + const updateStatus = (shouldSelect?: boolean, value?: string | number) => { + const selectedValue = value || props.value + const index = options.findIndex((item) => item.value === selectedValue) + setCurrentIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current - type && setChooseValue(-move) - setMove(-move) + shouldSelect && selectValue(-move) + handleMove(-move) } - // stop inertial rolling - const stopMomentum = () => { - moving.current = false + const stopMomentumScroll = () => { + isMoving.current = false setTouchTime(0) - setChooseValue(scrollDistance) + selectValue(scrollDistance) } - const preventDefault = ( - event: React.TouchEvent, - isStopPropagation?: boolean - ) => { - if (typeof event.cancelable !== 'boolean' || event.cancelable) { - event.preventDefault() - } - - if (isStopPropagation) { - event.stopPropagation() + // lineSpacing.current CSS variable + useEffect(() => { + const element = pickerRollerRef.current + if (element) { + const computedStyle = getComputedStyle(element) + const currentLineSpacing = computedStyle.getPropertyValue( + '--nutui-picker-item-height' + ) + !!currentLineSpacing && + (lineSpacing.current = parseFloat(currentLineSpacing)) } - } + }, [pickerRollerRef.current]) useEffect(() => { + isMoving.current = false setScrollDistance(0) transformY.current = 0 - modifyStatus(false) + updateStatus(false) }, [options, props.value]) useImperativeHandle(ref, () => ({ - stopMomentum, - moving: moving.current, + stopMomentum: stopMomentumScroll, + moving: isMoving.current, })) useEffect(() => { const options = passiveSupported ? { passive: false } : false - pickerRollerRef.current?.addEventListener('touchstart', touchStart, options) - pickerRollerRef.current?.addEventListener('touchmove', touchMove, options) - pickerRollerRef.current?.addEventListener('touchend', touchEnd, options) + pickerRollerRef.current?.addEventListener( + 'touchstart', + handleTouchStart, + options + ) + pickerRollerRef.current?.addEventListener( + 'touchmove', + handleTouchMove, + options + ) + pickerRollerRef.current?.addEventListener( + 'touchend', + handleTouchEnd, + options + ) return () => { - pickerRollerRef.current?.removeEventListener('touchstart', touchStart) - pickerRollerRef.current?.removeEventListener('touchmove', touchMove) - pickerRollerRef.current?.removeEventListener('touchend', touchEnd) - } - }, [pickerRollerRef.current, touchStart, touchMove, touchEnd]) - - const touchRollerStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `rotate3d(1, 0, 0, ${touchDeg})`, - } - } - const touchTileStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `translate3d(0, ${scrollDistance}px, 0)`, - } - } - - const rollerStyle = (index: number) => { - return { - transform: `rotate3d(1, 0, 0, ${ - -rotation * (index + 1) - }deg) translate3d(0px, 0px, ${Math.round(lineSpacing.current * 3.2)}px)`, + pickerRollerRef.current?.removeEventListener( + 'touchstart', + handleTouchStart + ) + pickerRollerRef.current?.removeEventListener('touchmove', handleTouchMove) + pickerRollerRef.current?.removeEventListener('touchend', handleTouchEnd) } - } + }, [ + pickerRollerRef.current, + handleTouchStart, + handleTouchMove, + handleTouchEnd, + ]) return (
- {/* 3D 效果 */} + {/* 3D */} {threeDimensional && options.map((item, index) => (
))} - {/* 平铺 */} + {/* Tiled */} {!threeDimensional && - options.map((item, index) => { - return ( -
- {renderLabel(item)} -
- ) - })} + options.map((item, index) => ( +
+ {renderLabel(item)} +
+ ))}
) diff --git a/src/packages/pickerview/pickerview.scss b/src/packages/pickerview/pickerview.scss index 595e61d224..1face81104 100644 --- a/src/packages/pickerview/pickerview.scss +++ b/src/packages/pickerview/pickerview.scss @@ -65,7 +65,7 @@ } &-roller-item, - &-roller-item-title { + &-roller-item-tiled { width: 100%; height: $picker-item-height; line-height: $picker-item-height; diff --git a/src/packages/pickerview/utils.ts b/src/packages/pickerview/utils.ts new file mode 100644 index 0000000000..cc022656da --- /dev/null +++ b/src/packages/pickerview/utils.ts @@ -0,0 +1,4 @@ +export const momentum = (distance: number, duration: number) => { + const speed = Math.abs(distance / duration) + return (speed / 0.003) * (distance < 0 ? -1 : 1) +} From 654d1e023198fa09428b3a402adbdebfec69071b Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 18:52:52 +0800 Subject: [PATCH 35/54] fix: update test --- .../__snapshots__/pickerview.spec.tsx.snap | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap index 10f61a642c..723cd2df1b 100644 --- a/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap +++ b/src/packages/pickerview/__test__/__snapshots__/pickerview.spec.tsx.snap @@ -180,47 +180,47 @@ exports[`should render tiled 1`] = ` style="transition: transform 0ms cubic-bezier(0.17, 0.89, 0.45, 1); transform: translate3d(0, 0px, 0);" >
南京市
无锡市
海北藏族自治区
北京市
连云港市
大庆市
绥化市
潍坊市
乌鲁木齐市
From d9628e7c560e29de33ff5bf859d8eda6a0cc00df Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 20:13:02 +0800 Subject: [PATCH 36/54] fix: path not compilable --- src/packages/pickerview/hooks/useStyles.ts | 26 ------------------ src/packages/pickerview/pickerroller.taro.tsx | 3 +-- src/packages/pickerview/pickerroller.tsx | 3 +-- src/packages/pickerview/utils.ts | 27 +++++++++++++++++++ 4 files changed, 29 insertions(+), 30 deletions(-) delete mode 100644 src/packages/pickerview/hooks/useStyles.ts diff --git a/src/packages/pickerview/hooks/useStyles.ts b/src/packages/pickerview/hooks/useStyles.ts deleted file mode 100644 index ecdb3e178e..0000000000 --- a/src/packages/pickerview/hooks/useStyles.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const useStyles = ( - touchTime: number, - touchDeg: string, - scrollDistance: number, - lineSpacing: React.MutableRefObject, - rotation: number -) => { - const getTransitionStyle = (transformValue: string) => ({ - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: transformValue, - }) - - const touchRollerStyle = () => - getTransitionStyle(`rotate3d(1, 0, 0, ${touchDeg})`) - - const touchTiledStyle = () => - getTransitionStyle(`translate3d(0, ${scrollDistance}px, 0)`) - - const rollerStyle = (index: number) => ({ - transform: `rotate3d(1, 0, 0, ${-rotation * (index + 1)}deg) translate3d(0px, 0px, ${Math.round( - lineSpacing.current * 3.2 - )}px)`, - }) - - return { touchRollerStyle, touchTiledStyle, rollerStyle } -} diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index c8dfe41a1a..d553df569f 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -11,8 +11,7 @@ import { passiveSupported } from '@/utils/supports-passive' import { PickerRollerProps, PickerOptionItem } from './types' import { web } from '@/utils/platform-taro' import { preventDefault } from '@/utils' -import { momentum } from './utils' -import { useStyles } from './hooks/useStyles' +import { momentum, useStyles } from './utils' const InternalPickerRoller: ForwardRefRenderFunction< { stopMomentum: () => void; moving: boolean }, diff --git a/src/packages/pickerview/pickerroller.tsx b/src/packages/pickerview/pickerroller.tsx index 60ff26ada5..deb1512a76 100644 --- a/src/packages/pickerview/pickerroller.tsx +++ b/src/packages/pickerview/pickerroller.tsx @@ -9,8 +9,7 @@ import { useTouch } from '@/hooks/use-touch' import { passiveSupported } from '@/utils/supports-passive' import { PickerRollerProps, PickerOptionItem } from './types' import { preventDefault } from '@/utils' -import { momentum } from './utils' -import { useStyles } from './hooks/useStyles' +import { momentum, useStyles } from './utils' const InternalPickerRoller: ForwardRefRenderFunction< { stopMomentum: () => void; moving: boolean }, diff --git a/src/packages/pickerview/utils.ts b/src/packages/pickerview/utils.ts index cc022656da..2d948b7361 100644 --- a/src/packages/pickerview/utils.ts +++ b/src/packages/pickerview/utils.ts @@ -2,3 +2,30 @@ export const momentum = (distance: number, duration: number) => { const speed = Math.abs(distance / duration) return (speed / 0.003) * (distance < 0 ? -1 : 1) } + +export const useStyles = ( + touchTime: number, + touchDeg: string, + scrollDistance: number, + lineSpacing: React.MutableRefObject, + rotation: number +) => { + const getTransitionStyle = (transformValue: string) => ({ + transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, + transform: transformValue, + }) + + const touchRollerStyle = () => + getTransitionStyle(`rotate3d(1, 0, 0, ${touchDeg})`) + + const touchTiledStyle = () => + getTransitionStyle(`translate3d(0, ${scrollDistance}px, 0)`) + + const rollerStyle = (index: number) => ({ + transform: `rotate3d(1, 0, 0, ${-rotation * (index + 1)}deg) translate3d(0px, 0px, ${Math.round( + lineSpacing.current * 3.2 + )}px)`, + }) + + return { touchRollerStyle, touchTiledStyle, rollerStyle } +} From 38296d379b36162ebec0a07afc36cc1f1b1f5f9d Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 13 Feb 2025 20:21:40 +0800 Subject: [PATCH 37/54] fix: key error --- src/packages/pickerview/pickerroller.taro.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index d553df569f..10c38d145f 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -240,7 +240,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< return ( {renderLabel(item)} From 65238d3880401c113c36fba4e173afe55f84577c Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Fri, 14 Feb 2025 10:17:36 +0800 Subject: [PATCH 38/54] fix: update datepicker test --- src/packages/datepicker/__test__/datepicker.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/datepicker/__test__/datepicker.spec.tsx b/src/packages/datepicker/__test__/datepicker.spec.tsx index 562ed34c7e..befde3672c 100644 --- a/src/packages/datepicker/__test__/datepicker.spec.tsx +++ b/src/packages/datepicker/__test__/datepicker.spec.tsx @@ -41,7 +41,7 @@ test('Min date & Max date', async () => { ) const columns = container.querySelectorAll('.nut-pickerview-list')[0] - const lists = columns.querySelectorAll('.nut-pickerview-roller-item-title') + const lists = columns.querySelectorAll('.nut-pickerview-roller-item-tiled') expect(lists.length).toBe(3) rerender( Date: Fri, 14 Feb 2025 19:18:11 +0800 Subject: [PATCH 39/54] fix: hidden entry --- src/config.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/config.json b/src/config.json index 754e8b9951..79c8ee2bf3 100644 --- a/src/config.json +++ b/src/config.json @@ -582,7 +582,7 @@ "show": true, "taro": true, "author": "dsj", - "dd": true + "dd": false }, { "version": "2.0.0", @@ -594,7 +594,7 @@ "show": true, "taro": true, "author": "hanyuxinting", - "dd": true + "dd": false }, { "version": "2.0.0", @@ -607,7 +607,7 @@ "taro": true, "exportEmpty": true, "author": "hanyuxinting", - "dd": true + "dd": false }, { "version": "3.0.0", @@ -687,7 +687,7 @@ "sort": 10, "show": true, "taro": true, - "dd": true, + "dd": false, "author": "songsong" }, { @@ -699,7 +699,7 @@ "sort": 15, "show": true, "taro": true, - "dd": true, + "dd": false, "author": "songsong" }, { From b458e92074f2b1146c04a5abbc5622ca17ef7a29 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Mon, 17 Feb 2025 10:51:08 +0800 Subject: [PATCH 40/54] fix: modify config --- src/config.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/config.json b/src/config.json index 79c8ee2bf3..868dbf98e4 100644 --- a/src/config.json +++ b/src/config.json @@ -691,7 +691,7 @@ "author": "songsong" }, { - "version": "2.0.0", + "version": "3.0.0", "name": "PickerView", "type": "component", "cName": "选择器视图", @@ -699,6 +699,7 @@ "sort": 15, "show": true, "taro": true, + "v15": false, "dd": false, "author": "songsong" }, @@ -1467,4 +1468,4 @@ ] } ] -} \ No newline at end of file +} From ffba77fa8e7a88c897212b2cbb412adf4081b949 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Mon, 17 Feb 2025 11:10:50 +0800 Subject: [PATCH 41/54] fix: remove repeat config --- src/config.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/config.json b/src/config.json index 868dbf98e4..1c1bafcbcb 100644 --- a/src/config.json +++ b/src/config.json @@ -703,19 +703,6 @@ "dd": false, "author": "songsong" }, - { - "version": "3.0.0", - "name": "PickerView", - "type": "component", - "cName": "选择器视图", - "desc": "PickerView 是 Picker 的内容区域。", - "sort": 15, - "show": true, - "taro": true, - "v15": false, - "dd": true, - "author": "songsong" - }, { "version": "3.0.0", "name": "Radio", From f6edf91a8d8911ae121f7153771c7cd78fec004a Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 25 Feb 2025 11:08:38 +0800 Subject: [PATCH 42/54] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=BC=95=E5=85=A5=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/picker/picker.taro.tsx | 9 ++++----- src/packages/picker/picker.tsx | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index efefedb9fc..9190e7620b 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -9,15 +9,14 @@ import { View } from '@tarojs/components' import classNames from 'classnames' import isEqual from 'react-fast-compare' import { - PickerView, PickerOptions, PickerValue, PickerOptionItem, PickerOnChangeCallbackParameter, - SafeArea, - Popup, - PopupProps, -} from '@nutui/nutui-react-taro' +} from '@/packages/pickerview/types' +import PickerView from '@/packages/pickerview/index.taro' +import Popup, { PopupProps } from '@/packages/popup/index.taro' +import SafeArea from '@/packages/safearea/index.taro' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider/index.taro' import { usePropsValue } from '@/hooks/use-props-value' diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 45068b5815..6e4f7076b1 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -8,15 +8,14 @@ import React, { import classNames from 'classnames' import isEqual from 'react-fast-compare' import { - PickerView, PickerOptions, PickerValue, PickerOptionItem, PickerOnChangeCallbackParameter, - SafeArea, - Popup, - PopupProps, -} from '@nutui/nutui-react' +} from '@/packages/pickerview/types' +import PickerView from '@/packages/pickerview/index' +import Popup, { PopupProps } from '@/packages/popup/index' +import SafeArea from '@/packages/safearea/index' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/hooks/use-props-value' From 9688104ca7ff515b84e6e0cbb012ecd10cfed445 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 25 Feb 2025 14:29:30 +0800 Subject: [PATCH 43/54] =?UTF-8?q?fix:=20ts=E7=B1=BB=E5=9E=8B=E5=92=8C?= =?UTF-8?q?=E5=8D=95=E6=B5=8B=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datepicker/__test__/datepicker.spec.tsx | 12 ++++----- src/packages/datepicker/datepicker.taro.tsx | 24 ++++++++++------- src/packages/datepicker/datepicker.tsx | 27 ++++++++++--------- src/packages/datepicker/demos/h5/demo1.tsx | 6 ++--- src/packages/datepicker/demos/h5/demo2.tsx | 4 +-- src/packages/datepicker/demos/h5/demo3.tsx | 4 +-- src/packages/datepicker/demos/h5/demo4.tsx | 4 +-- src/packages/datepicker/demos/h5/demo5.tsx | 4 +-- src/packages/datepicker/demos/h5/demo6.tsx | 7 ++--- src/packages/datepicker/demos/h5/demo7.tsx | 4 +-- src/packages/datepicker/demos/h5/demo8.tsx | 9 ++++--- src/packages/datepicker/demos/taro/demo1.tsx | 6 ++--- src/packages/datepicker/demos/taro/demo2.tsx | 4 +-- src/packages/datepicker/demos/taro/demo3.tsx | 4 +-- src/packages/datepicker/demos/taro/demo4.tsx | 4 +-- src/packages/datepicker/demos/taro/demo5.tsx | 4 +-- src/packages/datepicker/demos/taro/demo6.tsx | 7 ++--- src/packages/datepicker/demos/taro/demo7.tsx | 4 +-- src/packages/datepicker/demos/taro/demo8.tsx | 9 ++++--- src/packages/picker/demos/h5/demo1.tsx | 4 +-- src/packages/picker/demos/h5/demo6.tsx | 12 +++++++-- src/packages/picker/demos/h5/demo7.tsx | 5 +++- src/packages/picker/demos/h5/demo8.tsx | 4 +-- src/packages/picker/demos/taro/demo1.tsx | 4 +-- src/packages/picker/demos/taro/demo6.tsx | 5 +++- src/packages/picker/demos/taro/demo7.tsx | 5 +++- src/packages/picker/demos/taro/demo8.tsx | 4 +-- src/packages/picker/picker.taro.tsx | 6 ++--- src/packages/picker/picker.tsx | 6 ++--- .../pickerview/__test__/pickerview.spec.tsx | 2 +- src/packages/pickerview/doc.en-US.md | 4 +-- src/packages/pickerview/doc.md | 4 +-- src/packages/pickerview/doc.taro.md | 4 +-- src/packages/pickerview/doc.zh-TW.md | 4 +-- src/packages/pickerview/index.taro.ts | 2 +- src/packages/pickerview/index.ts | 2 +- src/packages/pickerview/pickerroller.taro.tsx | 12 +++++---- src/packages/pickerview/pickerroller.tsx | 12 +++++---- src/packages/pickerview/pickerview.taro.tsx | 16 +++++------ src/packages/pickerview/pickerview.tsx | 16 +++++------ src/packages/pickerview/types.ts | 16 +++++------ 41 files changed, 164 insertions(+), 132 deletions(-) diff --git a/src/packages/datepicker/__test__/datepicker.spec.tsx b/src/packages/datepicker/__test__/datepicker.spec.tsx index befde3672c..26a75e4ae9 100644 --- a/src/packages/datepicker/__test__/datepicker.spec.tsx +++ b/src/packages/datepicker/__test__/datepicker.spec.tsx @@ -40,8 +40,8 @@ test('Min date & Max date', async () => { /> ) - const columns = container.querySelectorAll('.nut-pickerview-list')[0] - const lists = columns.querySelectorAll('.nut-pickerview-roller-item-tiled') + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[0].querySelectorAll('.nut-pickerview-roller-item-tiled') expect(lists.length).toBe(3) rerender( { /> ) - const columns = container.querySelectorAll('.nut-pickerview-list')[1] - const lists = columns.querySelectorAll('.nut-pickerview-roller-item') + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[1].querySelectorAll('.nut-pickerview-roller-item') expect(lists.length).toBe(12) }) @@ -185,7 +185,7 @@ test('Filter Time', async () => { /> ) - const columns = container.querySelectorAll('.nut-pickerview-list')[3] - const lists = columns.querySelectorAll('.nut-pickerview-roller-item') + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[3].querySelectorAll('.nut-pickerview-roller-item') expect(lists.length).toBe(4) }) diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 27a30826fb..3a6719d316 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -6,7 +6,11 @@ import { usePropsValue } from '@/hooks/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' import { isDate } from '@/utils/is-date' import { padZero } from '@/utils/pad-zero' -import { PickerOptionItem, PickerValue } from '@/packages/pickerview/index.taro' +import { + PickerOption, + PickerOptions, + PickerValue, +} from '@/packages/pickerview/types' export interface DatePickerProps extends BasicComponent { value?: Date @@ -39,16 +43,16 @@ export interface DatePickerProps extends BasicComponent { | 'onChange' > > - formatter: (type: string, option: PickerOptionItem) => PickerOptionItem - filter: (type: string, option: PickerOptionItem[]) => PickerOptionItem[] + formatter: (type: string, option: PickerOption) => PickerOption + filter: (type: string, options: PickerOptions) => PickerOptions onClose: () => void onCancel: () => void onConfirm: ( - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, selectedValue: PickerValue[] ) => void onChange?: ( - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, selectedValue: PickerValue[], columnIndex: number ) => void @@ -106,7 +110,7 @@ export const DatePicker: FunctionComponent< seconds: lang.seconds, } const [pickerValue, setPickerValue] = useState([]) - const [pickerOptions, setPickerOptions] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) const formatValue = (value: Date | null) => { if (!value || (value && !isDate(value))) { value = startDate @@ -219,7 +223,7 @@ export const DatePicker: FunctionComponent< const compareDateChange = ( currentDate: number, newDate: Date | null, - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, index: number ) => { const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() @@ -239,7 +243,7 @@ export const DatePicker: FunctionComponent< } } const handlePickerChange = ( - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, selectedValue: PickerValue[], index: number ) => { @@ -331,7 +335,7 @@ export const DatePicker: FunctionComponent< columnIndex: number ) => { let cmin = min - const arr: Array = [] + const arr: Array = [] let index = 0 while (cmin <= max) { arr.push(formatOption(type, cmin)) @@ -414,7 +418,7 @@ export const DatePicker: FunctionComponent< onCancel={onCancel} value={pickerValue} onConfirm={( - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, selectedValue: PickerValue[] ) => onConfirm && onConfirm(selectedOptions, selectedValue)} onChange={({ value, index, selectedOptions }) => { diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 826ba57b7c..91f53c4780 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -1,12 +1,15 @@ import React, { FunctionComponent, useState, useEffect } from 'react' -import Picker from '@/packages/picker' -import { PickerProps } from '@/packages/picker/index' +import Picker, { PickerProps } from '@/packages/picker/index' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/hooks/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' import { isDate } from '@/utils/is-date' import { padZero } from '@/utils/pad-zero' -import { PickerOptionItem, PickerValue } from '@/packages/pickerview/index' +import { + PickerOption, + PickerOptions, + PickerValue, +} from '@/packages/pickerview/types' export interface DatePickerProps extends BasicComponent { value?: Date @@ -39,16 +42,16 @@ export interface DatePickerProps extends BasicComponent { | 'onChange' > > - formatter: (type: string, option: PickerOptionItem) => PickerOptionItem - filter: (type: string, option: PickerOptionItem[]) => PickerOptionItem[] + formatter: (type: string, option: PickerOption) => PickerOption + filter: (type: string, options: PickerOptions) => PickerOptions onClose: () => void onCancel: () => void onConfirm: ( - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, selectedValue: PickerValue[] ) => void onChange?: ( - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, selectedValue: PickerValue[], columnIndex: number ) => void @@ -106,7 +109,7 @@ export const DatePicker: FunctionComponent< seconds: lang.seconds, } const [pickerValue, setPickerValue] = useState([]) - const [pickerOptions, setPickerOptions] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) const formatValue = (value: Date | null) => { if (!value || (value && !isDate(value))) { value = startDate @@ -219,7 +222,7 @@ export const DatePicker: FunctionComponent< const compareDateChange = ( currentDate: number, newDate: Date | null, - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, index: number ) => { const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() @@ -239,7 +242,7 @@ export const DatePicker: FunctionComponent< } } const handlePickerChange = ( - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, selectedValue: PickerValue[], index: number ) => { @@ -331,7 +334,7 @@ export const DatePicker: FunctionComponent< columnIndex: number ) => { let cmin = min - const arr: Array = [] + const arr: Array = [] let index = 0 while (cmin <= max) { arr.push(formatOption(type, cmin)) @@ -409,7 +412,7 @@ export const DatePicker: FunctionComponent< onCancel={onCancel} value={pickerValue} onConfirm={( - selectedOptions: PickerOptionItem[], + selectedOptions: PickerOptions, selectedValue: PickerValue[] ) => onConfirm && onConfirm(selectedOptions, selectedValue)} onChange={({ value, index, selectedOptions }) => { diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index 154dbe8a0a..769885abf8 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -3,7 +3,7 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOptions, } from '@nutui/nutui-react' const Demo1 = () => { @@ -17,10 +17,10 @@ const Demo1 = () => { const [value, setValue] = useState('2023/01/01') const [show2, setShow2] = useState(false) const [desc2, setDesc2] = useState('') - const confirm1 = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm1 = (values: PickerValue[], options: PickerOptions) => { setDesc1(options.map((option) => option.label).join(' ')) } - const change = (options: PickerOptionItem[], values: PickerValue[]) => { + const change = (options: PickerOptions, values: PickerValue[]) => { const v = values.join('/') setValue(v) diff --git a/src/packages/datepicker/demos/h5/demo2.tsx b/src/packages/datepicker/demos/h5/demo2.tsx index 03641ee437..d1d4975610 100644 --- a/src/packages/datepicker/demos/h5/demo2.tsx +++ b/src/packages/datepicker/demos/h5/demo2.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { DatePicker, Cell, - PickerOptionItem, + PickerOptions, PickerValue, } from '@nutui/nutui-react' @@ -13,7 +13,7 @@ const Demo2 = () => { `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` ) - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { console.log('values', values, options) setDesc(options.map((option) => option.label).join('-')) } diff --git a/src/packages/datepicker/demos/h5/demo3.tsx b/src/packages/datepicker/demos/h5/demo3.tsx index 37f6f241cb..bf0cce9a2e 100644 --- a/src/packages/datepicker/demos/h5/demo3.tsx +++ b/src/packages/datepicker/demos/h5/demo3.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { DatePicker, Cell, - PickerOptionItem, + PickerOptions, PickerValue, } from '@nutui/nutui-react' @@ -16,7 +16,7 @@ const Demo3 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 11:08`) - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { const date = values.slice(0, 3).join('-') const time = values.slice(3).join(':') setDesc(`${date} ${time}`) diff --git a/src/packages/datepicker/demos/h5/demo4.tsx b/src/packages/datepicker/demos/h5/demo4.tsx index 67b8494727..7e01fa6996 100644 --- a/src/packages/datepicker/demos/h5/demo4.tsx +++ b/src/packages/datepicker/demos/h5/demo4.tsx @@ -3,7 +3,7 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOptions, } from '@nutui/nutui-react' const Demo4 = () => { @@ -15,7 +15,7 @@ const Demo4 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { setDesc(options.map((option) => option.label).join(':')) } diff --git a/src/packages/datepicker/demos/h5/demo5.tsx b/src/packages/datepicker/demos/h5/demo5.tsx index e2a7a3414e..e396a55a9f 100644 --- a/src/packages/datepicker/demos/h5/demo5.tsx +++ b/src/packages/datepicker/demos/h5/demo5.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { DatePicker, Cell, - PickerOptionItem, + PickerOptions, PickerValue, } from '@nutui/nutui-react' @@ -15,7 +15,7 @@ const Demo5 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10') - const confirm8 = (options: PickerOptionItem[], values: PickerValue[]) => { + const confirm8 = (options: PickerOptions, values: PickerValue[]) => { setDesc(options.map((option) => option.label).join(':')) } diff --git a/src/packages/datepicker/demos/h5/demo6.tsx b/src/packages/datepicker/demos/h5/demo6.tsx index e3e9d9bf20..21350012de 100644 --- a/src/packages/datepicker/demos/h5/demo6.tsx +++ b/src/packages/datepicker/demos/h5/demo6.tsx @@ -3,7 +3,8 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOption, + PickerOptions, } from '@nutui/nutui-react' const Demo6 = () => { @@ -14,7 +15,7 @@ const Demo6 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 10:10`) - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { const date = options .slice(1, 3) .map((op) => op.label) @@ -25,7 +26,7 @@ const Demo6 = () => { .join(':') setDesc(`${options[0].label}年${date} ${time}`) } - const formatter = (type: string, option: PickerOptionItem) => { + const formatter = (type: string, option: PickerOption) => { switch (type) { case 'year': option.label += '' diff --git a/src/packages/datepicker/demos/h5/demo7.tsx b/src/packages/datepicker/demos/h5/demo7.tsx index afd222d8ae..ba3b8f9366 100644 --- a/src/packages/datepicker/demos/h5/demo7.tsx +++ b/src/packages/datepicker/demos/h5/demo7.tsx @@ -3,7 +3,7 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOptions, } from '@nutui/nutui-react' const Demo7 = () => { @@ -16,7 +16,7 @@ const Demo7 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm6 = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm6 = (values: PickerValue[], options: PickerOptions) => { setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/h5/demo8.tsx b/src/packages/datepicker/demos/h5/demo8.tsx index 709cea3193..79886c8d19 100644 --- a/src/packages/datepicker/demos/h5/demo8.tsx +++ b/src/packages/datepicker/demos/h5/demo8.tsx @@ -3,7 +3,8 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOption, + PickerOptions, } from '@nutui/nutui-react' const Demo8 = () => { @@ -16,16 +17,16 @@ const Demo8 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 00`) - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { setDesc(options.map((option) => option.label).join(' ')) } - const filter = (type: string, options: PickerOptionItem[]) => { + const filter = (type: string, options: PickerOptions) => { if (type === 'hour') { return options.filter((option) => Number(option.value) % 6 === 0) } return options } - const formatter1 = (type: string, option: PickerOptionItem) => { + const formatter1 = (type: string, option: PickerOption) => { switch (type) { case 'year': option.label += `年` diff --git a/src/packages/datepicker/demos/taro/demo1.tsx b/src/packages/datepicker/demos/taro/demo1.tsx index 23ff5f1c36..1a9c273446 100644 --- a/src/packages/datepicker/demos/taro/demo1.tsx +++ b/src/packages/datepicker/demos/taro/demo1.tsx @@ -3,7 +3,7 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOptions, } from '@nutui/nutui-react-taro' const Demo1 = () => { @@ -17,10 +17,10 @@ const Demo1 = () => { const [value, setValue] = useState('2023/01/01') const [show2, setShow2] = useState(false) const [desc2, setDesc2] = useState('') - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { setDesc1(options.map((option) => option.label).join(' ')) } - const change = (options: PickerOptionItem[], values: PickerValue[]) => { + const change = (options: PickerOptions, values: PickerValue[]) => { const v = values.join('/') setValue(v) diff --git a/src/packages/datepicker/demos/taro/demo2.tsx b/src/packages/datepicker/demos/taro/demo2.tsx index 52b47fd8d3..9e57427a40 100644 --- a/src/packages/datepicker/demos/taro/demo2.tsx +++ b/src/packages/datepicker/demos/taro/demo2.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { DatePicker, Cell, - PickerOptionItem, + PickerOptions, PickerValue, } from '@nutui/nutui-react-taro' @@ -12,7 +12,7 @@ const Demo2 = () => { const [desc, setDesc] = useState( `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` ) - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { setDesc(options.map((option) => option.label).join('-')) } return ( diff --git a/src/packages/datepicker/demos/taro/demo3.tsx b/src/packages/datepicker/demos/taro/demo3.tsx index 2d1c33e401..b4dedc010f 100644 --- a/src/packages/datepicker/demos/taro/demo3.tsx +++ b/src/packages/datepicker/demos/taro/demo3.tsx @@ -3,7 +3,7 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOptions, } from '@nutui/nutui-react-taro' const Demo3 = () => { @@ -16,7 +16,7 @@ const Demo3 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 11:08`) - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { const date = values.slice(0, 3).join('-') const time = values.slice(3).join(':') setDesc(`${date} ${time}`) diff --git a/src/packages/datepicker/demos/taro/demo4.tsx b/src/packages/datepicker/demos/taro/demo4.tsx index 69de439a51..00e85593b7 100644 --- a/src/packages/datepicker/demos/taro/demo4.tsx +++ b/src/packages/datepicker/demos/taro/demo4.tsx @@ -3,7 +3,7 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOptions, } from '@nutui/nutui-react-taro' const Demo4 = () => { @@ -15,7 +15,7 @@ const Demo4 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { setDesc(options.map((option) => option.label).join(':')) } diff --git a/src/packages/datepicker/demos/taro/demo5.tsx b/src/packages/datepicker/demos/taro/demo5.tsx index 2ee4f868cd..0386bc9c61 100644 --- a/src/packages/datepicker/demos/taro/demo5.tsx +++ b/src/packages/datepicker/demos/taro/demo5.tsx @@ -3,7 +3,7 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOptions, } from '@nutui/nutui-react-taro' const Demo5 = () => { @@ -15,7 +15,7 @@ const Demo5 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10') - const confirm = (options: PickerOptionItem[], values: PickerValue[]) => { + const confirm = (options: PickerOptions, values: PickerValue[]) => { setDesc(options.map((option) => option.label).join(':')) } diff --git a/src/packages/datepicker/demos/taro/demo6.tsx b/src/packages/datepicker/demos/taro/demo6.tsx index 7314893062..9aa1371946 100644 --- a/src/packages/datepicker/demos/taro/demo6.tsx +++ b/src/packages/datepicker/demos/taro/demo6.tsx @@ -3,7 +3,8 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOption, + PickerOptions, } from '@nutui/nutui-react-taro' const Demo6 = () => { @@ -14,7 +15,7 @@ const Demo6 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 10:10`) - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { const date = options .slice(1, 3) .map((op) => op.label) @@ -25,7 +26,7 @@ const Demo6 = () => { .join(':') setDesc(`${options[0].label}年${date} ${time}`) } - const formatter = (type: string, option: PickerOptionItem) => { + const formatter = (type: string, option: PickerOption) => { switch (type) { case 'year': option.label += '' diff --git a/src/packages/datepicker/demos/taro/demo7.tsx b/src/packages/datepicker/demos/taro/demo7.tsx index 8934627df5..6f7210ac00 100644 --- a/src/packages/datepicker/demos/taro/demo7.tsx +++ b/src/packages/datepicker/demos/taro/demo7.tsx @@ -3,7 +3,7 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOptions, } from '@nutui/nutui-react-taro' const Demo7 = () => { @@ -16,7 +16,7 @@ const Demo7 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/taro/demo8.tsx b/src/packages/datepicker/demos/taro/demo8.tsx index d1e1d69be8..42441084f5 100644 --- a/src/packages/datepicker/demos/taro/demo8.tsx +++ b/src/packages/datepicker/demos/taro/demo8.tsx @@ -3,7 +3,8 @@ import { DatePicker, Cell, PickerValue, - PickerOptionItem, + PickerOption, + PickerOptions, } from '@nutui/nutui-react-taro' const Demo8 = () => { @@ -16,16 +17,16 @@ const Demo8 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 00`) - const confirm = (values: PickerValue[], options: PickerOptionItem[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { setDesc(options.map((option) => option.label).join(' ')) } - const filter = (type: string, options: PickerOptionItem[]) => { + const filter = (type: string, options: PickerOptions) => { if (type === 'hour') { return options.filter((option) => Number(option.value) % 6 === 0) } return options } - const formatter = (type: string, option: PickerOptionItem) => { + const formatter = (type: string, option: PickerOption) => { switch (type) { case 'year': option.label += `年` diff --git a/src/packages/picker/demos/h5/demo1.tsx b/src/packages/picker/demos/h5/demo1.tsx index b1d08573ab..480bf28554 100644 --- a/src/packages/picker/demos/h5/demo1.tsx +++ b/src/packages/picker/demos/h5/demo1.tsx @@ -5,7 +5,7 @@ import { PickerOptions, PickerValue, PickerOnChangeCallbackParameter, - PickerOptionItem, + PickerOption, } from '@nutui/nutui-react' const Demo1 = () => { @@ -37,7 +37,7 @@ const Demo1 = () => { ) => { console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - selectedOptions.forEach((option: PickerOptionItem) => { + selectedOptions.forEach((option: PickerOption) => { description += ` ${option.label}` }) setBaseDesc(description) diff --git a/src/packages/picker/demos/h5/demo6.tsx b/src/packages/picker/demos/h5/demo6.tsx index 92a4677cf4..f39d06544a 100644 --- a/src/packages/picker/demos/h5/demo6.tsx +++ b/src/packages/picker/demos/h5/demo6.tsx @@ -1,5 +1,11 @@ import React, { useState } from 'react' -import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOption, +} from '@nutui/nutui-react' const Demo6 = () => { const [isVisible, setIsVisible] = useState(false) @@ -71,7 +77,9 @@ const Demo6 = () => { selectedValue: PickerValue[] ) => { console.log('onconfirm', selectedOptions, selectedValue) - const city = selectedOptions.map((item) => item.label).join('-') + const city = selectedOptions + .map((item: PickerOption) => item.label) + .join('-') setCityCustom(city) setValue(selectedValue) } diff --git a/src/packages/picker/demos/h5/demo7.tsx b/src/packages/picker/demos/h5/demo7.tsx index 2d64d3b0af..553359db48 100644 --- a/src/packages/picker/demos/h5/demo7.tsx +++ b/src/packages/picker/demos/h5/demo7.tsx @@ -5,6 +5,7 @@ import { PickerOptions, PickerValue, PickerOnChangeCallbackParameter, + PickerOption, } from '@nutui/nutui-react' const Demo7 = () => { @@ -89,7 +90,9 @@ const Demo7 = () => { selectedValue: PickerValue[] ) => { console.log('onconfirm', selectedOptions, selectedValue) - const city = selectedOptions.map((item) => item.label).join('-') + const city = selectedOptions + .map((item: PickerOption) => item.label) + .join('-') setasyncDesc(city) setValue(selectedValue) } diff --git a/src/packages/picker/demos/h5/demo8.tsx b/src/packages/picker/demos/h5/demo8.tsx index c96d49c3a4..4f16c7e357 100644 --- a/src/packages/picker/demos/h5/demo8.tsx +++ b/src/packages/picker/demos/h5/demo8.tsx @@ -3,7 +3,7 @@ import { Picker, Cell, ConfigProvider, - PickerOptionItem, + PickerOption, PickerOptions, PickerValue, } from '@nutui/nutui-react' @@ -31,7 +31,7 @@ const Demo8 = () => { ) => { console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - selectedOptions.forEach((option: PickerOptionItem) => { + selectedOptions.forEach((option: PickerOption) => { description += ` ${option.label}` }) setBaseDesc(description) diff --git a/src/packages/picker/demos/taro/demo1.tsx b/src/packages/picker/demos/taro/demo1.tsx index 88678f7c5e..94bbae0cdf 100644 --- a/src/packages/picker/demos/taro/demo1.tsx +++ b/src/packages/picker/demos/taro/demo1.tsx @@ -5,7 +5,7 @@ import { PickerOptions, PickerValue, PickerOnChangeCallbackParameter, - PickerOptionItem, + PickerOption, } from '@nutui/nutui-react-taro' const Demo1 = () => { @@ -37,7 +37,7 @@ const Demo1 = () => { ) => { console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - selectedOptions.forEach((option: PickerOptionItem) => { + selectedOptions.forEach((option: PickerOption) => { description += ` ${option.label}` }) setBaseDesc(description) diff --git a/src/packages/picker/demos/taro/demo6.tsx b/src/packages/picker/demos/taro/demo6.tsx index 1a0d29aae4..64f6bf4ba9 100644 --- a/src/packages/picker/demos/taro/demo6.tsx +++ b/src/packages/picker/demos/taro/demo6.tsx @@ -4,6 +4,7 @@ import { Cell, PickerOptions, PickerValue, + PickerOption, } from '@nutui/nutui-react-taro' const Demo6 = () => { @@ -76,7 +77,9 @@ const Demo6 = () => { selectedValue: PickerValue[] ) => { console.log('onconfirm', selectedOptions, selectedValue) - const city = selectedOptions.map((item) => item.label).join('-') + const city = selectedOptions + .map((item: PickerOption) => item.label) + .join('-') setCityCustom(city) setValue(selectedValue) } diff --git a/src/packages/picker/demos/taro/demo7.tsx b/src/packages/picker/demos/taro/demo7.tsx index f217879e9d..2071f0010f 100644 --- a/src/packages/picker/demos/taro/demo7.tsx +++ b/src/packages/picker/demos/taro/demo7.tsx @@ -5,6 +5,7 @@ import { PickerOptions, PickerValue, PickerOnChangeCallbackParameter, + PickerOption, } from '@nutui/nutui-react-taro' const Demo7 = () => { @@ -89,7 +90,9 @@ const Demo7 = () => { selectedValue: PickerValue[] ) => { console.log('onconfirm', selectedOptions, selectedValue) - const city = selectedOptions.map((item) => item.label).join('-') + const city = selectedOptions + .map((item: PickerOption) => item.label) + .join('-') setasyncDesc(city) setValue(selectedValue) } diff --git a/src/packages/picker/demos/taro/demo8.tsx b/src/packages/picker/demos/taro/demo8.tsx index b61a9b599b..5cb9b00514 100644 --- a/src/packages/picker/demos/taro/demo8.tsx +++ b/src/packages/picker/demos/taro/demo8.tsx @@ -3,7 +3,7 @@ import { Picker, Cell, ConfigProvider, - PickerOptionItem, + PickerOption, PickerOptions, PickerValue, } from '@nutui/nutui-react-taro' @@ -31,7 +31,7 @@ const Demo8 = () => { ) => { console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - selectedOptions.forEach((option: PickerOptionItem) => { + selectedOptions.forEach((option: PickerOption) => { description += ` ${option.label}` }) setBaseDesc(description) diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 9190e7620b..eb1e19b159 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -11,7 +11,7 @@ import isEqual from 'react-fast-compare' import { PickerOptions, PickerValue, - PickerOptionItem, + PickerOption, PickerOnChangeCallbackParameter, } from '@/packages/pickerview/types' import PickerView from '@/packages/pickerview/index.taro' @@ -32,7 +32,7 @@ export interface PickerProps extends Omit { threeDimensional?: boolean duration: number | string closeOnOverlayClick: boolean - renderLabel?: (item: PickerOptionItem) => React.ReactNode + renderLabel?: (item: PickerOption) => React.ReactNode popupProps: Partial< Omit @@ -115,7 +115,7 @@ const InternalPicker: ForwardRefRenderFunction< const [innerValue, setInnerValue] = useState([...selectedValue]) const innerValueRef = useRef(innerValue) const [innerOptions, setInnerOptions] = useState([]) - const selectedOptionsRef = useRef([] as PickerOptionItem[]) + const selectedOptionsRef = useRef([] as PickerOptions) const [refs, setRefs] = useRefs() useEffect(() => { diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 6e4f7076b1..9a932aa875 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -10,7 +10,7 @@ import isEqual from 'react-fast-compare' import { PickerOptions, PickerValue, - PickerOptionItem, + PickerOption, PickerOnChangeCallbackParameter, } from '@/packages/pickerview/types' import PickerView from '@/packages/pickerview/index' @@ -31,7 +31,7 @@ export interface PickerProps extends Omit { threeDimensional?: boolean duration: number | string closeOnOverlayClick: boolean - renderLabel?: (item: PickerOptionItem) => React.ReactNode + renderLabel?: (item: PickerOption) => React.ReactNode popupProps: Partial< Omit @@ -114,7 +114,7 @@ const InternalPicker: ForwardRefRenderFunction< const [innerValue, setInnerValue] = useState([...selectedValue]) const innerValueRef = useRef(innerValue) const [innerOptions, setInnerOptions] = useState([]) - const selectedOptionsRef = useRef([] as PickerOptionItem[]) + const selectedOptionsRef = useRef([] as PickerOptions) const [refs, setRefs] = useRefs() useEffect(() => { diff --git a/src/packages/pickerview/__test__/pickerview.spec.tsx b/src/packages/pickerview/__test__/pickerview.spec.tsx index 5dfd175869..6f67fe5c6c 100644 --- a/src/packages/pickerview/__test__/pickerview.spec.tsx +++ b/src/packages/pickerview/__test__/pickerview.spec.tsx @@ -133,7 +133,7 @@ test('should render with Multi Column', () => { test('should match onchange', async () => { const PenderContent = () => { - const [value, setValue] = useState([]) + const [value, setValue] = useState([] as number[]) const [options, setInnerOptions] = useState([]) useEffect(() => { diff --git a/src/packages/pickerview/doc.en-US.md b/src/packages/pickerview/doc.en-US.md index 42006732bd..0193489639 100644 --- a/src/packages/pickerview/doc.en-US.md +++ b/src/packages/pickerview/doc.en-US.md @@ -71,13 +71,13 @@ import { PickerView } from '@nutui/nutui-react' | duration | The duration of inertial rolling during rapid sliding, in ms | `string` \| `number` | `1000` | | onChange | Called when the value of each column changes | `({value, index, selectedOptions}) => void` | `-` | -### PickerOptionItem +### PickerOption | Property | Description | Type | Default | | --- | --- | --- | --- | | label | Text of column | `string` \| `number` | `-` | | value | Value of column | `string` \| `number` | `-` | -| children | Cascader Option | `PickerOptionItem[]` | `-` | +| children | Cascader Option | `PickerOptions` | `-` | ## Theming diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index ef3cb7dc18..378d917da3 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -71,13 +71,13 @@ import { PickerView } from '@nutui/nutui-react' | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | -### PickerOptionItem +### PickerOption | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | label | 选项的文字内容 | `string` \| `number` | `-` | | value | 选项对应的值,且唯一 | `string` \| `number` | `-` | -| children | 用于级联选项 | `PickerOptionItem[]` | `-` | +| children | 用于级联选项 | `PickerOptions` | `-` | ## 主题定制 diff --git a/src/packages/pickerview/doc.taro.md b/src/packages/pickerview/doc.taro.md index 344fc9dee3..cc0bcb4e1d 100644 --- a/src/packages/pickerview/doc.taro.md +++ b/src/packages/pickerview/doc.taro.md @@ -71,13 +71,13 @@ import { PickerView } from '@nutui/nutui-react-taro' | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | -### PickerOptionItem +### PickerOption | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | label | 选项的文字内容 | `string` \| `number` | `-` | | value | 选项对应的值,且唯一 | `string` \| `number` | `-` | -| children | 用于级联选项 | `PickerOptionItem[]` | `-` | +| children | 用于级联选项 | `PickerOptions` | `-` | ## 主题定制 diff --git a/src/packages/pickerview/doc.zh-TW.md b/src/packages/pickerview/doc.zh-TW.md index 073f5b8e4f..cfe5e7360e 100644 --- a/src/packages/pickerview/doc.zh-TW.md +++ b/src/packages/pickerview/doc.zh-TW.md @@ -71,13 +71,13 @@ import { PickerView } from '@nutui/nutui-react-taro' | duration | 快速滑動時慣性滾動的時長,單位 ms | `string` \| `number` | `1000` | | onChange | 每一列值變更時調用 | `({value, index, selectedOptions}) => void` | `-` | -### PickerOptionItem +### PickerOption | 屬性 | 說明 | 類型 | 默認值 | | --- | --- | --- | --- | | label | 選項的文字內容 | `string` \| `number` | `-` | | value | 選項對應的值,且唯一 | `string` \| `number` | `-` | -| children | 用於級聯選項 | `PickerOptionItem[]` | `-` | +| children | 用於級聯選項 | `PickerOptions` | `-` | ## 主題定制 diff --git a/src/packages/pickerview/index.taro.ts b/src/packages/pickerview/index.taro.ts index 1bd5f2a397..8d492244e8 100644 --- a/src/packages/pickerview/index.taro.ts +++ b/src/packages/pickerview/index.taro.ts @@ -2,7 +2,7 @@ import PickerView from './pickerview.taro' export type { PickerViewProps, - PickerOptionItem, + PickerOption, PickerRollerProps, PickerValue, PickerOptions, diff --git a/src/packages/pickerview/index.ts b/src/packages/pickerview/index.ts index 15c781a272..bff2bf6714 100644 --- a/src/packages/pickerview/index.ts +++ b/src/packages/pickerview/index.ts @@ -2,7 +2,7 @@ import PickerView from './pickerview' export type { PickerViewProps, - PickerOptionItem, + PickerOption, PickerRollerProps, PickerValue, PickerOptions, diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 10c38d145f..2acf57c520 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -8,7 +8,7 @@ import React, { import { View } from '@tarojs/components' import { useTouch } from '@/hooks/use-touch' import { passiveSupported } from '@/utils/supports-passive' -import { PickerRollerProps, PickerOptionItem } from './types' +import { PickerRollerProps, PickerOption } from './types' import { web } from '@/utils/platform-taro' import { preventDefault } from '@/utils' import { momentum, useStyles } from './utils' @@ -23,7 +23,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< threeDimensional = true, duration = 1000, onSelect, - renderLabel = (item: PickerOptionItem) => item.label, + renderLabel = (item: PickerOption) => item.label, } = props const DEFAULT_DURATION = 200 @@ -132,7 +132,9 @@ const InternalPickerRoller: ForwardRefRenderFunction< const updateStatus = (shouldSelect?: boolean, value?: string | number) => { const selectedValue = value || props.value - const index = options.findIndex((item) => item.value === selectedValue) + const index = options.findIndex( + (item: PickerOption) => item.value === selectedValue + ) setCurrentIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current shouldSelect && selectValue(-move) @@ -223,7 +225,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< > {/* 3D 效果 */} {threeDimensional && - options.map((item, index) => ( + options.map((item: PickerOption, index: number) => ( { + options.map((item: PickerOption, index: number) => { return ( item.label, + renderLabel = (item: PickerOption) => item.label, } = props const DEFAULT_DURATION = 200 @@ -130,7 +130,9 @@ const InternalPickerRoller: ForwardRefRenderFunction< const updateStatus = (shouldSelect?: boolean, value?: string | number) => { const selectedValue = value || props.value - const index = options.findIndex((item) => item.value === selectedValue) + const index = options.findIndex( + (item: PickerOption) => item.value === selectedValue + ) setCurrentIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current shouldSelect && selectValue(-move) @@ -210,7 +212,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< > {/* 3D */} {threeDimensional && - options.map((item, index) => ( + options.map((item: PickerOption, index: number) => (
( + options.map((item: PickerOption, index: number) => (
item.label, + renderLabel: (item: PickerOption) => item.label, } as PickerViewProps const InternalPickerView: ForwardRefRenderFunction< @@ -77,7 +77,7 @@ const InternalPickerView: ForwardRefRenderFunction< if (!options.length) return [] // 如果 options 为空,直接返回空数组 const formatted: PickerOptions[] = [] - let columnOptions: PickerOptionItem = { + let columnOptions: PickerOption = { label: '', value: '', children: options, @@ -95,7 +95,7 @@ const InternalPickerView: ForwardRefRenderFunction< } else if (currentValue) { // 如果 currentValue 存在,查找匹配的项 const index = currentOptions.findIndex( - (columnItem) => columnItem.value === currentValue + (columnItem: PickerOption) => columnItem.value === currentValue ) columnOptions = currentOptions[index === -1 ? 0 : index] // 如果未找到,默认取第一个 } else { @@ -131,7 +131,7 @@ const InternalPickerView: ForwardRefRenderFunction< }, [selectedValue]) const handleSelect = useCallback( - (option: PickerOptionItem, index: number) => { + (option: PickerOption, index: number) => { const newValue = option?.value if (!newValue || innerValue[index] === newValue) return changeIndex.current = index @@ -159,7 +159,7 @@ const InternalPickerView: ForwardRefRenderFunction< ...values.splice(startIndex), ] setInnerValue([...combineResult]) - const optionFirst = props?.options?.[0] as PickerOptionItem[] + const optionFirst = props?.options?.[0] as PickerOptions if ( !isEqual( formatCascadeOptions(optionFirst, combineResult), @@ -177,12 +177,12 @@ const InternalPickerView: ForwardRefRenderFunction< return innerOptions .map((columnOptions, index) => { const selectedOption = columnOptions.find( - (item) => item.value === innerValue[index] + (item: PickerOption) => item.value === innerValue[index] ) return selectedOption // return selectedOption || columnOptions[0] }) - .filter(Boolean) as PickerOptionItem[] + .filter(Boolean) as PickerOptions }, [innerOptions, innerValue]) useEffect(() => { diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index 0fef411bea..fbf2189c52 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -12,7 +12,7 @@ import { ComponentDefaults } from '@/utils/typings' import { usePropsValue } from '@/hooks/use-props-value' import { PickerViewProps, - PickerOptionItem, + PickerOption, PickerValue, PickerOptions, } from './types' @@ -23,7 +23,7 @@ const defaultProps = { options: [], defaultValue: [], value: undefined, - renderLabel: (item: PickerOptionItem) => item.label, + renderLabel: (item: PickerOption) => item.label, } as PickerViewProps const InternalPickerView: ForwardRefRenderFunction< @@ -76,7 +76,7 @@ const InternalPickerView: ForwardRefRenderFunction< if (!options.length) return [] // 如果 options 为空,直接返回空数组 const formatted: PickerOptions[] = [] - let columnOptions: PickerOptionItem = { + let columnOptions: PickerOption = { label: '', value: '', children: options, @@ -94,7 +94,7 @@ const InternalPickerView: ForwardRefRenderFunction< } else if (currentValue) { // 如果 currentValue 存在,查找匹配的项 const index = currentOptions.findIndex( - (columnItem) => columnItem.value === currentValue + (columnItem: PickerOption) => columnItem.value === currentValue ) columnOptions = currentOptions[index === -1 ? 0 : index] // 如果未找到,默认取第一个 } else { @@ -130,7 +130,7 @@ const InternalPickerView: ForwardRefRenderFunction< }, [selectedValue]) const handleSelect = useCallback( - (option: PickerOptionItem, index: number) => { + (option: PickerOption, index: number) => { const newValue = option?.value if (!newValue || innerValue[index] === newValue) return changeIndex.current = index @@ -158,7 +158,7 @@ const InternalPickerView: ForwardRefRenderFunction< ...values.splice(startIndex), ] setInnerValue([...combineResult]) - const optionFirst = props?.options?.[0] as PickerOptionItem[] + const optionFirst = props?.options?.[0] as PickerOptions if ( !isEqual( formatCascadeOptions(optionFirst, combineResult), @@ -176,12 +176,12 @@ const InternalPickerView: ForwardRefRenderFunction< return innerOptions .map((columnOptions, index) => { const selectedOption = columnOptions.find( - (item) => item.value === innerValue[index] + (item: PickerOption) => item.value === innerValue[index] ) return selectedOption // return selectedOption || columnOptions[0] }) - .filter(Boolean) as PickerOptionItem[] + .filter(Boolean) as PickerOptions }, [innerOptions, innerValue]) useEffect(() => { diff --git a/src/packages/pickerview/types.ts b/src/packages/pickerview/types.ts index e25d83faf5..57efbd2b7e 100644 --- a/src/packages/pickerview/types.ts +++ b/src/packages/pickerview/types.ts @@ -2,28 +2,28 @@ import { BasicComponent } from '@/utils/typings' export type PickerValue = string | number | null -export interface PickerOptionItem { +export interface PickerOption { label: string | number value: string | number - children?: PickerOptionItem[] + children?: PickerOptions } -export type PickerOptions = PickerOptionItem[] +export type PickerOptions = PickerOption[] export interface PickerRollerProps { - options: PickerOptionItem[] + options: PickerOptions keyIndex: number value: PickerValue threeDimensional?: boolean duration?: number | string - onSelect: (option: PickerOptionItem, index: number) => void - renderLabel: (item: PickerOptionItem) => React.ReactNode + onSelect: (option: PickerOption, index: number) => void + renderLabel: (item: PickerOption) => React.ReactNode } export interface PickerOnChangeCallbackParameter { value: PickerValue[] index: number - selectedOptions: PickerOptionItem[] + selectedOptions: PickerOptions } export interface PickerViewProps extends BasicComponent { @@ -33,6 +33,6 @@ export interface PickerViewProps extends BasicComponent { defaultValue?: PickerValue[] threeDimensional?: boolean duration?: number | string - renderLabel: (item: PickerOptionItem) => React.ReactNode + renderLabel: (item: PickerOption) => React.ReactNode onChange?: (arg0: PickerOnChangeCallbackParameter) => void } From f81ef999671ea7214c9060e8a7e9387a1280061a Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 25 Feb 2025 14:45:38 +0800 Subject: [PATCH 44/54] =?UTF-8?q?fix:=20=E5=BC=B9=E7=AA=97=E5=8A=A8?= =?UTF-8?q?=E6=95=88=E8=BF=98=E5=8E=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/picker/picker.taro.tsx | 27 +++++++++++++-------------- src/packages/picker/picker.tsx | 27 +++++++++++++-------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index eb1e19b159..d9b96e3d2c 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -142,6 +142,7 @@ const InternalPicker: ForwardRefRenderFunction< value, index, }) + console.log('sss') } const onConfirmEvent = () => { @@ -216,20 +217,18 @@ const InternalPicker: ForwardRefRenderFunction< return ( <> {typeof children === 'function' && children(selectedValue)} - {innerVisible ? ( - { - if (!closeOnOverlayClick) return - onCancelEvent() - }} - > - {renderPickerElement()} - - - ) : null} + { + if (!closeOnOverlayClick) return + onCancelEvent() + }} + > + {innerVisible ? <>{renderPickerElement()} : null} + + ) } diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 9a932aa875..a2a965bfc6 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -214,20 +214,19 @@ const InternalPicker: ForwardRefRenderFunction< return ( <> {typeof children === 'function' && children(selectedValue)} - {innerVisible ? ( - { - if (!closeOnOverlayClick) return - onCancelEvent() - }} - > - {renderPickerElement()} - - - ) : null} + + { + if (!closeOnOverlayClick) return + onCancelEvent() + }} + > + {innerVisible ? <>{renderPickerElement()} : null} + + ) } From f27847978a5d313ca5f3b1dca185e825569a2e24 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 25 Feb 2025 15:22:54 +0800 Subject: [PATCH 45/54] fix: remove console --- src/packages/picker/picker.taro.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index d9b96e3d2c..cba096362d 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -142,7 +142,6 @@ const InternalPicker: ForwardRefRenderFunction< value, index, }) - console.log('sss') } const onConfirmEvent = () => { From 7b5fd5e58c5b7b0b289b1a29bd197f90909e61f5 Mon Sep 17 00:00:00 2001 From: oasis <12181600+oasis-cloud@users.noreply.github.com> Date: Thu, 27 Feb 2025 10:00:10 +0800 Subject: [PATCH 46/54] chore: update migrate (#3021) --- migrate-from-v2.md | 797 ++++--------------------------- src/packages/backtop/doc.taro.md | 2 +- 2 files changed, 105 insertions(+), 694 deletions(-) diff --git a/migrate-from-v2.md b/migrate-from-v2.md index c414b0a315..19cddb0c34 100644 --- a/migrate-from-v2.md +++ b/migrate-from-v2.md @@ -18,12 +18,9 @@ npm install @nutui/nutui-react-taro 3. 处理不兼容更新 -从 NutUI React 1.x 到 NutUI React 2.x 存在一些不兼容更新,需要仔细阅读不兼容更新内容,并依次处理。 - -你可以手动对照下面的列表逐条检查代码进行修改,另外,我们也提供了一个 codemod cli 工具 @nutui/nutui-react-codemod 以帮助你快速升级到 v2 版本。在运行 codemod cli 前,请先提交你的本地代码修改。 +从 NutUI React 2.x 到 NutUI React 3.x 存在一些不兼容更新,需要仔细阅读不兼容更新内容,并依次处理。 4. 主题变量更名: - 如,primary-color 更名为 color-primary;注意在使用自定义主题,特别是使用 ConfigProvider 组件的情况下,有没有使用 `nutuiBrandColor`,这时记得更名为 `nutuiColorPrimary` ## 兼容更新 @@ -43,6 +40,9 @@ plugins: [ libraryDirectory: "dist/esm", style: 'css', camel2DashComponentName: false, + "customName": (name, file) => { + return `@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}` + } }, "nutui-react", ] @@ -61,206 +61,67 @@ plugins: [ libraryDirectory: "dist/esm", style: 'css', camel2DashComponentName: false, + "customName": (name, file) => { + return `@nutui/nutui-react-taro/dist/es/packages/${name.toLowerCase()}` + } }, "nutui-react-taro", ] ] ``` -2. 更完善的类型导出以及对类型增加 `JSDoc` 注释 -3. 组件分类的调整 - 在组件分类上,我们从交互维度上,和交互设计侧共同对 1.x 分类进行了基于信息结构的评审,并进行了子类梳理,完成重新分类,目标是更贴合交互场景的分布,易于查找组件。主要分布在: +## 新增组件 -- 基础组件,将 `Popup` 组件移除,将 `Popup` 细分到操作反馈-引导提示部分; -- 布局组件,保持不变; -- 导航组件:将分页相关组件 `Pagination`、`Indicator` 移动到展示组件(考虑移动端的分页轻操作);`Menu` 菜单移动到数据录入类-选择器子类(考虑 `Menu` 主要是作为筛选器);将 `BackTop` 移至导航组件,作为锚点组件的一部分; -- 展示组件:将 `Badge`、`NoticeBar`、`Popover` 移至操作反馈-引导提示类,`Empty`、`Skeleton` 移至操作反馈-加载状态结果反馈中;`WaterMark`、`TrendArrow` 作为特性增强类组件放在特色组件中,待由该类组件的使用场景和范围确认是否变更分类;同时新增 `Audio`,将其同 `Video`、`ImagePreview`、`Swiper` 一同归为展示-多媒体类; -- 操作反馈类,新增 `Skeleton`、`Empty`(加载结果反馈类),`Popover`、`Notify`、`NoticeBar`、`Popup` (引导提示类)6个组件;同时去除 `BackTop`(导航组件-锚点类)、`Switch`(数据录入-选择器)、`Audio``(展示-多媒体);在此基础上,未来会考虑增加 ResultPage`,整合错误状态、空状态等反馈状态,该组件在考虑中;同时考虑增加加载状态 `Loading` 组件。版本待定。 -- 数据录入类,主要分为两大类-输入及选择器。在输入中增加 `Signature`,该组件在 `Form` 表单中的应用范围日渐广泛,从特色组件中移入到数据录入部分;选择器中增加 `Switch`、`Menu`,及 `Address`。其中 `Signature` 和 `Address` 都是考虑其常用性,从特色中迁移到数据录入部分。 -- 特色组件,保留 `Barrage`、`Card`、`TimeSelect`,新增 `WaterMark`、`TrendArrow`。 +- SideBar +- PickerView ## 不兼容更新 -## NutUI Icons 调整 - -1.x 版本我们在实际开发过程中会发现 `Button` 只是引用了一个很小的 Loading Icon,但是全量引用了 IconFont 字体 ,会导致开发者的项目文件增大。我们在 NutUI React 2.x 中为解决此问题,重新定义了 Icon 组件,将所有的 Icons 抽离成单独的图标组件库 @nutui/icons-react(Taro 适配下为 @nutui/icons-react-taro) ,使其可以进行按需加载使用。 因此一些组件之前关于 Icon 的相关 Props 将被移除,需要使用插槽或者传递一个 Component 组件的 Props 进行使用。 受影响的组件如下: - -- Avatar -- Button -- ImagePreview -- Collapse -- InfiniteLoading -- Popup -- Steps -- Switch -- Toast -- Progress -- NoticeBar -- SearchBar -- Navbar -- Menu -- Tabbar -- Checkbox -- InputNumber -- Input -- Radio -- Rate -- Uploader -- Popover -- Grid -- TrendArrow - -如果你的项目中使用了这些组件,请仔细阅读文档并进行升级。 - -## 组件名称调整 - -- GridItem -> Grid.Item -- TabbarItem -> Tabbar.Item -- CollapseItem -> Collapse.Item -- SwiperItem -> Swiper.Item -- CellGroup -> Cell.Group -- MenuItem -> Menu.Item -- Infiniteloading -> InfiniteLoading +如果你的项目中使用了这些组件,请仔细阅读文档并进行升级 ## 组件 API 调整 -在 2.0 版本中,我们重点对组件 API 进行了评审和修订,使属性和方法命名更贴合常用的命名习惯及 React 语言规范,目标希望开发者在使用组件时得心应手。我们的思路大体如下: - -### 属性定义 - -本次升级重点关注属性的命名方面,从 1.x 的 610 个属性精简为 410 个,更精简、更规范;同时增强属性的类型范围,提升自定义能力。 - -- 对同一属性进行统一描述,比如: - - 缩写类会改为全拼,如 `desc`、`descSlot`、`description` 统一为 `description` - - 能使用名词或形容词的优先使用该类词性,一个词能说明白的不用两个词。 - - 如 `wrap`、`wrapable` 统一为 `wrap` - - 如将 `isXxx` 统一为 `xxx`,如 `isVisible`、`isDeletable` 等,可直接使用 `visible`、`deletable` 等,形容词化 - - 如`showXxx` 尽量统一为 `xxx`,名词化。【部分属性待优化。】 - - 如 `roundRadius` 改为 `radius` ,`columnNum` 改为 `columns`等 - - `onClickXxx` 统一为 `onXxxClick` - - `modelValue` 统一为 `value`,并增加支持 `defaultValue`,支持受控与非受控模式 - - 对于标识位置、对齐等类的属性,将属性名变更为其上一层的属性定义,如 `center`会改为 `align`、`vertical`,改为 `direction`,像标记距离的,如 `top`、`bottom`、`distance` 等,会改为 `threshold` - - 不规范的定义如 `okBtn`、`okText` 这种,会改为 `confirmXxx` -- 扩充属性的类型。如 `title` 的类型从 `string` 扩充为 `React.ReactNode`,增强自定义内容;其中有涉及合并属性的,统一用最简命名来定义属性;如 `title`、`titleSlot` 合并为 `title`,再扩充属性类型。 -- 对于 `xxClass`、`xxStyle`类的属性,移除,可使用 `className` 、`style` 来实现。 -- 移除与样式有关的属性,除基础组件的样式属性及部分实现起来较为复杂的样式属性外,大多数样式属性被移除,可通过样式变量来实现。 -- 将普遍认同可内置的属性或不怎么使用的属性,直接内置,去掉属性设置。 - -### 组件实现 - ### 基础组件 #### Button -- 去掉一些样式变量。如 `$button-default-font-weight` `$button-large-font-weight` 等。 - -#### Cell - -- `subTitle` 重命名为 `description`,类型修改为 `React.Node` -- `desc` 重命名为 `extra`,类型修改为 `React.Node` -- `roundRadius` 重命名为 `radius` -- `center` 重命名为 `align`,默认值修改为`flex-start`,可选值为 `flex-start`、`center`、`flex-end` -- 移除 `icon`、 `isLink`、`url`、`linkSlot`、`replace`、`descTextAlign`,通过用户自定义节点实现,参考文档demo示例 - -#### CellGroup - -- 新增 `divider`,单元格之间是否有分割线 -- `desc` 重命名为 `description` -- `title`、`description` 类型修改为 `React.Node` -- 移除 `titleSlot` 和 `descSlot`,通过 `title`、`description` 实现 -- - -#### ConfigProvider - -#### Icon - -#### Image - -- 移除 `round`,通过 `radius` 实现圆或圆角 -- 移除 `loadingImg` 和 `slotLoading`,通过 `loading` 属性实现,当 `loading` 属性设置为 `ReactNode` 或 `true` 时,表示展示 `loading` 状态 -- 移除 `showError` 和 `slotError`,通过 `error` 属性实现,当 `error` 属性设置为 `ReactNode` 或 `true` 时,表示展示 `error` 状态 -- 移除 `loadingImg`,可通过 `loading` 设置 `ReactNode` -- 移除 `errorImg`,可通过 `error` 设置 `ReactNode` -- `showError` 重命名为 `error`,类型修改为 `boolean|ReactNode` -- `showLoading` 重命名为 `loading`,类型修改为 `boolean|ReactNode` +- 移除样式变量 `--nutui-button-default-font-weight` -#### Overlay - -- `overlayClass` 重命名为 `className` -- `overlayStyle` 重命名为 `style` -- `closeOnClickOverlay` 重命名为 `closeOnOverlayClick` -- 更改 `lockScroll` 默认值为 `true` -- 新增 `afterClose` 和 `afterShow`,用于完全关闭后触发的回调和完全展示后触发的回调 - -#### Popup - -- `popClass` 重命名为 `className`,统一将组件的样式类名使用 `className`,不再指定特殊名字,减轻用户使用的记忆成本 -- `overlayClass` 重命名为 `overlayClassName`,继承自`Overlay` -- `closeOnClickOverlay` 重命名为 `closeOnOverlayClick` -- `onOpened` 和 `onClosed` 重命名为 `afterShow` 和 `afterClose`,继承自`Overlay`,用于完全关闭后触发的回调和完全展示后触发的回调 -- `destroyOnClose` 的描述进行了修订,改为:“组件不可见时,卸载内容”,并把其默认值改为了`false` -- `onClickCloseIcon` 和 `onClickOverlay` 两个方法,增加布尔判断,如返回false 或 未定义返回值时,将不再关闭 Popup;默认值为 `true`;在demo中已增加相应示例;同时,两者的名字变更为 `onCloseIconClick`、`onOverlayClick` -- `closeIcon` 类型从 `string` 改为 `ReactNode`,以前的 `closeIcon='mask-close'` 需改为 `closeIcon={}` -- `onOverlayClick` 和 `onCloseIconClick` 不会自动触发 `onClose` 了,如需触发关闭事件,需主动调用 `onClose` 回调函数 -- 新增 `description` 属性,支持标题下展示描述内容。 -- 调整 `position` 为 `bottom` 时的默认样式,默认支持圆角,此刻不需要再设置 `round` 属性。 +[//]: # '#### Cell' +[//]: # '#### CellGroup' +[//]: # '#### ConfigProvider' +[//]: # '#### Icon' +[//]: # '#### Image' +[//]: # '#### Overlay' +[//]: # '#### Popup' ### 布局组件 -#### Divider - -- 移除 `dashed`, 通过 `style` 属性实现 -- 移除 `hairline`, 默认为 `true` -- CSS 变量调整:`$divider-before-margin-right`、`$divider-after-margin-left` 统一为 `$divider-spacing`,`$divider-vertical-border-left` 变更为 `$divider-border-color`,增加 `$divider-side-width`。 +[//]: # '#### Divider' #### Grid -- 移除 `fontSize`,可自行控制传入的组件字体大小 -- 移除 `border`,作为默认样式 -- `columnNum` 重命名为 `columns` -- `GridItem` 使用方式修改为 `Grid.Item` +- square 属性的默认值调整为 true +- 新增主题变量 + - `--nutui-grid-border-width` 控制边框宽度 + - `--nutui-grid-border-radius` 控制圆角大小 + - -#### Layout - -#### Sticky - -- 移除 `top` 和 `bottom`, 重命名为 `threshold` +[//]: # '#### Layout' +[//]: # '#### Sticky' ### 导航组件 -#### Elevator - -- `acceptKey` 重命名为 `floorKey` -- `indexList` 重命名为 `list` -- `isSticky` 重命名为 `sticky` -- `onClickIndex` 重命名为 `onIndexClick` -- `onClickItem` 重命名为 `onItemClick` -- 新增`showKeys`,是否展示右侧导航 -- CSS 变量部分,对命名做了简化。 - -#### FixedNav - -- `unActiveText` 重命名为 `inactiveText` -- `navList` 重命名为 `list` -- `slotBtn` 重命名为 `content` -- `onSelected` 重命名为 `onSelect` -- 移除 `fixednavClass`,通过 `className` 实现 -- 移除 `slotList`,通过 `children` 实现 -- 该组件已废弃 `BEM` 规范,记得把 `__` 改为 `-` +[//]: # '#### Elevator' +[//]: # '#### FixedNav' #### Indicator -- type 属性的值调整为 `'anchor'` 或 `'slide'` -- color 属性的值增加 `'white'` - -#### Menu +- 新增 `type` 属性,可选值 `'anchor'` 或 `'slide'`,默认 `anchor` +- 新增 `color` 属性,可选值 `primary` | `'white'`,默认值 `primary` +- `total` 属性的默认值调整为 `2` -- 移除 `fontClassName` -- 移除 `iconClassPrefix` -- `closeOnClickOverlay` 重命名为 `closeOnOverlayClick` -- `titleIcon` 重命名为 `icon` -- `optionsIcon` 重命名为 `icon` -- 增加 `closeOnClickAway` +[//]: # '#### Menu' #### NavBar @@ -270,252 +131,46 @@ plugins: [ #### Pagination -- 新增 `lite` 模式,只展示页码,不支持事件交互 -- 新增 `defaultValue` 非受控值 -- `modelValue` 重命名为 `value`,受控值 -- `prevText` 重命名为 `prev`,类型修改为 `ReactNode` -- `nextText` 重命名为 `next`,类型修改为 `ReactNode` -- `forceEllipses` 重命令为 `ellipse` -- `showPageSize` 重命名为 `itemSize` -- `itemsPerpage` 重命名为 `pageSize` -- `totalitems` 重命名为 `total` -- `pageNodeRender` 重命名为 `itemRender` -- 移除 `pageCount`,通过 `total` 与 `pageSize` 组合实现 +- `itemRender` 属性类型调整为:`(page: any, index: number) => ReactNode` #### SideNavBar -- `offset` 重命名为 `indent` - -#### SideBar - -- 新增SideBar组件 -- 支持属性value,用于当前激活的`item`的key -- 支持属性defaultValue, 表示未设置value时,`item`的key的默认值 -- 支持属性contentDuration, 用于内容滚动动画时长 -- 支持属性sidebarDuration, 用于侧栏滚动动画时长 -- 支持属性onClick, 点击标签时触发 -- 支持属性onChange, 当前激活的标签改变时触发 - -#### Tabbar - -- `unactiveColor` 重命名为 `inactiveColor` -- `tabTitle` 重命名为 `title`,类型修改为 `ReactNode` -- `bottom` 重命名为 `fixed` -- `safeAreaInsetBottom` 重命名为 `safeArea` -- `visible` 重命名为 `defaultValue`,非受控 -- `activeVisible` 重命名为 `value`,受控 - -#### TabbarItem - -- 使用方式修改为 `Tabbar.Item` -- `icon` 类型改为 `ReactNode`,移除其他 `icon` 关联属性 -- 移除 `href`,通过 `onSwitch` 事件控制链接与路由跳转 -- 移除 `num`,支持传入所有 `Badge` Props -- 移除 `color`,使用父元素的 `activeColor`,保持同样的 `active` 状态 - -#### Tabs - -- 增加 `lite`、`card`、`button`、`divider` 模式。 -- 移除 `background`,通过 `className` 或 `style` 控制 -- 移除 `titleScroll`, 默认支持滚动 -- 移除 `ellipsis`,默认 `flex:1` -- 移除 `size`,通过 css 变量 `--nutui-tabs-titles-item-font-size` 实现 -- `animatedTime` 重命名为 `duration` -- `titleGutter` 重命名为 css 样式变量实现 -- `titleNode` 重命名为 `title` -- `color` 重命名为 `activeColor` -- `type` 重命名为 `activeType` -- `leftAlign` 重命名为 `align` -- `onClick` 类型改为 `(index: string | number) => void` -- `onChange` 类型改为 `(index: string | number) => void` -- 增加 defaultValue -- 增加 `activeType` 类型 `simple`,实现选项卡的简约选择,只修改字号和字重,不处理字色。 - -#### Tabs.Tabpane - -- `paneKey` 重命名为 `value` +- 注意:** 该组件即将被废弃。请使用 SideBar ** + +[//]: # '#### Tabbar' +[//]: # '#### TabbarItem' +[//]: # '#### Tabs' +[//]: # '#### Tabs.Tabpane' ### 数据录入 -#### Calendar - -- `poppable` 重命名为 `popup` -- `isAutoBackFill` 重命名为 `autoBackfill` -- `toDateAnimation` 重命名为 `scrollAnimation` -- `startText` 类型改为 `ReactNode` -- `endText` 类型改为 `ReactNode` -- `confirmText` 类型改为 `ReactNode` -- `onBtn` 重命名为 `renderHeaderButtons` -- `onDay` 重命名为 `renderDay` -- `onTopInfo` 重命名为 `renderDayTop` -- `onBottomInfo` 重命名为 `renderDayBottom` -- `onSelected` 重命名为 `onDayClick` -- `onChoose` 重命名为 `onConfirm` -- `onYearMonthChange` 重命名为 `onPageChange` -- 新增 `firstDayOfWeek`,支持按照周进行选择,指定周起止日,如0-6 - -#### Cascader - -- 新增 `defaultValue`,其中 `defaultValue` 用于非受控,原 `value` 用于受控。两者的类型都改为 `(number | string | undefined)[]` -- `checkedIcon` 重命名为 `activeIcon` -- `poppable` 重命名为 `popup` -- `lazyLoad` 重命名为 `onLoad`,当启动懒加载 `lazy` 时,动态加载数据 -- `convertConfig` 重命名为 `format`,配置转换规则 -- 合并 `textKey` `valueKey` `childrenKey` 三个属性为对象属性 `optionKey` -- 移除 `tabsColor`, 该属性为设置 `Tabs` 当前选中的 `tab` 的下划线色值,但该值最好与文字部分搭配使用,统一处理 CSS 变量。 - -#### Checkbox - -- 新增 `defaultChecked`,用于非受控,`checked` 用于受控 -- 新增 `value`,用于 group 模式 -- `textPosition` 重命名为 `labelPosition` -- `iconName` 重命名为 `icon`,类型为 `ReactNode` -- `iconAcitveName` 重命名为 `activeIcon` -- `iconIndeterminateName` 重命名为 `iconIndeterminateIcon` -- 移除 `iconSize` -- 部分 className 命名变更,废弃 `nutui-checkbox__xx` 命名方式,直接使用 `nutui-checkbox-xx`,并对选中状态命名修订为 `nutui-checkbox-icon-checked` -- 增加半选的禁用状态 - -#### Checkbox.Group - -- 新增 `defaultValue`,用于非受控,`value` 用于受控 -- `textPosition` 重命名为 `labelPosition` -- `toggleAll` 重命名为 `toggle` -- `toggleReverse` 重命名为 `reverse` -- 部分 className 命名变更,废弃 `nutui-checkbox__xx` 命名方式,直接使用 `nutui-checkbox-xx`,并对选中状态命名修订为 `nutui-checkbox-icon-checked` - -#### DatePicker - -- `modelValue` 重命名为 `value`,并增加 `defaultValue` -- `isShowChinese` 重命名为 `showChinese` -- `minDate` 重命名为 `startDate` -- `maxDate` 重命名为 `endDate` -- `onConfirmDatePicker` 重命名为 `onConfirm` -- `onCloseDatePicker` 重命名为 `onClose` -- 因为依赖组件`Picker`的变更,方法 `onConfirmDatePicker`、`onChange`的参数进行了调整,从`(selectedValue, selectedOptions)` 改为 `(selectedOptions, selectedValue)`。 - -#### Form - -- 增加 `footer`,类型为 `ReactNode`,用于表单底部区域,通常用于设置提交、重置按钮 -- 增加 `initialValues`,用于设置表单初始值,同时用于表单的重置 -- 增加 `name` 属性 - -#### Form.Item - -- 移除 `labelWidth`, 通过 `--nutui-form-item-label-width` 控制宽度 -- 增加 `required`,用于必选样式控制 -- 增加 `trigger`,用于设置数据更新的时机 -- 增加 `valuePropName`,用于收集子组件受控的属性映射 -- 增加 `getValueFromEvent`,用于在收集数据中进行数据转换 -- 增加 `onclick` 用于收集子组件的 `ref` - -#### Input - -- 新增 `plain` 属性,标记为 纯文本型;该值默认为false,标记为 container 容器型; -- 区分了 readonly 和 disabled 的样式; -- 删除一些样式变量,统一到由通用变量控制,如`$input-color`、`$input-disabled-color` - -#### InputNumber - -- 增加 `allowEmpty`, 用于允许内容是否为空 -- 新增 `defaultValue`,用于非受控,`value` 用于受控 -- `decimalPlaces` 重命名为 `digits` -- `isAsync` 重命名为 `async` -- 移除 `inputWidth`, 通过`--nutui-inputnumber-input-width`控制输入框的宽度 -- 移除 `buttonSize`, 通过`–nutui-inputnumber-button-width` 和 `–nutui-inputnumber-button-height`控制按钮的宽度和高度 -- taro 新增 `formatter` 属性, 用于指定输入框展示值的格式 -- 移除 `errorMessage` -- 移除 `showWordLimit` -- `autofocus` 重命名为 `autoFocus` -- `type="textarea"` 建议改为使用 `TextArea` 组件实现 - -#### NumberKeyboard - -- `randomKeys` 重命名为 `random` -- `customKey` 重命名为 `custom` -- `title` 类型变更为 `ReactNode` -- 新增 `onConfirm` 事件 -- 移除 `popClass` 定义,默认支持透传 `Popup` 属性 - -#### Picker - -- `isVisible` 重命名为 `visible` -- `listData` 重命名为 `options` -- `defaultValueData` 重命名为 `defaultValue` -- 增加受控 `value` -- `swipeDuration` 重命名为 `duration` -- `onCloseUpdate` 重命名为 `afterClose` -- 方法 `onConfirm`、`onClose`、`afterClose`、`onChange`的参数进行了调整,从`(selectedValue, selectedOptions)` 改为 `(selectedOptions, selectedValue)`。 - -#### Radio - -- 移除 `iconSize`,可通过 Icon 的 css 变量设置 -- `iconName` 重命名为 `icon`,类型修改为 `ReactNode` -- 增加 `labelPosition`,用于设置 `label` 的位置 -- 增加 `checked` 和 `defaultChecked` ,用于受控和非受控 -- `onChange` 类型修改为 `(checked: boolean) => void` -- 部分 className 命名变更,废弃 `nutui-radio__xx` 命名方式,直接使用 `nutui-radio-xx`,并对选中状态命名修订为 `nutui-radio-icon-checked` - -### Radio.Group - -- `textPosition` 重命名为 `labelPosition` -- 增加 `defaultValue` ,用于非受控 -- `onChange` 类型修改为 `(value: string| number) => void` -- 部分 className 命名变更,废弃 `nutui-radio__xx` 命名方式,直接使用 `nutui-radio-xx`,并对选中状态命名修订为 `nutui-radio-icon-checked` - -#### Range - -- `maxDesc` 重命名为 `maxDescription`,类型改为 `ReactNode` -- `minDesc` 重命名为 `minDescription`,类型改为 `ReactNode` -- `curValueDesc` 重命名为 `currentDescription`,类型改为 `(value) => ReactNode` -- 移除 `hiddenRange`,通过 `max/minDescription` 传 `null` 实现 -- 移除 `hiddenTag`,通过 `currentDescription` 传 `null` 实现 -- 移除 `activeColor`、`inactiveColor`、`buttonColor`,通过 `css` 变量实现 -- `onDragStart` 重命名为 `onStart` -- `onDragEnd` 重命名为 `onEnd` -- `modelValue` 重命为 `value`,增加 `defaultValue` 非受控方式 +[//]: # '#### Calendar' +[//]: # '#### Cascader' +[//]: # '#### Checkbox' +[//]: # '#### Checkbox.Group' +[//]: # '#### DatePicker' +[//]: # '#### Form' +[//]: # '#### Form.Item' +[//]: # '#### Input' +[//]: # '#### InputNumber' +[//]: # '#### NumberKeyboard' +[//]: # '#### Picker' +[//]: # '#### Radio' +[//]: # '### Radio.Group' +[//]: # '#### Range' #### Rate -- `minimizeValue` 重命名为 `min` -- `readonly` 重命名为 `readOnly` -- 移除 `spacing`,通过 css 样式变量实现 -- 移除 `activeColor`、`voidColor`、`iconSize`,通过 `checkedIcon`、`uncheckedIcon` 实现 -- 增加受控 `value` 与非受控 `defaultValue`,移除 `modelValue` - -#### SearchBar - -- `onClickInput` 重命名为 `onInputClick` -- 移除 `clearSize`,样式默认 -- 移除 `background`,使用 CSS 变量 `--nutui-searchbar-background` 实现 -- 移除 `inputBackground`,使用 CSS 变量 `--nutui-searchbar-input-background` 实现 -- 移除 `align`,使用 CSS 变量 `--nutui-searchbar-input-text-align` -- 新增 `left` 和 `right`,为 `ReactNode` 节点,可自定义内容 -- 移除 `leftoutIcon` 和 `label`,使用 `left` 实现 -- 移除 `rightoutIcon` 和 `actionText`,使用 `right` 实现 -- 移除 `leftinIcon`,使用 `leftIn` 实现 -- 移除 `rightinIcon`,使用 `rightIn` 实现,同时兼顾和 clearIcon 的交互,当设置rightIn时,默认展示 rightIn,当触发输入后,展示 clearIcon。同时增加 `backable` 来标记是否展示左侧返回Icon -- 移除 `onCancel`,使用 `right` 来实现事件处理 -- 移除 `onClickLeftinIcon`,用户可使用 `left` 来实现事件处理 -- 移除 `onClickLeftoutIcon`,用户可使用 `left` 来实现事件处理 -- 移除 `onClickRightinIcon`,用户可使用 `right` 来实现事件处理 -- 移除 `onClickRightoutIcon`,用户可使用 `right` 来实现事件处理 - -#### ShortPassword - -- `desc` 重命名为 `description` -- `noButton` 重命名为 `hideFooter` -- `onOk` 重命名为 `onConfirm` -- `errorMsg` 重命名为 `error` -- 移除 `closeOnClickOverlay`,默认支持透传 Popup 属性 -- `title`、`description`、`tips`、`error` 类型修改为 `ReactNode` -- `modelValue` 重命名为 `value`,受控模式 -- 新增 `onFoucs` 事件 +- 新增 size 属性,控制图标的大小 +- 新增 showScore 属性,展示评分文案 + +[//]: # '#### SearchBar' +[//]: # '#### ShortPassword' #### TextArea -- 新增 `plain` 属性,标记为 纯文本型;该值默认为false,标记为 container 容器型; -- 新增 `status` 属性,值为 `default` | `error`,可定义输入框的状态; +- 新增 `plain` 属性,标记为 纯文本型;该值默认为false,标记为 container 容器型 +- 新增 `status` 属性,值为 `default` | `error`,可定义输入框的状态 - 删掉一些可使用基础样式变量,并且建议使用基础样式变量的样式变量,比如 `$textarea-font` `$textarea-limit-color` `$textarea-disabled-color` #### Uploader @@ -532,26 +187,12 @@ plugins: [ ### 操作反馈 -#### ActionSheet - -- `title`,类型变更为 `ReactNode` -- `description`,类型变更为 `ReactNode` -- `cancelTxt`,重命名为 `cancelText`,类型变更为 `ReactNode` -- `menuItems` 重命名为 `options` -- `chooseTagValue` 重命名为 `value` -- `onChoose` 重命名为 `onSelect` -- 增加 `options` 的定义 - - `color` 重命名为 `danger` - - `name`,列表项的标题key - - `description`,列表项的描述key - - `danger`,列表项中提醒用户重点关注的操作 - - `disabled`,列表项中禁用项 +[//]: # '#### ActionSheet' #### BackTop -- 使用 `HoverButton` 重构 `BackTop` -- 新增 `icon` 字段,可直接修改图标 -- 继续支持自定义节点 +- Taro + - 提供鸿蒙端能力,增加 scrollRes 属性 #### Dialog @@ -559,304 +200,74 @@ plugins: [ - 当只有一个主操作按钮时,主操作按钮样式撑开; - 增加了底部icon的大小设置的样式变量;修改右侧按钮的默认值为 16 px; -#### Drag +[//]: # '#### Drag' #### InfiniteLoading -- `useCapture` 重命名为 `capture` -- `onScrollChange` 重命名为 `onScroll` -- `isOpenRefresh` 重命名为 `pullRefresh` -- `pullTxt` 重命名为 `pullingText`,类型变更为 `ReactNode` -- `loadTxt` 重命名为 `loadingText`,类型变更为 `ReactNode` -- `containerId` 重命名为 `target` -- 修订类名如 `top-box`、`bottom-box` 为 `nut-infinite-top-tips`、`nut-infinite-bottom-tips` - -#### Notify +- `target` 属性获取监听的目标元素 -- 移除 `color` ,通过css变量`--nutui-notify-text-color`实现 -- 移除`background`,通过css变量`--nutui-notify-base-background-color`实现 -- `onClosed` 重命名为 `onClose` - -#### PullToRefresh - -#### Swipe - -- 移除 `leftWidth` ,通过 `leftAction` 实现 -- 移除 `rightWidth` ,通过 `rightAction` 实现 +[//]: # '#### Notify' +[//]: # '#### PullToRefresh' +[//]: # '#### Swipe' #### Switch -- 新增 `defaultChecked`,用于非受控,`checked` 用于受控 -- 移除 `isAsync`,通过 `checked`实现 -- 移除 `activeColor` ,通过css变量`--nutui-switch-open-background-color`实现 -- 移除 `inactiveColor`,通过css变量`--nutui-switch-close-background-color`实现 - `activeText` 属性类型更改为`ReactNode` - `inactiveText` 属性类型更改为 `ReactNode` -#### Toast - -- 移除H5版本 `id` -- 移除 `center`和 `bottom`,通过 `position` 实现 -- 移除 `bgColor`,通过 css 变量实现 -- 移除 `customClass`,通过 `className` 实现 -- 移除 `cover` 和 `coverColor` ,通过css变量实现 -- 移除 `loadingRotate`,旋转状态通过 `iconFont`实现 -- 移除 `textAlignCenter`,通过css变量实现 -- 修改 `closeOnClickOverlay` 为 `closeOnOverlayClick` ,语义不变,是否在点击遮罩层后关闭提示 -- 新增 `lockScroll` ,用于背景是否锁定,默认值为 `false` +[//]: # '#### Toast' ### 展示组件 -#### Animate - -- `type` 属性类型更改为 `AnimateType` ,具体值详见文档 -- `action` 属性类型更改为 `initial \| click` - -#### AnimatingNumbers - -- `maxLen` 重命名为 `length` -- `endNumber` 重命名为 `value`,类型修改为 `string|number` -- `delaySpeed` 重命名为 `delay` -- `easeSpeed` 重命名为 `duration` - -#### Audio - -- `url` 重命名为 `src` -- `autoplay` 重命名为 `autoPlay` -- `onFastBack` 重命名为 `onBack` -- `onPlayEnd` 重命名为 `onEnd` - -#### Avatar - -- `url` 重命名为 `src` -- `onActiveAvatar` 重命名为 `onClick` -- 新增 `fit` 属性,用于图片填充模式 -- 移除 `iconSize`,可通过 icon 属性传入自定义 icon 或借助 CSS Variables 修改 icon 大小 - -#### AvatarGroup - -- AvatarGroup `maxCount` 重命名为 `max` -- AvatarGroup `span` 重命名为 `gap` -- AvatarGroup `zIndex` 重命名为 `level` +[//]: # '#### Animate' +[//]: # '#### AnimatingNumbers' +[//]: # '#### Audio' +[//]: # '#### Avatar' +[//]: # '#### AvatarGroup' #### Badge - 新增 `size` 属性,dot 尺寸,当 dot 等于 `true` 时生效 -- 移除 `徽标背景颜色`,通过css变量`--nutui-badge-background-color`实现 +- 移除 `color`属性(`徽标背景颜色`),通过css变量`--nutui-badge-background-color`实现 -#### CircleProgress - -- `progress` 重命名为 `percent` -- `circleColor` 重命名为 `color` -- `pathColor` 重命名为 `background` - -#### Collapse - -- 新增 `defaultActiveName` 非受控 -- `activeName` 改为受控方式 -- `icon` 重命名为 `expandIcon`,类型修改为 `ReactNode` -- `onChange` 参数变更为 `activeName, name, status` -- 新增一种样式,尝试修改 `--nutui-collapse-item-border-bottom` 和 `-nutui-collapse-item-header-border-bottom`,可查看 - -#### CollapseItem - -- 使用方式调整为 `Collapse.Item` -- subTitle 重命名为 extra,类型修改为 `ReactNode` -- 新增 `expnandIcon`,优先级高于父组件对应值 -- 新增 `rotate`,优先级高于父组件对应值 +[//]: # '#### CircleProgress' +[//]: # '#### Collapse' +[//]: # '#### CollapseItem' #### CountDown -- 新增 `remainingTime`,支持剩余毫秒时间倒计时。 - -#### Ellipsis +- 新增 `type`,设置变体类型 -- 新增 `className` 和 `style` 属性的支持 -- 优化 H5 的代码,去掉 `useEffect` 渲染改用 `useLayoutEffect` +[//]: # '#### Ellipsis' #### Empty -- 新增 `status` 属性,用于默认图片错误类型 -- 新增 `size` 属性,用于区分全屏与半屏状态下图片的不同大小 -- 新增 `title` 属性,用于展示提示的标题部分 -- 新增 `actions` 属性,用于展示提示的操作部分,支持1个或2个操作 -- `image` 属性类型更改为 `ReactNode` -- 新增 `title`、`size`、`actions` 属性,支持标题的设置、图片大小的设置、可能的操作设置,操作设置默认以`Button`实现。 - -#### ImagePreview - -- `show` 重命名为 `visible` -- `autoplay` 重命名为 `autoPlay` -- `initNo` 重命名为 `defaultValue`,同时增加 `value`,为受控 -- `paginationVisible` 重命名为 `indicator` -- `paginationColor` 重命名为 `indicatorColor` -- `contentClose` 重命名为 `closeOnContentClick` -- 在 `Taro` 下支持视频 - -#### NoticeBar - -- `direction` 的可选值从 `across` 重命名为 `horizontal` -- `text` 重命名为 `content` -- `closeMode` 重命名为 `closeable` -- `leftIcon` 类型扩充,支持 `ReactNode` -- `rightIcon` 类型扩充,支持 `ReactNode` -- `color` 移除,使用 CSS 变量,之前已支持 -- `background` 移除,使用 CSS 变量,之前已支持 -- `wrapable` 重命名为 `wrap` -- `standTime` 重命名为 `duration` -- `onClickItem` 重命名为 `onItemClick` -- `complexAm` 废弃 - -#### Popover - -- 移除 `theme` 属性,可以通过css变量 `--nutui-brand-color` 控制暗黑模式 -- 新增 `showArrow` 属性,用于是否显示小箭头 -- 新增 `closeOnActionClick` 属性,用于是否在点击选项后关闭 -- 新增 `closeOnOutsideClick` 属性,用于是否在点击外部元素后关闭菜单 -- 新增 `targetId` 属性,用于自定义目标元素 id -- 新增 `onOpen` 属性,用于点击菜单时触发 -- 新增 `onClose` 属性,用于关闭菜单时触发 -- 新增 `action` 属性,用于为对应的选项添加方法 -- `onChoose` 重命名为 `onSelect` -- 继承Popup组件的 `overlayStyle` 、`overlayClassName` 、`overlay` 、`closeOnOverlayClick` 属性。 +- 移除 `--nutui-empty-title-margin-top` 主题变量 +- 移除 `--nutui-empty-description-margin-top` 主题变量 + +[//]: # '#### ImagePreview' +[//]: # '#### NoticeBar' +[//]: # '#### Popover' #### Price - 修改 `size`,增加 'xlarge' 尺寸 - 新增 `color`, 价格类型 -#### Progress - -- `percentage` 重命名为 `percent`,受控 -- 移除 `isShowPercentage`,可以自定义传入文案 -- 移除 `textWidth`,可以自定义传入内容的宽度 -- `strokeColor` 重命名为 `color` -- `fillColor` 重命名为 `background` -- 移除 `size`,通过 `strokeWidth`、`progress-height` css 变量实现尺寸自定义 -- `status` 重命名为 `animated`,表示是否展示动画效果 -- 移除 `textBackground`,通过 css 实现 -- 移除 `textColor`,通过 css 实现 -- 移除 `textInside`,仅保留内显功能 -- 移除 `textType、icon`,通过 `children` 传入自定义 `ReactNode`,不再区分类型 -- 新增 `lazy` 属性,支持每次进入可视区时展示进度条动画 -- 新增 `delay` 属性,表示延迟数据加载时长 - -#### Skeleton - -- `loading` 重命名为 `visible` -- `row` 重命名为 `rows` -- 移除 `width`,通过 css 变量 `skeleton-line-width` 实现 -- 移除 `height`,通过 css 变量 `skeleton-line-height` 实现 -- 移除 `round`,通过 css 变量 `skeleton-line-border-radius` 实现 - -#### Steps - -- `current` 重命名为 `value` -- `onClickStep` 重命名为 `onStepClick` -- `progressDot` 重命名为 `dot` - -#### Step - -- 移除 `iconColor`,可通过 `icon` 属性传入自定义 icon 或借助 CSS Variables 修改 icon 颜色 -- 移除 `size`,可通过 icon 属性传入自定义 icon 或借助 CSS Variables 修改 icon 大小 -- 移除 `renderContent` ,可通过 `description` 实现 -- `title` 类型修改为 `ReactNode` -- `content` 重命名为 `description`,类型改为 `ReactNode` -- `icon` 类型修改为 `ReactNode` -- `activeIndex` 重命名为 `value` - -#### Swiper - -- h5 - - 移除 `paginationColor`,通过 `indicator` 的 CSS 变量控制 - - 移除 `paginationBgColor`,通过 `indicator` 的 CSS 变量控制 - - 移除 `pageContent`,通过 indicator 实现 - - `autoplay` 重命名为 `autoplay` - - `initPage` 重命名为 `defaultValue` - - `paginationVisible` 重命名为 `indicator`,类型改为`ReactNode` - - `isPreventDefault` 重命名为 `preventDefault` - - `isStopPropagation` 重命名为 `stopPropagation` - - `isCenter` 重命名为 `center` -- taro - - 通过封装 Taro 的 `Swiper` 和 `SwiperItem` 实现,支持的属性可参考 Taro Swiper 文档。 - -#### Table - -- `onSorter` 重命名为 `onSort` -- 合并 `summary` 与 `noData` 的样式处理 - -#### Tag - -- `color` 重命名为 `background` -- `textColor` 重命名为 `color` - -#### TrendArrow - -- `rate` 重命名为 `value` -- `showSign` 重命名为 `symbol` -- `showZero` 重命名为 `zero` -- `arrowLeft` 重命名为 `left` -- `syncTextColor` 重命名为 `sync` -- `textColor` 重命名为 `color` -- `upIconName` 重命名为 `riseIcon`,类型修改为 `React.Node` -- `downIconName` 重命名为 `dropIcon`,类型修改为 `React.Node` -- 移除 `iconSize`,通过`riseIcon`、`dropIcon`自定义传入icon大小 -- 新增 `size`,star 尺寸, 默认值 `normal` 为 `12px` -- 新增 `showScore`, 展示评分 - -#### Video - -- 在 `Taro` 下新增video的适配 - -#### VirtualList - -- `sourceData` 重命名为 `list` -- `conatinerSize` 重命名为 `containerHeight` -- `itemSize` 重命名为 `itemHeight` -- `itemEqualSize` 重命名为 `itemEqual` -- `horizontal` 修改为 `direction`,默认值 `vertical`,可选值 `horizontal` - -#### WaterMark - -- `fontColor` 重命名为 `color` - -### 特色组件 - -#### Address - -- 该组件的 `custom` 改用 `Cascader` 组件重写;`custom2`也将使用 `Cascader` 完成,在 `Cascader` 中支持 `Elevator`,开发中。所以会废弃 `province`、`city`、`country`、`town` 这些属性,同时支持 `Cascader` 的属性。 -- `modelValue` 重命名为 `visible` -- `modelSelect` 重命名为 `defaultValue` -- `onSelected` 重命名为 `onSelect` -- `existAddress` 重命名为 `existList` -- `selectedIcon` 重命名为 `selectIcon` -- `closeBtnIcon` 重命名为 `closeIcon` -- `backBtnIcon` 重命名为 `backIcon` -- `isShowCustomAddress` 重命名为 `custom`,用于已有地址列表与自定义列表的切换,修改默认为值 `false` -- `customAndExistTitle` 废弃,与 `custom` 合并,当 `custom` 为 true 时,为默认文案,设置为某字符串时,展示字符串。 -- `customAddressTitle`、`existAddressTitle` 重命名为 `title`,不再区分状态,可通过onSwitch修改title -- 精简布局和样式 - -#### Barrage - -- `barrageList` 重命名为 `list` -- `frequency` 重命名为 `interval` -- `speeds` 重命名为 `duration` -- `top` 重命名为 `gapY` - -#### Card - -#### Signature - -- `type` 类型修改为 `png|jpg` -- `unSupportTpl` 重命名为 `unsupported`,类型修改为 `ReactNode` -- 新增 `confirm`和`clear` ref 的方法,移除组件内部 `button`元素,通过自定义按钮元素,设置元素点击事件结合ref实现,参考文档demo示例 - -#### TimeSelect - -- 移除 `height`,通过 `style` 设置高度 -- `title` 类型修改为 `ReactNode` -- `onPannelChange` 重命名为 `onDateChange` -- 移除 `dates`、`times`,合并为 `options`,重新设计了数据结构 -- 增加 `optionKey` 用于自定义数据中的关键字 -- 移除 `currentKey`,新增 `defaultValue` 用于设置默认选项,支持时间选择 +[//]: # '#### Progress' +[//]: # '#### Skeleton' +[//]: # '#### Steps' +[//]: # '#### Step' +[//]: # '#### Swiper' +[//]: # '#### Table' +[//]: # '#### Tag' +[//]: # '#### TrendArrow' +[//]: # '#### Video' +[//]: # '#### VirtualList' +[//]: # '#### WaterMark' +[//]: # '### 特色组件' +[//]: # '#### Address' +[//]: # '#### Barrage' +[//]: # '#### Card' +[//]: # '#### Signature' +[//]: # '#### TimeSelect' diff --git a/src/packages/backtop/doc.taro.md b/src/packages/backtop/doc.taro.md index dec6816459..bfa5ecd0ea 100644 --- a/src/packages/backtop/doc.taro.md +++ b/src/packages/backtop/doc.taro.md @@ -61,7 +61,7 @@ import { BackTop } from '@nutui/nutui-react-taro' | threshold | 页面垂直滚动多高后出现 | `number` | `200` | | zIndex | 设置组件页面层级 | `number` | `900` | | duration | 设置动画持续时间,为 0 时表示无动画 | `number` | `1000` | -| scrollRes | 被监听容器滚动时的回调参数,主要用于 rn、鸿蒙端 | `PageScrollObejct` | `-` | +| scrollRes | 被监听容器滚动时的回调参数 | `PageScrollObejct` | `-` | | onClick | 按钮点击时触发事件 | `(event: MouseEvent) => void` | `-` | ## 主题定制 From e6a63374f77ba7237680458c83dac8406268ea7e Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 11:25:04 +0800 Subject: [PATCH 47/54] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9pickerprops?= =?UTF-8?q?=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datepickerview.spec.tsx.snap | 431 ++++++++++++++++++ src/packages/picker/__tests__/picker.spec.tsx | 3 +- src/packages/picker/picker.taro.tsx | 37 +- src/packages/picker/picker.tsx | 34 +- src/packages/picker/types.ts | 31 +- 5 files changed, 455 insertions(+), 81 deletions(-) create mode 100644 src/packages/datepickerview/__test__/__snapshots__/datepickerview.spec.tsx.snap diff --git a/src/packages/datepickerview/__test__/__snapshots__/datepickerview.spec.tsx.snap b/src/packages/datepickerview/__test__/__snapshots__/datepickerview.spec.tsx.snap new file mode 100644 index 0000000000..4f973c2b0b --- /dev/null +++ b/src/packages/datepickerview/__test__/__snapshots__/datepickerview.spec.tsx.snap @@ -0,0 +1,431 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`should match snapshot 1`] = ` +
+
+
+
+
+
+ 2015 +
+
+ 2016 +
+
+ 2017 +
+
+ 2018 +
+
+ 2019 +
+
+ 2020 +
+
+ 2021 +
+
+ 2022 +
+
+ 2023 +
+
+ 2024 +
+
+ 2025 +
+
+ 2026 +
+
+ 2027 +
+
+ 2028 +
+
+ 2029 +
+
+ 2030 +
+
+ 2031 +
+
+ 2032 +
+
+ 2033 +
+
+ 2034 +
+
+ 2035 +
+
+
+
+
+
+ 01 +
+
+ 02 +
+
+ 03 +
+
+ 04 +
+
+ 05 +
+
+ 06 +
+
+ 07 +
+
+ 08 +
+
+ 09 +
+
+ 10 +
+
+ 11 +
+
+ 12 +
+
+
+
+
+
+ 01 +
+
+ 02 +
+
+ 03 +
+
+ 04 +
+
+ 05 +
+
+ 06 +
+
+ 07 +
+
+ 08 +
+
+ 09 +
+
+ 10 +
+
+ 11 +
+
+ 12 +
+
+ 13 +
+
+ 14 +
+
+ 15 +
+
+ 16 +
+
+ 17 +
+
+ 18 +
+
+ 19 +
+
+ 20 +
+
+ 21 +
+
+ 22 +
+
+ 23 +
+
+ 24 +
+
+ 25 +
+
+ 26 +
+
+ 27 +
+
+ 28 +
+
+ 29 +
+
+ 30 +
+
+ 31 +
+
+
+
+
+
+
+
+`; diff --git a/src/packages/picker/__tests__/picker.spec.tsx b/src/packages/picker/__tests__/picker.spec.tsx index 60fe500538..c3f2447d3f 100644 --- a/src/packages/picker/__tests__/picker.spec.tsx +++ b/src/packages/picker/__tests__/picker.spec.tsx @@ -2,6 +2,7 @@ import React, { useState } from 'react' import { render, waitFor, fireEvent, act } from '@testing-library/react' import '@testing-library/jest-dom' import Picker from '../picker' +import { PickerOptions } from '@/packages/pickerview' function sleep(delay = 0): Promise { return new Promise((resolve) => { @@ -13,7 +14,7 @@ interface PickerOption { label: string | number value: string | number disabled?: boolean - children?: PickerOption[] + children?: PickerOptions className?: string | number } diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index cba096362d..4f5651654f 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -11,44 +11,17 @@ import isEqual from 'react-fast-compare' import { PickerOptions, PickerValue, - PickerOption, PickerOnChangeCallbackParameter, } from '@/packages/pickerview/types' import PickerView from '@/packages/pickerview/index.taro' -import Popup, { PopupProps } from '@/packages/popup/index.taro' +import Popup from '@/packages/popup/index.taro' import SafeArea from '@/packages/safearea/index.taro' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider/index.taro' import { usePropsValue } from '@/hooks/use-props-value' -import { BasicComponent, ComponentDefaults } from '@/utils/typings' +import { ComponentDefaults } from '@/utils/typings' import { PickerActions, PickerRef } from './types' - -export interface PickerProps extends Omit { - visible?: boolean | undefined - title?: string - options: PickerOptions[] - value?: PickerValue[] - defaultValue?: PickerValue[] - threeDimensional?: boolean - duration: number | string - closeOnOverlayClick: boolean - renderLabel?: (item: PickerOption) => React.ReactNode - - popupProps: Partial< - Omit - > - onConfirm?: ( - selectedOptions: PickerOptions, - selectedValue: PickerValue[] - ) => void - onCancel?: () => void - onClose?: ( - selectedOptions: PickerOptions, - selectedValue: PickerValue[] - ) => void - onChange?: (args0: PickerOnChangeCallbackParameter) => void - children?: any -} +import { PickerProps } from './types.taro' const defaultProps = { ...ComponentDefaults, @@ -167,7 +140,7 @@ const InternalPicker: ForwardRefRenderFunction< { + onClick={(e: { stopPropagation: () => void }) => { e.stopPropagation() onCancelEvent() }} @@ -177,7 +150,7 @@ const InternalPicker: ForwardRefRenderFunction< {title || ''} { + onClick={(e: { stopPropagation: () => void }) => { e.stopPropagation() onConfirmEvent() }} diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index a2a965bfc6..6477f9d83c 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -10,44 +10,16 @@ import isEqual from 'react-fast-compare' import { PickerOptions, PickerValue, - PickerOption, PickerOnChangeCallbackParameter, } from '@/packages/pickerview/types' import PickerView from '@/packages/pickerview/index' -import Popup, { PopupProps } from '@/packages/popup/index' +import Popup from '@/packages/popup/index' import SafeArea from '@/packages/safearea/index' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/hooks/use-props-value' -import { BasicComponent, ComponentDefaults } from '@/utils/typings' -import { PickerActions, PickerRef } from './types' - -export interface PickerProps extends Omit { - visible?: boolean | undefined - title?: string - options: PickerOptions[] - value?: PickerValue[] - defaultValue?: PickerValue[] - threeDimensional?: boolean - duration: number | string - closeOnOverlayClick: boolean - renderLabel?: (item: PickerOption) => React.ReactNode - - popupProps: Partial< - Omit - > - onConfirm?: ( - selectedOptions: PickerOptions, - selectedValue: PickerValue[] - ) => void - onCancel?: () => void - onClose?: ( - selectedOptions: PickerOptions, - selectedValue: PickerValue[] - ) => void - onChange?: (args0: PickerOnChangeCallbackParameter) => void - children?: any -} +import { ComponentDefaults } from '@/utils/typings' +import { PickerActions, PickerProps, PickerRef } from './types' const defaultProps = { ...ComponentDefaults, diff --git a/src/packages/picker/types.ts b/src/packages/picker/types.ts index adc36c5edb..147341b1c5 100644 --- a/src/packages/picker/types.ts +++ b/src/packages/picker/types.ts @@ -1,7 +1,11 @@ -import { RefObject } from 'react' import { PopupProps } from '@/packages/popup/types' import { BasicComponent } from '@/utils/typings' -import { PickerOption, PickerOptions } from '@/packages/pickerview/types' +import { + PickerOnChangeCallbackParameter, + PickerOption, + PickerOptions, + PickerValue, +} from '@/packages/pickerview/types' export type PickerRef = PickerActions export type PickerActions = { @@ -13,33 +17,26 @@ export type ColumnsType = 'single' | 'multiple' | 'cascade' export interface PickerProps extends Omit { visible?: boolean | undefined title?: string - options: (PickerOption | PickerOptions)[] - value?: (number | string)[] - defaultValue?: (number | string)[] + options: PickerOptions[] + value?: PickerValue[] + defaultValue?: PickerValue[] threeDimensional?: boolean duration: number | string closeOnOverlayClick: boolean + renderLabel?: (item: PickerOption) => React.ReactNode + popupProps: Partial< Omit > onConfirm?: ( selectedOptions: PickerOptions, - selectedValue: (string | number)[] + selectedValue: PickerValue[] ) => void onCancel?: () => void onClose?: ( selectedOptions: PickerOptions, - selectedValue: (string | number)[] - ) => void - afterClose?: ( - selectedOptions: PickerOptions, - selectedValue: (string | number)[], - pickerRef: RefObject - ) => void - onChange?: ( - selectedOptions: PickerOptions, - selectedValue: (string | number)[], - columnIndex: number + selectedValue: PickerValue[] ) => void + onChange?: (args0: PickerOnChangeCallbackParameter) => void children?: any } From 95dc4b8c5d731a09b35595a707c1a0e30d6dc70b Mon Sep 17 00:00:00 2001 From: songsong <353833373@qq.com> Date: Thu, 27 Feb 2025 11:52:32 +0800 Subject: [PATCH 48/54] refactor: picker (#2990) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 初始化pickerview * feat: add pickerView * feat: add tiled demo * refactor: 重构h5 * fix: 适配demo * feat: 增加级联功能 * fix: 级联优化 * feat: pickerview增加级联效果 * fix: 更新taro版本 * fix: 移除冗余代码+解决form中滚动未结束死循环问题 * fix: 移除console * fix: 更新pickerview * fix: update test * fix: update error * fix: update error * fix: update demo * fix: update test * feat: 添加异步demo * fix: update doc * fix: 同步pickerview变更 * fix: 解决小程序控制条样式问题+demo7取消不还原问题 * fix: datepicker类型同步更新 * fix: update test * fix: 修改标签 * feat: add test * fix: update doc and test * fix: adjust the dependency package location * fix: adjust the dependency package path * fix: adjust the dependency package path * fix: adjust the dependency package path * fix: update doc props type * fix: adjust note * fix: adjust the dependency package path * fix: optimize details * fix: update test * fix: path not compilable * fix: key error * fix: update datepicker test * fix: hidden entry * fix: modify config * fix: remove repeat config * fix: 修改组件引入路径 * fix: ts类型和单测修正 * fix: 弹窗动效还原 * fix: remove console --- src/config.json | 6 +- .../datepicker/__test__/datepicker.spec.tsx | 32 +- src/packages/datepicker/datepicker.taro.tsx | 48 ++- src/packages/datepicker/datepicker.tsx | 49 ++- src/packages/datepicker/demos/h5/demo1.tsx | 18 +- src/packages/datepicker/demos/h5/demo2.tsx | 12 +- src/packages/datepicker/demos/h5/demo3.tsx | 9 +- src/packages/datepicker/demos/h5/demo4.tsx | 11 +- src/packages/datepicker/demos/h5/demo5.tsx | 11 +- src/packages/datepicker/demos/h5/demo6.tsx | 26 +- src/packages/datepicker/demos/h5/demo7.tsx | 11 +- src/packages/datepicker/demos/h5/demo8.tsx | 24 +- src/packages/datepicker/demos/taro/demo1.tsx | 16 +- src/packages/datepicker/demos/taro/demo2.tsx | 11 +- src/packages/datepicker/demos/taro/demo3.tsx | 9 +- src/packages/datepicker/demos/taro/demo4.tsx | 11 +- src/packages/datepicker/demos/taro/demo5.tsx | 11 +- src/packages/datepicker/demos/taro/demo6.tsx | 26 +- src/packages/datepicker/demos/taro/demo7.tsx | 11 +- src/packages/datepicker/demos/taro/demo8.tsx | 24 +- src/packages/form/demos/h5/demo7.tsx | 17 +- src/packages/form/demos/taro/demo7.tsx | 16 +- src/packages/picker/__tests__/picker.spec.tsx | 128 +++--- src/packages/picker/demos/h5/demo1.tsx | 58 +-- src/packages/picker/demos/h5/demo2.tsx | 48 +-- src/packages/picker/demos/h5/demo3.tsx | 77 ++-- src/packages/picker/demos/h5/demo4.tsx | 53 ++- src/packages/picker/demos/h5/demo5.tsx | 43 +- src/packages/picker/demos/h5/demo6.tsx | 177 ++++---- src/packages/picker/demos/h5/demo7.tsx | 161 +++---- src/packages/picker/demos/h5/demo8.tsx | 63 ++- src/packages/picker/demos/taro/demo1.tsx | 58 +-- src/packages/picker/demos/taro/demo2.tsx | 53 ++- src/packages/picker/demos/taro/demo3.tsx | 82 ++-- src/packages/picker/demos/taro/demo4.tsx | 62 ++- src/packages/picker/demos/taro/demo5.tsx | 52 +-- src/packages/picker/demos/taro/demo6.tsx | 177 ++++---- src/packages/picker/demos/taro/demo7.tsx | 161 +++---- src/packages/picker/demos/taro/demo8.tsx | 63 ++- src/packages/picker/index.taro.ts | 1 - src/packages/picker/index.ts | 1 - src/packages/picker/picker.scss | 135 +----- src/packages/picker/picker.taro.tsx | 394 ++++-------------- src/packages/picker/picker.tsx | 313 +++++--------- src/packages/picker/pickerpanel.taro.tsx | 322 -------------- src/packages/picker/pickerpanel.tsx | 305 -------------- src/packages/picker/types.ts | 11 +- .../pickerview/__test__/pickerview.spec.tsx | 2 +- src/packages/pickerview/doc.en-US.md | 4 +- src/packages/pickerview/doc.md | 4 +- src/packages/pickerview/doc.taro.md | 4 +- src/packages/pickerview/doc.zh-TW.md | 4 +- src/packages/pickerview/index.taro.ts | 2 +- src/packages/pickerview/index.ts | 2 +- src/packages/pickerview/pickerroller.taro.tsx | 12 +- src/packages/pickerview/pickerroller.tsx | 12 +- src/packages/pickerview/pickerview.taro.tsx | 16 +- src/packages/pickerview/pickerview.tsx | 16 +- src/packages/pickerview/types.ts | 16 +- 59 files changed, 1246 insertions(+), 2255 deletions(-) delete mode 100644 src/packages/picker/pickerpanel.taro.tsx delete mode 100644 src/packages/picker/pickerpanel.tsx diff --git a/src/config.json b/src/config.json index 70976e92f3..ac1ac996d7 100644 --- a/src/config.json +++ b/src/config.json @@ -688,8 +688,8 @@ "sort": 10, "show": true, "taro": true, - "author": "dsj", - "dd": false + "dd": false, + "author": "songsong" }, { "version": "3.0.0", @@ -1456,4 +1456,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/packages/datepicker/__test__/datepicker.spec.tsx b/src/packages/datepicker/__test__/datepicker.spec.tsx index 2bfbba3dcc..26a75e4ae9 100644 --- a/src/packages/datepicker/__test__/datepicker.spec.tsx +++ b/src/packages/datepicker/__test__/datepicker.spec.tsx @@ -21,7 +21,7 @@ test('Show Chinese', async () => { fireEvent.click(confirmBtn) await waitFor(() => { expect( - confirm.mock.calls[0][0].map((option: any) => option.text).join('') + confirm.mock.calls[0][0].map((option: any) => option.label).join('') ).toEqual(`${currentYear - 10}年01月01日`) }) }) @@ -40,13 +40,9 @@ test('Min date & Max date', async () => { /> ) - const columns = container.querySelectorAll('.nut-picker-list')[0] - const lists = columns.querySelectorAll('.nut-picker-roller-item-title') - const years = ['2020', '2021', '2022'] + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[0].querySelectorAll('.nut-pickerview-roller-item-tiled') expect(lists.length).toBe(3) - lists.forEach((list, i) => { - expect(list.textContent).toEqual(years[i]) - }) rerender( { const formatter = (type: string, option: any) => { switch (type) { case 'year': - option.text += '' + option.label += '' break case 'month': - option.text += 'M' + option.label += 'M' break case 'day': - option.text += 'D' + option.label += 'D' break case 'hour': - option.text += 'H' + option.label += 'H' break case 'minute': - option.text += 'M' + option.label += 'M' break default: - option.text += '' + option.label += '' } return option } @@ -148,7 +144,7 @@ test('should pick defaultValue', async () => { fireEvent.click(confirmBtn) await waitFor(() => expect( - confirm.mock.calls[0][0].map((option: any) => option.text).join('') + confirm.mock.calls[0][0].map((option: any) => option.label).join('') ).toEqual('20210301') ) }) @@ -166,8 +162,8 @@ test('Increment step setting', async () => { /> ) - const columns = container.querySelectorAll('.nut-picker-list')[1] - const lists = columns.querySelectorAll('.nut-picker-roller-item') + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[1].querySelectorAll('.nut-pickerview-roller-item') expect(lists.length).toBe(12) }) @@ -189,7 +185,7 @@ test('Filter Time', async () => { /> ) - const columns = container.querySelectorAll('.nut-picker-list')[3] - const lists = columns.querySelectorAll('.nut-picker-roller-item') + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[3].querySelectorAll('.nut-pickerview-roller-item') expect(lists.length).toBe(4) }) diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 702f1e4e07..3a6719d316 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -1,11 +1,16 @@ import React, { FunctionComponent, useState, useEffect } from 'react' import { View } from '@tarojs/components' -import Picker, { PickerOption, PickerProps } from '@/packages/picker/index.taro' +import Picker, { PickerProps } from '@/packages/picker/index.taro' import { useConfig } from '@/packages/configprovider/index.taro' import { usePropsValue } from '@/hooks/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' import { isDate } from '@/utils/is-date' import { padZero } from '@/utils/pad-zero' +import { + PickerOption, + PickerOptions, + PickerValue, +} from '@/packages/pickerview/types' export interface DatePickerProps extends BasicComponent { value?: Date @@ -39,16 +44,16 @@ export interface DatePickerProps extends BasicComponent { > > formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] + filter: (type: string, options: PickerOptions) => PickerOptions onClose: () => void onCancel: () => void onConfirm: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedOptions: PickerOptions, + selectedValue: PickerValue[], columnIndex: number ) => void } @@ -104,8 +109,8 @@ export const DatePicker: FunctionComponent< minute: lang.min, seconds: lang.seconds, } - const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) - const [pickerOptions, setPickerOptions] = useState([]) + const [pickerValue, setPickerValue] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) const formatValue = (value: Date | null) => { if (!value || (value && !isDate(value))) { value = startDate @@ -218,7 +223,7 @@ export const DatePicker: FunctionComponent< const compareDateChange = ( currentDate: number, newDate: Date | null, - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, index: number ) => { const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() @@ -238,8 +243,8 @@ export const DatePicker: FunctionComponent< } } const handlePickerChange = ( - selectedOptions: PickerOption[], - selectedValue: (number | string)[], + selectedOptions: PickerOptions, + selectedValue: PickerValue[], index: number ) => { const rangeType = type.toLocaleLowerCase() @@ -248,7 +253,7 @@ export const DatePicker: FunctionComponent< rangeType ) ) { - const formatDate: (number | string)[] = [] + const formatDate: PickerValue[] = [] selectedValue.forEach((item) => { formatDate.push(item) }) @@ -313,13 +318,13 @@ export const DatePicker: FunctionComponent< const formatOption = (type: string, value: string | number) => { if (formatter) { return formatter(type, { - text: padZero(value, 2), + label: padZero(value, 2), value: padZero(value, 2), }) } const padMin = padZero(value, 2) const fatter = showChinese ? zhCNType[type] : '' - return { text: padMin + fatter, value: padMin } + return { label: padMin + fatter, value: padMin } } const generateColumn = ( @@ -412,14 +417,13 @@ export const DatePicker: FunctionComponent< onClose={onClose} onCancel={onCancel} value={pickerValue} - onConfirm={(options: PickerOption[], value: (string | number)[]) => - onConfirm && onConfirm(options, value) - } - onChange={( - options: PickerOption[], - value: (number | string)[], - index: number - ) => handlePickerChange(options, value, index)} + onConfirm={( + selectedOptions: PickerOptions, + selectedValue: PickerValue[] + ) => onConfirm && onConfirm(selectedOptions, selectedValue)} + onChange={({ value, index, selectedOptions }) => { + handlePickerChange(selectedOptions, value, index) + }} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 47011cdc27..91f53c4780 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -1,11 +1,15 @@ import React, { FunctionComponent, useState, useEffect } from 'react' -import Picker from '@/packages/picker' -import { PickerOption, PickerProps } from '@/packages/picker/index' +import Picker, { PickerProps } from '@/packages/picker/index' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/hooks/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' import { isDate } from '@/utils/is-date' import { padZero } from '@/utils/pad-zero' +import { + PickerOption, + PickerOptions, + PickerValue, +} from '@/packages/pickerview/types' export interface DatePickerProps extends BasicComponent { value?: Date @@ -39,16 +43,16 @@ export interface DatePickerProps extends BasicComponent { > > formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] + filter: (type: string, options: PickerOptions) => PickerOptions onClose: () => void onCancel: () => void onConfirm: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedOptions: PickerOptions, + selectedValue: PickerValue[], columnIndex: number ) => void } @@ -104,8 +108,8 @@ export const DatePicker: FunctionComponent< minute: lang.min, seconds: lang.seconds, } - const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) - const [pickerOptions, setPickerOptions] = useState([]) + const [pickerValue, setPickerValue] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) const formatValue = (value: Date | null) => { if (!value || (value && !isDate(value))) { value = startDate @@ -218,7 +222,7 @@ export const DatePicker: FunctionComponent< const compareDateChange = ( currentDate: number, newDate: Date | null, - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, index: number ) => { const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() @@ -238,8 +242,8 @@ export const DatePicker: FunctionComponent< } } const handlePickerChange = ( - selectedOptions: PickerOption[], - selectedValue: (number | string)[], + selectedOptions: PickerOptions, + selectedValue: PickerValue[], index: number ) => { const rangeType = type.toLocaleLowerCase() @@ -248,7 +252,7 @@ export const DatePicker: FunctionComponent< rangeType ) ) { - const formatDate: (number | string)[] = [] + const formatDate: PickerValue[] = [] selectedValue.forEach((item) => { formatDate.push(item) }) @@ -313,13 +317,13 @@ export const DatePicker: FunctionComponent< const formatOption = (type: string, value: string | number) => { if (formatter) { return formatter(type, { - text: padZero(value, 2), + label: padZero(value, 2), value: padZero(value, 2), }) } const padMin = padZero(value, 2) const fatter = showChinese ? zhCNType[type] : '' - return { text: padMin + fatter, value: padMin } + return { label: padMin + fatter, value: padMin } } const generateColumn = ( @@ -407,14 +411,13 @@ export const DatePicker: FunctionComponent< onClose={onClose} onCancel={onCancel} value={pickerValue} - onConfirm={(options: PickerOption[], value: (string | number)[]) => - onConfirm && onConfirm(options, value) - } - onChange={( - options: PickerOption[], - value: (number | string)[], - index: number - ) => handlePickerChange(options, value, index)} + onConfirm={( + selectedOptions: PickerOptions, + selectedValue: PickerValue[] + ) => onConfirm && onConfirm(selectedOptions, selectedValue)} + onChange={({ value, index, selectedOptions }) => { + handlePickerChange(selectedOptions, value, index) + }} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index f227a06824..769885abf8 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react' const Demo1 = () => { const defaultValue = new Date() @@ -12,13 +17,14 @@ const Demo1 = () => { const [value, setValue] = useState('2023/01/01') const [show2, setShow2] = useState(false) const [desc2, setDesc2] = useState('') - const confirm1 = (values: (string | number)[], options: PickerOption[]) => { - setDesc1(options.map((option) => option.text).join(' ')) + const confirm1 = (values: PickerValue[], options: PickerOptions) => { + setDesc1(options.map((option) => option.label).join(' ')) } - const change = (options: PickerOption[], values: (string | number)[]) => { + const change = (options: PickerOptions, values: PickerValue[]) => { const v = values.join('/') + setValue(v) - setDesc2(options.map((option) => option.text).join(' ')) + setDesc2(options.map((option) => option.label).join(' ')) } return ( <> @@ -39,7 +45,7 @@ const Demo1 = () => { onConfirm={(options, values) => { setShow1(false) confirm1(values, options) - console.log('onconfirm') + console.log('onconfirm', values, options) }} /> { const defaultValue = new Date() @@ -8,8 +13,9 @@ const Demo2 = () => { `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` ) - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join('-')) + const confirm = (values: PickerValue[], options: PickerOptions) => { + console.log('values', values, options) + setDesc(options.map((option) => option.label).join('-')) } return ( <> diff --git a/src/packages/datepicker/demos/h5/demo3.tsx b/src/packages/datepicker/demos/h5/demo3.tsx index 1143b67773..bf0cce9a2e 100644 --- a/src/packages/datepicker/demos/h5/demo3.tsx +++ b/src/packages/datepicker/demos/h5/demo3.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react' const Demo3 = () => { const defaultValue = new Date() @@ -11,7 +16,7 @@ const Demo3 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 11:08`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { const date = values.slice(0, 3).join('-') const time = values.slice(3).join(':') setDesc(`${date} ${time}`) diff --git a/src/packages/datepicker/demos/h5/demo4.tsx b/src/packages/datepicker/demos/h5/demo4.tsx index fefb4b317d..7e01fa6996 100644 --- a/src/packages/datepicker/demos/h5/demo4.tsx +++ b/src/packages/datepicker/demos/h5/demo4.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react' const Demo4 = () => { const defaultValue = new Date() @@ -10,8 +15,8 @@ const Demo4 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/h5/demo5.tsx b/src/packages/datepicker/demos/h5/demo5.tsx index 0ab54a263e..e396a55a9f 100644 --- a/src/packages/datepicker/demos/h5/demo5.tsx +++ b/src/packages/datepicker/demos/h5/demo5.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react' const Demo5 = () => { const defaultValue = new Date() @@ -10,8 +15,8 @@ const Demo5 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10') - const confirm8 = (options: PickerOption[], values: (string | number)[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm8 = (options: PickerOptions, values: PickerValue[]) => { + setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/h5/demo6.tsx b/src/packages/datepicker/demos/h5/demo6.tsx index 09e831821c..21350012de 100644 --- a/src/packages/datepicker/demos/h5/demo6.tsx +++ b/src/packages/datepicker/demos/h5/demo6.tsx @@ -1,5 +1,11 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOption, + PickerOptions, +} from '@nutui/nutui-react' const Demo6 = () => { const defaultValue = new Date() @@ -9,36 +15,36 @@ const Demo6 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 10:10`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { const date = options .slice(1, 3) - .map((op) => op.text) + .map((op) => op.label) .join('') const time = options .slice(3) .map((op) => op.value) .join(':') - setDesc(`${options[0].text}年${date} ${time}`) + setDesc(`${options[0].label}年${date} ${time}`) } const formatter = (type: string, option: PickerOption) => { switch (type) { case 'year': - option.text += '' + option.label += '' break case 'month': - option.text += '月' + option.label += '月' break case 'day': - option.text += '日' + option.label += '日' break case 'hour': - option.text += '时' + option.label += '时' break case 'minute': - option.text += '分' + option.label += '分' break default: - option.text += '' + option.label += '' } return option } diff --git a/src/packages/datepicker/demos/h5/demo7.tsx b/src/packages/datepicker/demos/h5/demo7.tsx index a447fa27f5..ba3b8f9366 100644 --- a/src/packages/datepicker/demos/h5/demo7.tsx +++ b/src/packages/datepicker/demos/h5/demo7.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react' const Demo7 = () => { const defaultValue = new Date() @@ -11,8 +16,8 @@ const Demo7 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm6 = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm6 = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join(':')) } return ( <> diff --git a/src/packages/datepicker/demos/h5/demo8.tsx b/src/packages/datepicker/demos/h5/demo8.tsx index a5cce7f3eb..79886c8d19 100644 --- a/src/packages/datepicker/demos/h5/demo8.tsx +++ b/src/packages/datepicker/demos/h5/demo8.tsx @@ -1,5 +1,11 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePicker, + Cell, + PickerValue, + PickerOption, + PickerOptions, +} from '@nutui/nutui-react' const Demo8 = () => { const startDate = new Date(2020, 0, 1) @@ -11,10 +17,10 @@ const Demo8 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 00`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(' ')) + const confirm = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join(' ')) } - const filter = (type: string, options: PickerOption[]) => { + const filter = (type: string, options: PickerOptions) => { if (type === 'hour') { return options.filter((option) => Number(option.value) % 6 === 0) } @@ -23,19 +29,19 @@ const Demo8 = () => { const formatter1 = (type: string, option: PickerOption) => { switch (type) { case 'year': - option.text += `年` + option.label += `年` break case 'month': - option.text += `月` + option.label += `月` break case 'day': - option.text += `日` + option.label += `日` break case 'hour': - option.text += `时` + option.label += `时` break default: - option.text += '' + option.label += '' } return option } diff --git a/src/packages/datepicker/demos/taro/demo1.tsx b/src/packages/datepicker/demos/taro/demo1.tsx index dadd406cb0..1a9c273446 100644 --- a/src/packages/datepicker/demos/taro/demo1.tsx +++ b/src/packages/datepicker/demos/taro/demo1.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react-taro' const Demo1 = () => { const defaultValue = new Date() @@ -12,13 +17,14 @@ const Demo1 = () => { const [value, setValue] = useState('2023/01/01') const [show2, setShow2] = useState(false) const [desc2, setDesc2] = useState('') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc1(options.map((option) => option.text).join(' ')) + const confirm = (values: PickerValue[], options: PickerOptions) => { + setDesc1(options.map((option) => option.label).join(' ')) } - const change = (options: PickerOption[], values: (string | number)[]) => { + const change = (options: PickerOptions, values: PickerValue[]) => { const v = values.join('/') + setValue(v) - setDesc2(options.map((option) => option.text).join(' ')) + setDesc2(options.map((option) => option.label).join(' ')) } return ( <> diff --git a/src/packages/datepicker/demos/taro/demo2.tsx b/src/packages/datepicker/demos/taro/demo2.tsx index 7d3b42edaf..9e57427a40 100644 --- a/src/packages/datepicker/demos/taro/demo2.tsx +++ b/src/packages/datepicker/demos/taro/demo2.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' const Demo2 = () => { const defaultValue = new Date() @@ -7,8 +12,8 @@ const Demo2 = () => { const [desc, setDesc] = useState( `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` ) - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join('-')) + const confirm = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join('-')) } return ( <> diff --git a/src/packages/datepicker/demos/taro/demo3.tsx b/src/packages/datepicker/demos/taro/demo3.tsx index 0334291b40..b4dedc010f 100644 --- a/src/packages/datepicker/demos/taro/demo3.tsx +++ b/src/packages/datepicker/demos/taro/demo3.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react-taro' const Demo3 = () => { const defaultValue = new Date() @@ -11,7 +16,7 @@ const Demo3 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 11:08`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { const date = values.slice(0, 3).join('-') const time = values.slice(3).join(':') setDesc(`${date} ${time}`) diff --git a/src/packages/datepicker/demos/taro/demo4.tsx b/src/packages/datepicker/demos/taro/demo4.tsx index 99f4ca3a55..00e85593b7 100644 --- a/src/packages/datepicker/demos/taro/demo4.tsx +++ b/src/packages/datepicker/demos/taro/demo4.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react-taro' const Demo4 = () => { const defaultValue = new Date() @@ -10,8 +15,8 @@ const Demo4 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/taro/demo5.tsx b/src/packages/datepicker/demos/taro/demo5.tsx index db834280b3..0386bc9c61 100644 --- a/src/packages/datepicker/demos/taro/demo5.tsx +++ b/src/packages/datepicker/demos/taro/demo5.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react-taro' const Demo5 = () => { const defaultValue = new Date() @@ -10,8 +15,8 @@ const Demo5 = () => { const endDate = new Date(2025, 10, 1) const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10') - const confirm = (options: PickerOption[], values: (string | number)[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm = (options: PickerOptions, values: PickerValue[]) => { + setDesc(options.map((option) => option.label).join(':')) } return ( diff --git a/src/packages/datepicker/demos/taro/demo6.tsx b/src/packages/datepicker/demos/taro/demo6.tsx index 037c396eec..9aa1371946 100644 --- a/src/packages/datepicker/demos/taro/demo6.tsx +++ b/src/packages/datepicker/demos/taro/demo6.tsx @@ -1,5 +1,11 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOption, + PickerOptions, +} from '@nutui/nutui-react-taro' const Demo6 = () => { const defaultValue = new Date() @@ -9,36 +15,36 @@ const Demo6 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 10:10`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { const date = options .slice(1, 3) - .map((op) => op.text) + .map((op) => op.label) .join('') const time = options .slice(3) .map((op) => op.value) .join(':') - setDesc(`${options[0].text}年${date} ${time}`) + setDesc(`${options[0].label}年${date} ${time}`) } const formatter = (type: string, option: PickerOption) => { switch (type) { case 'year': - option.text += '' + option.label += '' break case 'month': - option.text += '月' + option.label += '月' break case 'day': - option.text += '日' + option.label += '日' break case 'hour': - option.text += '时' + option.label += '时' break case 'minute': - option.text += '分' + option.label += '分' break default: - option.text += '' + option.label += '' } return option } diff --git a/src/packages/datepicker/demos/taro/demo7.tsx b/src/packages/datepicker/demos/taro/demo7.tsx index 24702e28bf..6f7210ac00 100644 --- a/src/packages/datepicker/demos/taro/demo7.tsx +++ b/src/packages/datepicker/demos/taro/demo7.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react-taro' const Demo7 = () => { const defaultValue = new Date() @@ -11,8 +16,8 @@ const Demo7 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState('10:10:00') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(':')) + const confirm = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join(':')) } return ( <> diff --git a/src/packages/datepicker/demos/taro/demo8.tsx b/src/packages/datepicker/demos/taro/demo8.tsx index 31911a0f5a..42441084f5 100644 --- a/src/packages/datepicker/demos/taro/demo8.tsx +++ b/src/packages/datepicker/demos/taro/demo8.tsx @@ -1,5 +1,11 @@ import React, { useState } from 'react' -import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import { + DatePicker, + Cell, + PickerValue, + PickerOption, + PickerOptions, +} from '@nutui/nutui-react-taro' const Demo8 = () => { const startDate = new Date(2020, 0, 1) @@ -11,10 +17,10 @@ const Demo8 = () => { const [show, setShow] = useState(false) const [desc, setDesc] = useState(`${defaultDescription} 00`) - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc(options.map((option) => option.text).join(' ')) + const confirm = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join(' ')) } - const filter = (type: string, options: PickerOption[]) => { + const filter = (type: string, options: PickerOptions) => { if (type === 'hour') { return options.filter((option) => Number(option.value) % 6 === 0) } @@ -23,19 +29,19 @@ const Demo8 = () => { const formatter = (type: string, option: PickerOption) => { switch (type) { case 'year': - option.text += `年` + option.label += `年` break case 'month': - option.text += `月` + option.label += `月` break case 'day': - option.text += `日` + option.label += `日` break case 'hour': - option.text += `时` + option.label += `时` break default: - option.text += '' + option.label += '' } return option } diff --git a/src/packages/form/demos/h5/demo7.tsx b/src/packages/form/demos/h5/demo7.tsx index f0387f47a7..8ba20b1cd3 100644 --- a/src/packages/form/demos/h5/demo7.tsx +++ b/src/packages/form/demos/h5/demo7.tsx @@ -17,14 +17,15 @@ import { ArrowRight } from '@nutui/icons-react' const Demo7 = () => { const pickerOptions = [ - { value: 4, text: 'BeiJing' }, - { value: 1, text: 'NanJing' }, - { value: 2, text: 'WuXi' }, - { value: 8, text: 'DaQing' }, - { value: 9, text: 'SuiHua' }, - { value: 10, text: 'WeiFang' }, - { value: 12, text: 'ShiJiaZhuang' }, + { value: 1, label: 'BeiJing' }, + { value: 2, label: 'NanJing' }, + { value: 3, label: 'WuXi' }, + { value: 4, label: 'DaQing' }, + { value: 5, label: 'SuiHua' }, + { value: 6, label: 'WeiFang' }, + { value: 7, label: 'ShiJiaZhuang' }, ] + const submitFailed = (error: any) => { Toast.show({ content: JSON.stringify(error), icon: 'fail' }) } @@ -106,7 +107,7 @@ const Demo7 = () => { title={ value.length ? pickerOptions.filter((po) => po.value === value[0])[0] - ?.text + ?.label : 'Please select' } extra={} diff --git a/src/packages/form/demos/taro/demo7.tsx b/src/packages/form/demos/taro/demo7.tsx index bee2dae660..97713f58e1 100644 --- a/src/packages/form/demos/taro/demo7.tsx +++ b/src/packages/form/demos/taro/demo7.tsx @@ -18,13 +18,13 @@ import { View } from '@tarojs/components' const Demo7 = () => { const pickerOptions = [ - { value: 4, text: 'BeiJing' }, - { value: 1, text: 'NanJing' }, - { value: 2, text: 'WuXi' }, - { value: 8, text: 'DaQing' }, - { value: 9, text: 'SuiHua' }, - { value: 10, text: 'WeiFang' }, - { value: 12, text: 'ShiJiaZhuang' }, + { value: 1, label: 'BeiJing' }, + { value: 2, label: 'NanJing' }, + { value: 3, label: 'WuXi' }, + { value: 4, label: 'DaQing' }, + { value: 5, label: 'SuiHua' }, + { value: 6, label: 'WeiFang' }, + { value: 7, label: 'ShiJiaZhuang' }, ] const submitFailed = (error: any) => { Taro.showToast({ title: JSON.stringify(error), icon: 'error' }) @@ -107,7 +107,7 @@ const Demo7 = () => { title={ value.length ? pickerOptions.filter((po) => po.value === value[0])[0] - ?.text + ?.label : 'Please select' } extra={} diff --git a/src/packages/picker/__tests__/picker.spec.tsx b/src/packages/picker/__tests__/picker.spec.tsx index 0c08207240..60fe500538 100644 --- a/src/packages/picker/__tests__/picker.spec.tsx +++ b/src/packages/picker/__tests__/picker.spec.tsx @@ -10,7 +10,7 @@ function sleep(delay = 0): Promise { } interface PickerOption { - text: string | number + label: string | number value: string | number disabled?: boolean children?: PickerOption[] @@ -18,72 +18,76 @@ interface PickerOption { } const simpleColumns = [ - { text: '南京市', value: 'NanJing' }, - { text: '无锡市', value: 'WuXi' }, - { text: '海北藏族自治区', value: 'ZangZu' }, - { text: '北京市', value: 'BeiJing' }, - { text: '连云港市', value: 'LianYunGang' }, + [ + { label: '南京市', value: 'NanJing' }, + { label: '无锡市', value: 'WuXi' }, + { label: '海北藏族自治区', value: 'ZangZu' }, + { label: '北京市', value: 'BeiJing' }, + { label: '连云港市', value: 'LianYunGang' }, + ], ] const multipleColumns = [ [ - { text: '周一', value: 'Monday' }, - { text: '周二', value: 'Tuesday' }, - { text: '周三', value: 'Wednesday' }, - { text: '周四', value: 'Thursday' }, - { text: '周五', value: 'Friday' }, + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, ], // 第二列 [ - { text: '上午', value: 'Morning' }, - { text: '下午', value: 'Afternoon' }, - { text: '晚上', value: 'Evening' }, + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, ], ] const multistageColumns = [ - { - text: '浙江', - value: 'ZheJiang', - children: [ - { - text: '杭州', - value: 'HangZhou', - children: [ - { text: '西湖区', value: 'XiHu' }, - { text: '余杭区', value: 'YuHang' }, - ], - }, - { - text: '温州', - value: 'WenZhou', - children: [ - { text: '鹿城区', value: 'LuCheng' }, - { text: '瓯海区', value: 'OuHai' }, - ], - }, - ], - }, - { - text: '福建', - value: 'FuJian', - children: [ - { - text: '福州', - value: 'FuZhou', - children: [ - { text: '鼓楼区', value: 'GuLou' }, - { text: '台江区', value: 'TaiJiang' }, - ], - }, - { - text: '厦门', - value: 'XiaMen', - children: [ - { text: '思明区', value: 'SiMing' }, - { text: '海沧区', value: 'HaiCang' }, - ], - }, - ], - }, + [ + { + label: '浙江', + value: 'ZheJiang', + children: [ + { + label: '杭州', + value: 'HangZhou', + children: [ + { label: '西湖区', value: 'XiHu' }, + { label: '余杭区', value: 'YuHang' }, + ], + }, + { + label: '温州', + value: 'WenZhou', + children: [ + { label: '鹿城区', value: 'LuCheng' }, + { label: '瓯海区', value: 'OuHai' }, + ], + }, + ], + }, + { + label: '福建', + value: 'FuJian', + children: [ + { + label: '福州', + value: 'FuZhou', + children: [ + { label: '鼓楼区', value: 'GuLou' }, + { label: '台江区', value: 'TaiJiang' }, + ], + }, + { + label: '厦门', + value: 'XiaMen', + children: [ + { label: '思明区', value: 'SiMing' }, + { label: '海沧区', value: 'HaiCang' }, + ], + }, + ], + }, + ], ] test('renderLabel works', async () => { @@ -98,8 +102,9 @@ test('simple list-data confirm event', async () => { const { container } = render( confirm(value)} + onConfirm={(selectedOptions, value) => confirm(value)} /> ) const confirmBtn = container.querySelectorAll('.nut-picker-confirm-btn')[0] @@ -137,6 +142,7 @@ test('multiple list-data render', async () => { const { container } = render( confirm(value)} /> @@ -154,6 +160,7 @@ test('multistageColumns list-data render', async () => { confirm(value)} /> ) @@ -167,7 +174,7 @@ test('multistageColumns list-data render', async () => { test('async list-data render', async () => { const confirm = vi.fn() const PenderContent = () => { - const [asyncColumns, setasyncColumns] = useState([]) + const [asyncColumns, setasyncColumns] = useState([]) setTimeout(() => { setasyncColumns(simpleColumns) @@ -176,6 +183,7 @@ test('async list-data render', async () => { return ( confirm(value)} /> diff --git a/src/packages/picker/demos/h5/demo1.tsx b/src/packages/picker/demos/h5/demo1.tsx index b0c5f799ba..480bf28554 100644 --- a/src/packages/picker/demos/h5/demo1.tsx +++ b/src/packages/picker/demos/h5/demo1.tsx @@ -1,40 +1,44 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' - -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOnChangeCallbackParameter, + PickerOption, +} from '@nutui/nutui-react' const Demo1 = () => { const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const listData1 = [ + const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] - const changePicker = (list: any[], option: any, columnIndex: number) => { - console.log(columnIndex, option) + const changePicker = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('changePicker', value, index, selectedOptions) } const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { + console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: PickerOption) => { + description += ` ${option.label}` }) setBaseDesc(description) } @@ -48,8 +52,8 @@ const Demo1 = () => { confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} onClose={() => setVisible(false)} onChange={changePicker} /> diff --git a/src/packages/picker/demos/h5/demo2.tsx b/src/packages/picker/demos/h5/demo2.tsx index 22b87f1203..643137b163 100644 --- a/src/packages/picker/demos/h5/demo2.tsx +++ b/src/packages/picker/demos/h5/demo2.tsx @@ -1,54 +1,46 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo2 = () => { const [visible, setVisible] = useState(false) - const [baseDefault, setbaseDefault] = useState('') + const [baseDesc, setBaseDesc] = useState('无锡市') const [defaultValue] = useState([2]) - - const listData1 = [ + const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: any) => { + description += ` ${option.label}` }) - setbaseDefault(description) + setBaseDesc(description) } return ( <> setVisible(!visible)} /> confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} onClose={() => setVisible(false)} /> diff --git a/src/packages/picker/demos/h5/demo3.tsx b/src/packages/picker/demos/h5/demo3.tsx index a4652af91d..81f59dcdfb 100644 --- a/src/packages/picker/demos/h5/demo3.tsx +++ b/src/packages/picker/demos/h5/demo3.tsx @@ -1,59 +1,64 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' +import isEqual from 'react-fast-compare' +import { PickerOnChangeCallbackParameter } from '@/packages/pickerview/types' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo3 = () => { - const [isVisible, setIsVisible] = useState(false) + const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const [val, setVal] = useState>([]) + const [value, setValue] = useState([] as PickerValue[]) const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] + + const changePicker = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('changePicker', value, index, selectedOptions) + } const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` - }) - setBaseDesc(description) + if (isEqual(selectedValue, [3])) { + setValue([1]) + setBaseDesc('南京市') + } else { + setValue(selectedValue) + let description = '' + selectedOptions.forEach((option: any) => { + description += ` ${option.label}` + }) + setBaseDesc(description) + } } return ( <> setIsVisible(!isVisible)} + onClick={() => setVisible(!visible)} /> { - confirmPicker(list, values) - setVal(values) - }} - onClose={() => { - setIsVisible(false) - }} + onChange={changePicker} + onConfirm={confirmPicker} + onClose={() => setVisible(false)} /> ) diff --git a/src/packages/picker/demos/h5/demo4.tsx b/src/packages/picker/demos/h5/demo4.tsx index b9594ffb6b..16192241fd 100644 --- a/src/packages/picker/demos/h5/demo4.tsx +++ b/src/packages/picker/demos/h5/demo4.tsx @@ -1,39 +1,34 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo4 = () => { - const [isVisible2, setIsVisible2] = useState(false) - const [mutilDesc, setMutilDesc] = useState('') - const listData2 = [ + const [visible, setVisible] = useState(false) + const [mutilDesc, setMutilDesc] = useState('周三') + const [defaultValue] = useState(['Wednesday']) + const options = [ // 第一列 [ - { text: '周一', value: 'Monday' }, - { text: '周二', value: 'Tuesday' }, - { text: '周三', value: 'Wednesday' }, - { text: '周四', value: 'Thursday' }, - { text: '周五', value: 'Friday' }, + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, ], // 第二列 [ - { text: '上午', value: 'Morning' }, - { text: '下午', value: 'Afternoon' }, - { text: '晚上', value: 'Evening' }, + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, ], ] const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { + console.log('confirmPicker', selectedOptions) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: any) => { + option?.label && (description += ` ${option.label}`) }) setMutilDesc(description) } @@ -42,14 +37,14 @@ const Demo4 = () => { setIsVisible2(!isVisible2)} + onClick={() => setVisible(!visible)} /> setIsVisible2(false)} - defaultValue={['Wednesday']} - onConfirm={(list, values) => confirmPicker(list, values)} + visible={visible} + options={options} + onClose={() => setVisible(false)} + defaultValue={defaultValue} + onConfirm={confirmPicker} /> ) diff --git a/src/packages/picker/demos/h5/demo5.tsx b/src/packages/picker/demos/h5/demo5.tsx index aeb6a2f1e0..73a72fd091 100644 --- a/src/packages/picker/demos/h5/demo5.tsx +++ b/src/packages/picker/demos/h5/demo5.tsx @@ -1,38 +1,27 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { Picker, Cell, PickerOptions, PickerValue } from '@nutui/nutui-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo5 = () => { - const [tileDesc, settileDesc] = useState('') const [isVisible, setIsVisible] = useState(false) - - const listData1 = [ + const [tileDesc, settileDesc] = useState('无锡市') + const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] - const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] - ) => { + const confirmPicker = (options: PickerOptions, values: PickerValue[]) => { let description = '' options.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) settileDesc(description) setIsVisible(false) @@ -46,8 +35,8 @@ const Demo5 = () => { /> confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} defaultValue={[2]} threeDimensional={false} duration={1000} diff --git a/src/packages/picker/demos/h5/demo6.tsx b/src/packages/picker/demos/h5/demo6.tsx index 007a1f8352..f39d06544a 100644 --- a/src/packages/picker/demos/h5/demo6.tsx +++ b/src/packages/picker/demos/h5/demo6.tsx @@ -1,105 +1,93 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOption, +} from '@nutui/nutui-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo6 = () => { const [isVisible, setIsVisible] = useState(false) + const [value, setValue] = useState([2]) + const [cityCustom, setCityCustom] = useState('上海') const customCityData = [ - { - value: 1, - text: '北京', - children: [ - { - value: 1, - text: '朝阳区', - }, - { - value: 2, - text: '海淀区', - }, - { - value: 3, - text: '大兴区', - }, - { - value: 4, - text: '东城区', - }, - { - value: 5, - text: '西城区', - }, - { - value: 6, - text: '丰台区', - }, - ], - }, - { - value: 2, - text: '上海', - children: [ - { - value: 1, - text: '黄埔区', - }, - { - value: 2, - text: '长宁区', - }, - { - value: 3, - text: '普陀区', - }, - { - value: 4, - text: '杨浦区', - }, - { - value: 5, - text: '浦东新区', - }, - ], - }, + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], ] - const [asyncData] = useState([ - { - value: 1, - text: '北京市', - children: [ - { value: 1, text: '朝阳区' }, - { value: 2, text: '海淀区' }, - { value: 3, text: '大兴区' }, - { value: 4, text: '东城区' }, - { value: 5, text: '西城区' }, - { value: 6, text: '丰台区' }, - ], - }, - { - value: 2, - text: '上海市', - children: [], - }, - ]) - const [cityCustom, setCityCustom] = useState('') const setChooseValueCustom = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - const str = options.map((item) => item.text).join('-') - setCityCustom(str) + console.log('onconfirm', selectedOptions, selectedValue) + const city = selectedOptions + .map((item: PickerOption) => item.label) + .join('-') + setCityCustom(city) + setValue(selectedValue) } + return ( <> setIsVisible(!isVisible)} /> @@ -107,13 +95,12 @@ const Demo6 = () => { setIsVisible(false)} - onConfirm={(list, values) => setChooseValueCustom(list, values)} - onChange={( - options: PickerOption[], - value: (string | number)[], - columnIndex: number - ) => console.log(asyncData, '多级联动', columnIndex, value, options)} + onConfirm={setChooseValueCustom} + onChange={({ value, index, selectedOptions }) => + console.log('多级联动', value, index, selectedOptions) + } /> ) diff --git a/src/packages/picker/demos/h5/demo7.tsx b/src/packages/picker/demos/h5/demo7.tsx index 1707e8c4aa..553359db48 100644 --- a/src/packages/picker/demos/h5/demo7.tsx +++ b/src/packages/picker/demos/h5/demo7.tsx @@ -1,77 +1,100 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react' +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOnChangeCallbackParameter, + PickerOption, +} from '@nutui/nutui-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo7 = () => { const [isVisible, setIsVisible] = useState(false) - const [asyncDesc, setasyncDesc] = useState('') + const [value, setValue] = useState([1]) + const [asyncDesc, setasyncDesc] = useState('北京') const [asyncData, setAsyncData] = useState([ - { - value: 1, - text: '北京市', - children: [ - { value: 1, text: '朝阳区' }, - { value: 2, text: '海淀区' }, - { value: 3, text: '大兴区' }, - { value: 4, text: '东城区' }, - { value: 5, text: '西城区' }, - { value: 6, text: '丰台区' }, - ], - }, - { - value: 2, - text: '上海市', - children: [], - }, + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [], + }, + ], ]) - const updateChooseValueCustmer = ( - options: PickerOption[], - values: (string | number)[], - columnIndex: number - ) => { - console.log('updateChooseValueCustmer', columnIndex, values, options) - if (columnIndex === 0 && values[0] === 2) { + const updateChooseValueCustmer = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + if (value[0] === 2 && asyncData[0]?.[1].children.length === 0) { + console.log('updateChooseValueCustmer', index, value, selectedOptions) setTimeout(() => { - if (asyncData[1].children.length === 0) { - asyncData[1].children = [ - { - value: 1, - text: '黄埔区', - }, - { - value: 2, - text: '长宁区', - }, - { - value: 3, - text: '普陀区', - }, - { - value: 4, - text: '杨浦区', - }, - { - value: 5, - text: '浦东新区', - }, - ] - setAsyncData([...asyncData]) - } - }, 100) + asyncData[0][1].children = [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ] + setAsyncData([...[...asyncData]]) + }, 0) } } const setAsyncConfirm = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - const str = options.map((item) => item.text).join('-') - setasyncDesc(str) + console.log('onconfirm', selectedOptions, selectedValue) + const city = selectedOptions + .map((item: PickerOption) => item.label) + .join('-') + setasyncDesc(city) + setValue(selectedValue) } return ( @@ -81,18 +104,14 @@ const Demo7 = () => { description={asyncDesc} onClick={() => setIsVisible(!isVisible)} /> + setIsVisible(false)} - onConfirm={(list, values) => setAsyncConfirm(list, values)} - onChange={( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => - updateChooseValueCustmer(selectedOptions, selectedValue, columnIndex) - } + onConfirm={setAsyncConfirm} + onChange={updateChooseValueCustmer} /> ) diff --git a/src/packages/picker/demos/h5/demo8.tsx b/src/packages/picker/demos/h5/demo8.tsx index 493b5dce06..4f16c7e357 100644 --- a/src/packages/picker/demos/h5/demo8.tsx +++ b/src/packages/picker/demos/h5/demo8.tsx @@ -1,50 +1,47 @@ import React, { useState } from 'react' -import { Picker, Cell, ConfigProvider } from '@nutui/nutui-react' +import { + Picker, + Cell, + ConfigProvider, + PickerOption, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo8 = () => { - const [isVisible, setIsVisible] = useState(false) + const [visible, setVisible] = useState(false) + const [baseDesc, setBaseDesc] = useState('') const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] - const [baseDesc, setBaseDesc] = useState('') - const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - console.log('demo 确定', options, values) + console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: PickerOption) => { + description += ` ${option.label}` }) setBaseDesc(description) - setIsVisible(false) } - return ( <> setIsVisible(!isVisible)} + onClick={() => setVisible(!visible)} /> { }} > confirmPicker(list, values)} - onClose={() => { - setIsVisible(false) - console.log('onclose') - }} + onConfirm={confirmPicker} + onClose={() => setVisible(false)} /> diff --git a/src/packages/picker/demos/taro/demo1.tsx b/src/packages/picker/demos/taro/demo1.tsx index f34f7003f3..94bbae0cdf 100644 --- a/src/packages/picker/demos/taro/demo1.tsx +++ b/src/packages/picker/demos/taro/demo1.tsx @@ -1,40 +1,44 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' - -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOnChangeCallbackParameter, + PickerOption, +} from '@nutui/nutui-react-taro' const Demo1 = () => { const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const listData1 = [ + const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] - const changePicker = (list: any[], option: any, columnIndex: number) => { - console.log(columnIndex, option) + const changePicker = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('changePicker', value, index, selectedOptions) } const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { + console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: PickerOption) => { + description += ` ${option.label}` }) setBaseDesc(description) } @@ -48,8 +52,8 @@ const Demo1 = () => { confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} onClose={() => setVisible(false)} onChange={changePicker} /> diff --git a/src/packages/picker/demos/taro/demo2.tsx b/src/packages/picker/demos/taro/demo2.tsx index ca6a3d4c09..f4fe925b36 100644 --- a/src/packages/picker/demos/taro/demo2.tsx +++ b/src/packages/picker/demos/taro/demo2.tsx @@ -1,54 +1,51 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo2 = () => { const [visible, setVisible] = useState(false) - const [baseDefault, setbaseDefault] = useState('') + const [baseDesc, setBaseDesc] = useState('无锡市') const [defaultValue] = useState([2]) - - const listData1 = [ + const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: any) => { + description += ` ${option.label}` }) - setbaseDefault(description) + setBaseDesc(description) } return ( <> setVisible(!visible)} /> confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} onClose={() => setVisible(false)} /> diff --git a/src/packages/picker/demos/taro/demo3.tsx b/src/packages/picker/demos/taro/demo3.tsx index f183117990..c0cee40ba1 100644 --- a/src/packages/picker/demos/taro/demo3.tsx +++ b/src/packages/picker/demos/taro/demo3.tsx @@ -1,59 +1,69 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' +import isEqual from 'react-fast-compare' +import { PickerOnChangeCallbackParameter } from '@/packages/pickerview/types' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo3 = () => { - const [isVisible, setIsVisible] = useState(false) + const [visible, setVisible] = useState(false) const [baseDesc, setBaseDesc] = useState('') - const [val, setVal] = useState>([]) + const [value, setValue] = useState([] as PickerValue[]) const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] + + const changePicker = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + console.log('changePicker', value, index, selectedOptions) + } const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` - }) - setBaseDesc(description) + if (isEqual(selectedValue, [3])) { + setValue([1]) + setBaseDesc('南京市') + } else { + setValue(selectedValue) + let description = '' + selectedOptions.forEach((option: any) => { + description += ` ${option.label}` + }) + setBaseDesc(description) + } } return ( <> setIsVisible(!isVisible)} + onClick={() => setVisible(!visible)} /> { - confirmPicker(list, values) - setVal(values) - }} - onClose={() => { - setIsVisible(false) - }} + onChange={changePicker} + onConfirm={confirmPicker} + onClose={() => setVisible(false)} /> ) diff --git a/src/packages/picker/demos/taro/demo4.tsx b/src/packages/picker/demos/taro/demo4.tsx index 79992790c3..ea0cfdfab9 100644 --- a/src/packages/picker/demos/taro/demo4.tsx +++ b/src/packages/picker/demos/taro/demo4.tsx @@ -1,59 +1,55 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo4 = () => { - const [isVisible2, setIsVisible2] = useState(false) - const [mutilDesc, setMutilDesc] = useState('') - const listData2 = [ + const [visible, setVisible] = useState(false) + const [mutilDesc, setMutilDesc] = useState('周三') + const [defaultValue] = useState(['Wednesday']) + const options = [ // 第一列 [ - { text: '周一', value: 'Monday' }, - { text: '周二', value: 'Tuesday' }, - { text: '周三', value: 'Wednesday' }, - { text: '周四', value: 'Thursday' }, - { text: '周五', value: 'Friday' }, + { label: '周一', value: 'Monday' }, + { label: '周二', value: 'Tuesday' }, + { label: '周三', value: 'Wednesday' }, + { label: '周四', value: 'Thursday' }, + { label: '周五', value: 'Friday' }, ], // 第二列 [ - { text: '上午', value: 'Morning' }, - { text: '下午', value: 'Afternoon' }, - { text: '晚上', value: 'Evening' }, + { label: '上午', value: 'Morning' }, + { label: '下午', value: 'Afternoon' }, + { label: '晚上', value: 'Evening' }, ], ] const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { + console.log('confirmPicker', selectedOptions) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: any) => { + option?.label && (description += ` ${option.label}`) }) setMutilDesc(description) } - const changePicker = (options: any[], values: any, columnIndex: number) => { - console.log('picker onChange', columnIndex, values, options) - } return ( <> setIsVisible2(!isVisible2)} + onClick={() => setVisible(!visible)} /> setIsVisible2(false)} - defaultValue={['Wednesday']} - onChange={changePicker} - onConfirm={(list, values) => confirmPicker(list, values)} + visible={visible} + options={options} + onClose={() => setVisible(false)} + defaultValue={defaultValue} + onConfirm={confirmPicker} /> ) diff --git a/src/packages/picker/demos/taro/demo5.tsx b/src/packages/picker/demos/taro/demo5.tsx index df1c6c3d36..cd4fcd0af9 100644 --- a/src/packages/picker/demos/taro/demo5.tsx +++ b/src/packages/picker/demos/taro/demo5.tsx @@ -1,45 +1,36 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo5 = () => { - const [tileDesc, settileDesc] = useState('') const [isVisible, setIsVisible] = useState(false) - - const listData1 = [ + const [tileDesc, settileDesc] = useState('无锡市') + const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] - const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] - ) => { + const confirmPicker = (options: PickerOptions, values: PickerValue[]) => { let description = '' options.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) settileDesc(description) setIsVisible(false) } - const changePicker = (options: any[], values: any, columnIndex: number) => { - console.log('picker onChange', columnIndex, values, options) - } return ( <> { /> confirmPicker(list, values)} + options={options} + onConfirm={confirmPicker} defaultValue={[2]} threeDimensional={false} duration={1000} onClose={() => setIsVisible(false)} - onChange={changePicker} /> ) diff --git a/src/packages/picker/demos/taro/demo6.tsx b/src/packages/picker/demos/taro/demo6.tsx index 3c3e880622..64f6bf4ba9 100644 --- a/src/packages/picker/demos/taro/demo6.tsx +++ b/src/packages/picker/demos/taro/demo6.tsx @@ -1,105 +1,93 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOption, +} from '@nutui/nutui-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo6 = () => { const [isVisible, setIsVisible] = useState(false) + const [value, setValue] = useState([2]) + const [cityCustom, setCityCustom] = useState('上海') const customCityData = [ - { - value: 1, - text: '北京', - children: [ - { - value: 1, - text: '朝阳区', - }, - { - value: 2, - text: '海淀区', - }, - { - value: 3, - text: '大兴区', - }, - { - value: 4, - text: '东城区', - }, - { - value: 5, - text: '西城区', - }, - { - value: 6, - text: '丰台区', - }, - ], - }, - { - value: 2, - text: '上海', - children: [ - { - value: 1, - text: '黄埔区', - }, - { - value: 2, - text: '长宁区', - }, - { - value: 3, - text: '普陀区', - }, - { - value: 4, - text: '杨浦区', - }, - { - value: 5, - text: '浦东新区', - }, - ], - }, + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ], + }, + ], ] - const [asyncData] = useState([ - { - value: 1, - text: '北京市', - children: [ - { value: 1, text: '朝阳区' }, - { value: 2, text: '海淀区' }, - { value: 3, text: '大兴区' }, - { value: 4, text: '东城区' }, - { value: 5, text: '西城区' }, - { value: 6, text: '丰台区' }, - ], - }, - { - value: 2, - text: '上海市', - children: [], - }, - ]) - const [cityCustom, setCityCustom] = useState('') const setChooseValueCustom = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - const str = options.map((item) => item.text).join('-') - setCityCustom(str) + console.log('onconfirm', selectedOptions, selectedValue) + const city = selectedOptions + .map((item: PickerOption) => item.label) + .join('-') + setCityCustom(city) + setValue(selectedValue) } + return ( <> setIsVisible(!isVisible)} /> @@ -107,13 +95,12 @@ const Demo6 = () => { setIsVisible(false)} - onConfirm={(list, values) => setChooseValueCustom(list, values)} - onChange={( - options: PickerOption[], - value: (string | number)[], - columnIndex: number - ) => console.log(asyncData, '多级联动', columnIndex, value, options)} + onConfirm={setChooseValueCustom} + onChange={({ value, index, selectedOptions }) => + console.log('多级联动', value, index, selectedOptions) + } /> ) diff --git a/src/packages/picker/demos/taro/demo7.tsx b/src/packages/picker/demos/taro/demo7.tsx index 2bd3af5785..2071f0010f 100644 --- a/src/packages/picker/demos/taro/demo7.tsx +++ b/src/packages/picker/demos/taro/demo7.tsx @@ -1,77 +1,100 @@ import React, { useState } from 'react' -import { Picker, Cell } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + PickerOptions, + PickerValue, + PickerOnChangeCallbackParameter, + PickerOption, +} from '@nutui/nutui-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo7 = () => { const [isVisible, setIsVisible] = useState(false) - const [asyncDesc, setasyncDesc] = useState('') + const [value, setValue] = useState([1]) + const [asyncDesc, setasyncDesc] = useState('北京') const [asyncData, setAsyncData] = useState([ - { - value: 1, - text: '北京市', - children: [ - { value: 1, text: '朝阳区' }, - { value: 2, text: '海淀区' }, - { value: 3, text: '大兴区' }, - { value: 4, text: '东城区' }, - { value: 5, text: '西城区' }, - { value: 6, text: '丰台区' }, - ], - }, - { - value: 2, - text: '上海市', - children: [], - }, + [ + { + value: 1, + label: '北京', + children: [ + { + value: 1, + label: '朝阳区', + }, + { + value: 2, + label: '海淀区', + }, + { + value: 3, + label: '大兴区', + }, + { + value: 4, + label: '东城区', + }, + { + value: 5, + label: '西城区', + }, + { + value: 6, + label: '丰台区', + }, + ], + }, + { + value: 2, + label: '上海', + children: [], + }, + ], ]) - const updateChooseValueCustmer = ( - options: PickerOption[], - values: (string | number)[], - columnIndex: number - ) => { - console.log('updateChooseValueCustmer', columnIndex, values, options) - if (columnIndex === 0 && values[0] === 2) { + const updateChooseValueCustmer = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + if (value[0] === 2 && asyncData[0]?.[1].children.length === 0) { + console.log('updateChooseValueCustmer', index, value, selectedOptions) setTimeout(() => { - if (asyncData[1].children.length === 0) { - asyncData[1].children = [ - { - value: 1, - text: '黄埔区', - }, - { - value: 2, - text: '长宁区', - }, - { - value: 3, - text: '普陀区', - }, - { - value: 4, - text: '杨浦区', - }, - { - value: 5, - text: '浦东新区', - }, - ] - setAsyncData([...asyncData]) - } - }, 100) + asyncData[0][1].children = [ + { + value: 1, + label: '黄埔区', + }, + { + value: 2, + label: '长宁区', + }, + { + value: 3, + label: '普陀区', + }, + { + value: 4, + label: '杨浦区', + }, + { + value: 5, + label: '浦东新区', + }, + ] + setAsyncData([...[...asyncData]]) + }, 0) } } const setAsyncConfirm = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - const str = options.map((item) => item.text).join('-') - setasyncDesc(str) + console.log('onconfirm', selectedOptions, selectedValue) + const city = selectedOptions + .map((item: PickerOption) => item.label) + .join('-') + setasyncDesc(city) + setValue(selectedValue) } return ( @@ -81,18 +104,14 @@ const Demo7 = () => { description={asyncDesc} onClick={() => setIsVisible(!isVisible)} /> + setIsVisible(false)} - onConfirm={(list, values) => setAsyncConfirm(list, values)} - onChange={( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => - updateChooseValueCustmer(selectedOptions, selectedValue, columnIndex) - } + onConfirm={setAsyncConfirm} + onChange={updateChooseValueCustmer} /> ) diff --git a/src/packages/picker/demos/taro/demo8.tsx b/src/packages/picker/demos/taro/demo8.tsx index 029c6a2d79..5cb9b00514 100644 --- a/src/packages/picker/demos/taro/demo8.tsx +++ b/src/packages/picker/demos/taro/demo8.tsx @@ -1,50 +1,47 @@ import React, { useState } from 'react' -import { Picker, Cell, ConfigProvider } from '@nutui/nutui-react-taro' +import { + Picker, + Cell, + ConfigProvider, + PickerOption, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo8 = () => { - const [isVisible, setIsVisible] = useState(false) + const [visible, setVisible] = useState(false) + const [baseDesc, setBaseDesc] = useState('') const options = [ [ - { value: 1, text: '南京市' }, - { value: 2, text: '无锡市' }, - { value: 3, text: '海北藏族自治区' }, - { value: 4, text: '北京市' }, - { value: 5, text: '连云港市' }, - { value: 8, text: '大庆市' }, - { value: 9, text: '绥化市' }, - { value: 10, text: '潍坊市' }, - { value: 12, text: '乌鲁木齐市' }, + { value: 1, label: '南京市' }, + { value: 2, label: '无锡市' }, + { value: 3, label: '海北藏族自治区' }, + { value: 4, label: '北京市' }, + { value: 5, label: '连云港市' }, + { value: 8, label: '大庆市' }, + { value: 9, label: '绥化市' }, + { value: 10, label: '潍坊市' }, + { value: 12, label: '乌鲁木齐市' }, ], ] - const [baseDesc, setBaseDesc] = useState('') - const confirmPicker = ( - options: PickerOption[], - values: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => { - console.log('demo 确定', options, values) + console.log('confirmPicker', selectedOptions, selectedValue) let description = '' - options.forEach((option: any) => { - description += ` ${option.text}` + selectedOptions.forEach((option: PickerOption) => { + description += ` ${option.label}` }) setBaseDesc(description) - setIsVisible(false) } - return ( <> setIsVisible(!isVisible)} + onClick={() => setVisible(!visible)} /> { }} > confirmPicker(list, values)} - onClose={() => { - setIsVisible(false) - console.log('onclose') - }} + onConfirm={confirmPicker} + onClose={() => setVisible(false)} /> diff --git a/src/packages/picker/index.taro.ts b/src/packages/picker/index.taro.ts index edc33812b3..ff0b062781 100644 --- a/src/packages/picker/index.taro.ts +++ b/src/packages/picker/index.taro.ts @@ -1,5 +1,4 @@ import Picker from './picker.taro' -export type { PickerOption } from './types' export type { PickerProps } from './picker.taro' export default Picker diff --git a/src/packages/picker/index.ts b/src/packages/picker/index.ts index 2f320e8d33..212b25cb51 100644 --- a/src/packages/picker/index.ts +++ b/src/packages/picker/index.ts @@ -1,5 +1,4 @@ import Picker from './picker' -export type { PickerOption } from './types' export type { PickerProps } from './picker' export default Picker diff --git a/src/packages/picker/picker.scss b/src/packages/picker/picker.scss index 6516efc1bf..a95a838b80 100644 --- a/src/packages/picker/picker.scss +++ b/src/packages/picker/picker.scss @@ -6,11 +6,12 @@ &-control { display: flex; - height: $popup-title-height; - font-size: $popup-title-font-size; align-items: center; justify-content: space-between; + height: $popup-title-height; padding: $popup-title-padding; + box-sizing: border-box; + font-size: $popup-title-font-size; } &-cancel-btn { @@ -36,135 +37,5 @@ &-panel { display: flex; - position: relative; - } - - &-indicator { - position: absolute; - top: 108px; - height: $picker-item-height; - width: 100%; - border: $picker-item-active-line-border; - border-left: 0; - border-right: 0; - color: $picker-item-text-color; - font-size: $picker-item-text-font-size; - z-index: 3; - - &-taro { - height: $picker-item-height; - border: 0; - - &::before, - &::after { - border: $picker-item-active-line-border; - border-left: 0; - border-right: 0; - } - } - } - - &-list { - flex: 1; - position: relative; - height: $picker-list-height; - overflow: hidden; - text-align: center; - } - - &-list-panel { - transform-style: preserve-3d; - } - - &-mask { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: $picker-mask-background; - background-position: top, bottom; - background-size: 100% 108px; - background-repeat: no-repeat; - transform: translateZ(0); - z-index: 3; - } - - &-view-panel { - height: $picker-list-height; - flex-grow: 1; - } - - &-content, - &-roller { - position: absolute; - top: 108px; - width: 100%; - height: $picker-item-height; - } - - &-content { - background: #fff; - z-index: 2; - overflow: hidden; - } - - &-item, - &-roller-item { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - height: $picker-item-height; - line-height: $picker-item-height; - color: $picker-item-text-color; - font-size: $picker-item-text-font-size; - text-align: center; - } - - &-item { - font-size: 16px; - background: #fff; } - - &-roller { - z-index: 1; - transform-style: preserve-3d; - } - - &-roller-item { - backface-visibility: hidden; - -moz-backface-visibility: hidden; - -webkit-backface-visibility: hidden; - position: absolute; - top: 0; - width: 100%; - } - - &-roller-item-title { - display: block; - width: 100%; - height: $picker-item-height; - line-height: $picker-item-height; - text-align: center; - color: $color-title; - font-size: $font-size-base; - } - - &-roller-item-hidden { - visibility: hidden; - opacity: 0; - } - - &-placeholder { - height: 1px; - } -} - -[dir='rtl'] .nut-picker-mask, -.nut-rtl .nut-picker-mask { - background-image: var( - --picker-mask-background, - linear-gradient(-180deg, var(--nutui-white-12), var(--nutui-white-7)), - linear-gradient(0deg, var(--nutui-white-12), var(--nutui-white-7)) - ); } diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 19c1fff276..cba096362d 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -2,73 +2,64 @@ import React, { useState, useEffect, useRef, - RefObject, ForwardRefRenderFunction, useImperativeHandle, } from 'react' +import { View } from '@tarojs/components' import classNames from 'classnames' -import Taro from '@tarojs/taro' -import { View, PickerView, PickerViewColumn } from '@tarojs/components' +import isEqual from 'react-fast-compare' +import { + PickerOptions, + PickerValue, + PickerOption, + PickerOnChangeCallbackParameter, +} from '@/packages/pickerview/types' +import PickerView from '@/packages/pickerview/index.taro' import Popup, { PopupProps } from '@/packages/popup/index.taro' -import PickerPanel from './pickerpanel.taro' +import SafeArea from '@/packages/safearea/index.taro' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider/index.taro' -import { PickerOption } from './types' import { usePropsValue } from '@/hooks/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' - -export type PickerActions = { - open: () => void - close: () => void -} +import { PickerActions, PickerRef } from './types' export interface PickerProps extends Omit { visible?: boolean | undefined title?: string - options: (PickerOption | PickerOption[])[] - value?: (number | string)[] - defaultValue?: (number | string)[] + options: PickerOptions[] + value?: PickerValue[] + defaultValue?: PickerValue[] threeDimensional?: boolean duration: number | string closeOnOverlayClick: boolean + renderLabel?: (item: PickerOption) => React.ReactNode + popupProps: Partial< Omit > onConfirm?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void onCancel?: () => void onClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => void - afterClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - pickerRef: RefObject - ) => void - onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void + onChange?: (args0: PickerOnChangeCallbackParameter) => void children?: any } const defaultProps = { ...ComponentDefaults, - visible: false, title: '', options: [], - value: [], + value: undefined, defaultValue: [], - threeDimensional: true, closeOnOverlayClick: true, - duration: 1000, } as unknown as PickerProps const InternalPicker: ForwardRefRenderFunction< - unknown, + PickerRef, Partial > = (props, ref) => { const { locale } = useConfig() @@ -87,37 +78,29 @@ const InternalPicker: ForwardRefRenderFunction< onConfirm, onCancel, onClose, - afterClose, onChange, ...rest } = { ...defaultProps, ...props } const classPrefix = 'nut-picker' const classes = classNames(classPrefix, className) - const [selectedValue, setSelectedValue] = usePropsValue< - Array - >({ + const [selectedValue, setSelectedValue] = usePropsValue({ value: props.value, defaultValue: [...defaultValue], finalValue: [...defaultValue], - onChange: (val) => { - props.onConfirm?.(setSelectedOptions(), val) + onChange: (value: PickerValue[]) => { + props.onConfirm?.(selectedOptionsRef.current, value) }, }) const [innerVisible, setInnerVisible] = usePropsValue({ value: props.visible, defaultValue: false, finalValue: false, - onChange: (val: boolean) => { - props.onClose?.(setSelectedOptions(), innerValue) + onChange: (v: boolean) => { + if (!v) { + props.onClose?.(selectedOptionsRef.current, innerValue) + } }, }) - const [innerValue, setInnerValue] = useState(selectedValue) - const [currentValue, setCurrentValue] = useState([]) - const [columnIndex, setColumnIndex] = useState(0) // 选中列 - const pickerRef = useRef(null) - const [refs, setRefs] = useRefs() - const [columnsList, setColumnsList] = useState([]) // 格式化后每一列的数据 - const isConfirmEvent = useRef(false) const actions: PickerActions = { open: () => { @@ -127,190 +110,56 @@ const InternalPicker: ForwardRefRenderFunction< setInnerVisible(false) }, } - useImperativeHandle(ref, () => actions) - // 级联数据格式化 - const formatCascade = ( - columns: PickerOption[], - values: (number | string)[] - ) => { - const formatted: PickerOption[][] = [] - let columnOptions: PickerOption = { - text: '', - value: '', - children: columns, - } - - let columnIndex = 0 - while (columnOptions && columnOptions.children) { - const options: PickerOption[] = columnOptions.children - const value = values[columnIndex] - let index = options.findIndex((columnItem) => columnItem.value === value) - if (index === -1) index = 0 - columnOptions = columnOptions.children[index] - columnIndex++ - formatted.push(options) - } - return formatted - } - - // 数据类型:多列、嵌套、单列 - const columnsType = () => { - const firstColumn: PickerOption | PickerOption[] = options[0] - if (firstColumn) { - if (Array.isArray(firstColumn)) { - return 'multiple' - } - if ('children' in firstColumn) { - return 'cascade' - } - } - return 'single' - } - - // 传入的数据格式化 - const normalListData = (innerValue: any) => { - const type = columnsType() - switch (type) { - case 'multiple': - return options - case 'cascade': - // 级联数据处理 - return formatCascade(options as PickerOption[], innerValue) - default: - return [options] - } - } - const init = () => { - const normalData: PickerOption[][] = normalListData( - innerValue - ) as PickerOption[][] - setColumnsList(normalData) - // 初始化默认选中数据 - const data: (string | number)[] = [] - normalData.length > 0 && - normalData.map((item) => { - item[0] && data.push(item[0].value) - return item - }) - if (!innerValue.length && innerValue.length === 0) { - setInnerValue([...data]) - } - } - - useEffect(() => { - setInnerValue(innerValue !== selectedValue ? selectedValue : innerValue) - }, [innerVisible, selectedValue]) + const [innerValue, setInnerValue] = useState([...selectedValue]) + const innerValueRef = useRef(innerValue) + const [innerOptions, setInnerOptions] = useState([]) + const selectedOptionsRef = useRef([] as PickerOptions) + const [refs, setRefs] = useRefs() useEffect(() => { if (innerVisible) { - init() + setInnerValue(selectedValue) + setInnerOptions(options as PickerOptions[]) } - }, [options, innerVisible]) - - // 选中值进行修改 - useEffect(() => { - if (!innerVisible) { - return + }, [selectedValue, innerOptions, innerVisible]) + + const onChangeItem = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + if (selectedOptions?.length) { + selectedOptionsRef.current = selectedOptions } - Taro.getEnv() !== 'WEB' && setCurrentValue(defaultValuesConvert()) - onChange && onChange(setSelectedOptions(), innerValue, columnIndex) - }, [innerValue, columnsList, innerVisible]) - - const setSelectedOptions = () => { - const options: PickerOption[] = [] - let currOptions = [] - columnsList.forEach((columnOptions: PickerOption[], index: number) => { - currOptions = columnOptions.filter( - (item) => item.value === innerValue[index] - ) - if (currOptions[0]) { - options.push(currOptions[0]) - } else { - columnOptions[0] && options.push(columnOptions[0]) - } - }) - return options - } - - const defaultValuesConvert = () => { - const defaultIndexs: number[] = [] - if (innerValue.length > 0) { - innerValue.forEach((value, index) => { - for (let i = 0; i < columnsList?.[index]?.length; i++) { - if (columnsList[index][i].value === value) { - defaultIndexs.push(i) - break - } - } - }) - } else if (columnsList && columnsList.length > 0) { - columnsList.forEach((item) => { - defaultIndexs.push(0) - item.length > 0 && selectedValue.push(item[0].value) + if (isEqual(value, innerValueRef.current)) return + innerValueRef.current = value + setInnerValue(value) + innerVisible && + onChange?.({ + selectedOptions, + value, + index, }) - } - - return defaultIndexs } - // 更新已选择数据 - const chooseItem = (columnOptions: PickerOption, columnIndex: number) => { - const values: any = [] - const start = columnIndex - if (columnOptions && Object.keys(columnOptions).length) { - // 切换数据后,数据有变动才触发。 - if (values[columnIndex] !== columnOptions.value) { - if (columnsType() === 'cascade') { - values[columnIndex] = columnOptions.value || '' - while (columnOptions?.children?.[0]) { - values[columnIndex + 1] = columnOptions.children[0].value - columnIndex++ - columnOptions = columnOptions.children[0] - } - // 当前改变列的下一列 children 值为空 - if (columnOptions?.children?.length) { - values[columnIndex + 1] = '' - } - const combineResult = [ - ...innerValue.slice(0, start), - ...values.splice(start), - ] - setInnerValue(combineResult) - setColumnsList(normalListData(combineResult) as PickerOption[][]) - } else { - setInnerValue((data: (number | string)[]) => { - const cdata: (number | string)[] = [...data] - cdata[columnIndex] = Object.prototype.hasOwnProperty.call( - columnOptions, - 'value' - ) - ? columnOptions.value - : '' - return cdata - }) - } - setColumnIndex(columnIndex) - } - } - } - // 点击确定 - const confirm = () => { + const onConfirmEvent = () => { let moving = false refs.forEach((ref: any) => { if (ref.moving) moving = true ref.stopMomentum() }) - if (moving) { - isConfirmEvent.current = true - } else { + if (!moving) { setSelectedValue(innerValue, true) setInnerVisible(false) } - setTimeout(() => { - isConfirmEvent.current = false - }, 0) + } + + const onCancelEvent = () => { + setInnerValue(selectedValue) + onCancel?.() + setInnerVisible(false) } const renderTitleBar = () => { @@ -320,8 +169,7 @@ const InternalPicker: ForwardRefRenderFunction< className={`${classPrefix}-cancel-btn`} onClick={(e) => { e.stopPropagation() - onCancel?.() - setInnerVisible(false) + onCancelEvent() }} > {locale?.cancel} @@ -331,7 +179,7 @@ const InternalPicker: ForwardRefRenderFunction< className={`${classPrefix}-confirm-btn`} onClick={(e) => { e.stopPropagation() - confirm() + onConfirmEvent() }} > {locale.confirm} @@ -340,32 +188,28 @@ const InternalPicker: ForwardRefRenderFunction< ) } - const [, setPickingStatus] = useState(false) - - const pickerStart = () => { - setPickingStatus(true) - } - - const pickerEnd = () => { - setPickingStatus(false) - } - - const pickerChange = (data: any) => { - const prevDefaultValue = currentValue - let changeIndex = 0 - // 判断变化的是第几个 - const list = data.detail.value - for (let i = 0, len = list.length; i < len; i++) { - if (prevDefaultValue[i] !== list[i]) { - changeIndex = i - break - } - } - - // 选择的是哪个 option - chooseItem( - columnsList[changeIndex][data.detail.value[changeIndex]], - changeIndex + const renderPickerElement = () => { + return ( + + {renderTitleBar()} + {typeof children !== 'function' && children} + + { + onChangeItem({ value, index, selectedOptions }) + }} + /> + + ) } @@ -377,72 +221,16 @@ const InternalPicker: ForwardRefRenderFunction< visible={innerVisible} position="bottom" onOverlayClick={() => { - if (closeOnOverlayClick) { - props.onCancel?.() - setInnerVisible(false) - } - }} - afterClose={() => { - afterClose?.(setSelectedOptions(), innerValue, pickerRef) + if (!closeOnOverlayClick) return + onCancelEvent() }} > - - {renderTitleBar()} - {typeof children !== 'function' && children} - - {Taro.getEnv() === 'WEB' ? ( - columnsList?.map((item, index) => { - return ( - - chooseItem(value, index) - } - duration={duration} - key={index} - keyIndex={index} - itemShow={visible} - /> - ) - }) - ) : ( - - {columnsList?.map((columnOptions, index) => { - return ( - - {columnOptions.map((item, index) => { - return ( - - <>{item.text} - - ) - })} - - ) - })} - - )} - - + {innerVisible ? <>{renderPickerElement()} : null} + ) } -const Picker = React.forwardRef>(InternalPicker) +const Picker = React.forwardRef>(InternalPicker) export default Picker diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 3ffbb5c2e0..a2a965bfc6 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -2,56 +2,50 @@ import React, { useState, useEffect, useRef, - RefObject, ForwardRefRenderFunction, useImperativeHandle, } from 'react' import classNames from 'classnames' +import isEqual from 'react-fast-compare' +import { + PickerOptions, + PickerValue, + PickerOption, + PickerOnChangeCallbackParameter, +} from '@/packages/pickerview/types' +import PickerView from '@/packages/pickerview/index' import Popup, { PopupProps } from '@/packages/popup/index' -import { SafeArea } from '@/packages/safearea/safearea' -import PickerPanel from './pickerpanel' +import SafeArea from '@/packages/safearea/index' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider' -import { PickerOption } from './types' import { usePropsValue } from '@/hooks/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' - -export type PickerActions = { - open: () => void - close: () => void -} +import { PickerActions, PickerRef } from './types' export interface PickerProps extends Omit { visible?: boolean | undefined title?: string - options: (PickerOption | PickerOption[])[] - value?: (number | string)[] - defaultValue?: (number | string)[] + options: PickerOptions[] + value?: PickerValue[] + defaultValue?: PickerValue[] threeDimensional?: boolean duration: number | string closeOnOverlayClick: boolean + renderLabel?: (item: PickerOption) => React.ReactNode + popupProps: Partial< Omit > onConfirm?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void onCancel?: () => void onClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => void - afterClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - pickerRef: RefObject - ) => void - onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number + selectedOptions: PickerOptions, + selectedValue: PickerValue[] ) => void + onChange?: (args0: PickerOnChangeCallbackParameter) => void children?: any } @@ -59,14 +53,12 @@ const defaultProps = { ...ComponentDefaults, title: '', options: [], - value: [], + value: undefined, defaultValue: [], - threeDimensional: true, closeOnOverlayClick: true, - duration: 1000, } as unknown as PickerProps const InternalPicker: ForwardRefRenderFunction< - unknown, + PickerRef, Partial > = (props, ref) => { const { locale } = useConfig() @@ -85,36 +77,29 @@ const InternalPicker: ForwardRefRenderFunction< onConfirm, onCancel, onClose, - afterClose, onChange, ...rest } = { ...defaultProps, ...props } const classPrefix = 'nut-picker' const classes = classNames(classPrefix, className) - const [selectedValue, setSelectedValue] = usePropsValue< - Array - >({ + const [selectedValue, setSelectedValue] = usePropsValue({ value: props.value, defaultValue: [...defaultValue], finalValue: [...defaultValue], - onChange: (val: (string | number)[]) => { - props.onConfirm?.(setSelectedOptions(), val) + onChange: (value: PickerValue[]) => { + props.onConfirm?.(selectedOptionsRef.current, value) }, }) const [innerVisible, setInnerVisible] = usePropsValue({ value: props.visible, defaultValue: false, finalValue: false, - onChange: (val: boolean) => { - props.onClose?.(setSelectedOptions(), innerValue) + onChange: (v: boolean) => { + if (!v) { + props.onClose?.(selectedOptionsRef.current, innerValue) + } }, }) - const [innerValue, setInnerValue] = useState(selectedValue) - const [columnIndex, setColumnIndex] = useState(0) // 选中列 - const pickerRef = useRef(null) - const [refs, setRefs] = useRefs() - const [columnsList, setColumnsList] = useState([]) // 格式化后每一列的数据 - const isConfirmEvent = useRef(false) const actions: PickerActions = { open: () => { @@ -124,164 +109,56 @@ const InternalPicker: ForwardRefRenderFunction< setInnerVisible(false) }, } - useImperativeHandle(ref, () => actions) - // 级联数据格式化 - const formatCascade = ( - columns: PickerOption[], - values: (number | string)[] - ) => { - const formatted: PickerOption[][] = [] - let columnOptions: PickerOption = { - text: '', - value: '', - children: columns, - } - - let columnIndex = 0 - while (columnOptions && columnOptions.children) { - const options: PickerOption[] = columnOptions.children - const value = values[columnIndex] - let index = options.findIndex((columnItem) => columnItem.value === value) - if (index === -1) index = 0 - columnOptions = columnOptions.children[index] - columnIndex++ - formatted.push(options) - } - return formatted - } - - // 数据类型:多列、嵌套、单列 - const columnsType = () => { - const firstColumn: PickerOption | PickerOption[] = options[0] - if (firstColumn) { - if (Array.isArray(firstColumn)) { - return 'multiple' - } - if ('children' in firstColumn) { - return 'cascade' - } - } - return 'single' - } - - // 传入的数据格式化 - const normalListData = (innerValue: any) => { - const type = columnsType() - switch (type) { - case 'multiple': - return options - case 'cascade': - // 级联数据处理 - return formatCascade(options as PickerOption[], innerValue) - default: - return [options] - } - } - const init = () => { - const normalData: PickerOption[][] = normalListData( - innerValue - ) as PickerOption[][] - setColumnsList(normalData) - // 初始化默认选中数据 - const data: (string | number)[] = [] - normalData.length > 0 && - normalData.map((item) => { - item[0] && data.push(item[0].value) - return item - }) - if (!innerValue.length && innerValue.length === 0) { - setInnerValue([...data]) - } - } - - useEffect(() => { - setInnerValue(innerValue !== selectedValue ? selectedValue : innerValue) - }, [innerVisible, selectedValue]) + const [innerValue, setInnerValue] = useState([...selectedValue]) + const innerValueRef = useRef(innerValue) + const [innerOptions, setInnerOptions] = useState([]) + const selectedOptionsRef = useRef([] as PickerOptions) + const [refs, setRefs] = useRefs() useEffect(() => { if (innerVisible) { - init() + setInnerValue(selectedValue) + setInnerOptions(options as PickerOptions[]) } - }, [options, innerVisible]) - - // 选中值进行修改 - useEffect(() => { - onChange && onChange(setSelectedOptions(), innerValue, columnIndex) - }, [innerValue, columnsList]) - - const setSelectedOptions = () => { - const options: PickerOption[] = [] - let currOptions = [] - columnsList.forEach((columnOptions: PickerOption[], index: number) => { - currOptions = columnOptions.filter( - (item) => item.value === innerValue[index] - ) - if (currOptions[0]) { - options.push(currOptions[0]) - } else { - columnOptions[0] && options.push(columnOptions[0]) - } - }) - return options - } - - // 更新已选择数据 - const chooseItem = (columnOptions: PickerOption, columnIndex: number) => { - const values: any = [] - const start = columnIndex - if (columnOptions && Object.keys(columnOptions).length) { - // 切换数据后,数据有变动才触发。 - if (values[columnIndex] !== columnOptions.value) { - if (columnsType() === 'cascade') { - values[columnIndex] = columnOptions.value || '' - while (columnOptions?.children?.[0]) { - values[columnIndex + 1] = columnOptions.children[0].value - columnIndex++ - columnOptions = columnOptions.children[0] - } - // 当前改变列的下一列 children 值为空 - if (columnOptions?.children?.length) { - values[columnIndex + 1] = '' - } - const combineResult = [ - ...innerValue.slice(0, start), - ...values.splice(start), - ] - setInnerValue(combineResult) - setColumnsList(normalListData(combineResult) as PickerOption[][]) - } else { - setInnerValue((data: (number | string)[]) => { - const cdata: (number | string)[] = [...data] - cdata[columnIndex] = Object.prototype.hasOwnProperty.call( - columnOptions, - 'value' - ) - ? columnOptions.value - : '' - return cdata - }) - } - setColumnIndex(columnIndex) - } + }, [selectedValue, innerOptions, innerVisible]) + + const onChangeItem = ({ + value, + index, + selectedOptions, + }: PickerOnChangeCallbackParameter) => { + if (selectedOptions?.length) { + selectedOptionsRef.current = selectedOptions } + if (isEqual(value, innerValueRef.current)) return + innerValueRef.current = value + setInnerValue(value) + innerVisible && + onChange?.({ + selectedOptions, + value, + index, + }) } - const confirm = () => { + + const onConfirmEvent = () => { let moving = false refs.forEach((ref: any) => { if (ref.moving) moving = true ref.stopMomentum() }) - if (moving) { - isConfirmEvent.current = true - } else { + if (!moving) { setSelectedValue(innerValue, true) setInnerVisible(false) } - setTimeout(() => { - isConfirmEvent.current = false - }, 0) + } + + const onCancelEvent = () => { + setInnerValue(selectedValue) + onCancel?.() + setInnerVisible(false) } const renderTitleBar = () => { @@ -291,8 +168,7 @@ const InternalPicker: ForwardRefRenderFunction< className={`${classPrefix}-cancel-btn`} onClick={(e) => { e.stopPropagation() - onCancel?.() - setInnerVisible(false) + onCancelEvent() }} > {locale?.cancel} @@ -302,7 +178,7 @@ const InternalPicker: ForwardRefRenderFunction< className={`${classPrefix}-confirm-btn`} onClick={(e) => { e.stopPropagation() - confirm() + onConfirmEvent() }} > {locale.confirm} @@ -311,50 +187,49 @@ const InternalPicker: ForwardRefRenderFunction< ) } + const renderPickerElement = () => { + return ( +
+ {renderTitleBar()} + {typeof children !== 'function' && children} +
+ { + onChangeItem({ value, index, selectedOptions }) + }} + /> +
+
+ ) + } return ( <> {typeof children === 'function' && children(selectedValue)} + { - if (closeOnOverlayClick) { - props.onCancel?.() - setInnerVisible(false) - } - }} - afterClose={() => { - afterClose?.(setSelectedOptions(), innerValue, pickerRef) + if (!closeOnOverlayClick) return + onCancelEvent() }} > -
- {renderTitleBar()} - {typeof children !== 'function' && children} -
- {columnsList?.map((item, index) => { - return ( - - chooseItem(value, index) - } - duration={duration} - key={index} - keyIndex={index} - /> - ) - })} -
-
+ {innerVisible ? <>{renderPickerElement()} : null}
) } -const Picker = React.forwardRef>(InternalPicker) +const Picker = React.forwardRef>(InternalPicker) export default Picker diff --git a/src/packages/picker/pickerpanel.taro.tsx b/src/packages/picker/pickerpanel.taro.tsx deleted file mode 100644 index 0ecb888d61..0000000000 --- a/src/packages/picker/pickerpanel.taro.tsx +++ /dev/null @@ -1,322 +0,0 @@ -import React, { - useState, - useEffect, - useRef, - ForwardRefRenderFunction, - useImperativeHandle, -} from 'react' -import { View } from '@tarojs/components' -import { PickerOption } from './types' -import { useTouch } from '@/hooks/use-touch' -import { getRectByTaro } from '@/utils/get-rect-by-taro' -import { passiveSupported } from '@/utils/supports-passive' - -interface PickerPanelProps { - keyIndex?: number - defaultValue?: string | number - options?: PickerOption[] - threeDimensional: boolean - duration: number | string - itemShow: boolean - chooseItem?: (val: PickerOption, idx: number) => void -} - -const InternalPickerPanel: ForwardRefRenderFunction< - { stopMomentum: () => void; moving: boolean }, - Partial -> = (props, ref) => { - const { - keyIndex = 0, - defaultValue, - options = [], - threeDimensional = true, - duration = 1000, - itemShow = false, - chooseItem, - } = props - - const touch = useTouch() - const DEFAULT_DURATION = 200 - // 触发惯性滑动条件: - // 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move - // 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动 - const INERTIA_TIME = 300 - const INERTIA_DISTANCE = 15 - const [currIndex, setCurrIndex] = useState(1) - const lineSpacing = useRef(36) - const [touchTime, setTouchTime] = useState(0) - const [touchDeg, setTouchDeg] = useState('0deg') - const rotation = 20 - const moving = useRef(false) - let timer: number | undefined - - const listRef = useRef(null) - const rollerRef = useRef(null) - const pickerPanelRef = useRef(null) - - const [startTime, setStartTime] = useState(0) - const [startY, setStartY] = useState(0) - - const transformY = useRef(0) - const [scrollDistance, setScrollDistance] = useState(0) - - const isHidden = (index: number) => { - if (index >= currIndex + 8 || index <= currIndex - 8) { - return true - } - return false - } - - const setTransform = ( - type: string, - deg: string, - time = DEFAULT_DURATION, - translateY = 0 - ) => { - let nTime = time - if (type !== 'end') { - nTime = 0 - } - setTouchTime(nTime) - setTouchDeg(deg) - setScrollDistance(translateY) - } - - const setMove = (move: number, type?: string, time?: number) => { - let updateMove = move + transformY.current - if (type === 'end') { - // 限定滚动距离 - if (updateMove > 0) { - updateMove = 0 - } - if (updateMove < -(options.length - 1) * lineSpacing.current) { - updateMove = -(options.length - 1) * lineSpacing.current - } - - // 设置滚动距离为lineSpacing的倍数值 - const endMove = - Math.round(updateMove / lineSpacing.current) * lineSpacing.current - const deg = `${ - (Math.abs(Math.round(endMove / lineSpacing.current)) + 1) * rotation - }deg` - - setTransform(type, deg, time, endMove) - setCurrIndex(Math.abs(Math.round(endMove / lineSpacing.current)) + 1) - } else { - let deg = 0 - const currentDeg = (-updateMove / lineSpacing.current + 1) * rotation - - // picker 滚动的最大角度 - const maxDeg = (options.length + 1) * rotation - const minDeg = 0 - deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - - if (minDeg < deg && deg < maxDeg) { - setTransform('', `${deg}deg`, undefined, updateMove) - setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing.current)) + 1) - } - } - } - - const setChooseValue = (move: number) => { - chooseItem?.(options?.[Math.round(-move / lineSpacing.current)], keyIndex) - } - - // 开始滚动 - const touchStart = (event: React.TouchEvent) => { - touch.start(event) - setStartY(touch.deltaY.current) - setStartTime(Date.now()) - transformY.current = scrollDistance - } - - const touchMove = (event: React.TouchEvent) => { - touch.move(event) - if ((touch as any).isVertical) { - moving.current = true - preventDefault(event, true) - } - const move = touch.deltaY.current - startY - setMove(move) - } - - const touchEnd = () => { - if (!moving.current) return - const move = touch.deltaY.current - startY - const moveTime = Date.now() - startTime - // 区分是否为惯性滚动 - if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { - // 惯性滚动 - const distance = momentum(move, moveTime) - setMove(distance, 'end', +duration) - } else { - setMove(move, 'end') - } - setTimeout(() => { - touch.reset() - }, 0) - } - - // 惯性滚动 距离 - const momentum = (distance: number, duration: number) => { - let nDistance = distance - // 惯性滚动的速度 - const speed = Math.abs(nDistance / duration) - // 惯性滚动的距离 - nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) - return nDistance - } - - const modifyStatus = (type?: boolean, val?: string | number) => { - const value = val || defaultValue - let index = -1 - if (value) { - options.some((item, idx) => { - if (item.value === value) { - index = idx - return true - } - return false - }) - } else { - options.forEach((item, i) => { - if (item.value === defaultValue) { - index = i - } - }) - } - - setCurrIndex(index === -1 ? 1 : index + 1) - const move = index === -1 ? 0 : index * lineSpacing.current - type && setChooseValue(-move) - setMove(-move) - } - - // 惯性滚动结束 - const stopMomentum = () => { - moving.current = false - setTouchTime(0) - setChooseValue(scrollDistance) - } - // 阻止默认事件 - const preventDefault = ( - event: React.TouchEvent, - isStopPropagation?: boolean - ) => { - event.preventDefault() - if (isStopPropagation) { - event.stopPropagation() - } - } - - const getReference = async () => { - const refe = await getRectByTaro(listRef?.current) - lineSpacing.current = refe.height ? refe.height : 36 - } - - const touchRollerStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `rotate3d(1, 0, 0, ${touchDeg})`, - } - } - const touchTileStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `translate3d(0, ${scrollDistance}px, 0)`, - } - } - - useEffect(() => { - setScrollDistance(0) - transformY.current = 0 - modifyStatus(false) - return () => { - clearTimeout(timer) - } - }, [options]) - - useEffect(() => { - if (itemShow) { - setTimeout(() => { - getReference() - }, 200) - } - }, [itemShow]) - - useImperativeHandle(ref, () => ({ - stopMomentum, - moving: moving.current, - })) - - useEffect(() => { - const eventOptions = passiveSupported - ? { passive: false, once: true } - : false - const element = pickerPanelRef.current - element.addEventListener('touchstart', touchStart, eventOptions) - element.addEventListener('touchmove', touchMove, eventOptions) - element.addEventListener('touchend', touchEnd, eventOptions) - return () => { - element.removeEventListener('touchstart', touchStart, eventOptions) - element.removeEventListener('touchmove', touchMove, eventOptions) - element.removeEventListener('touchend', touchEnd, eventOptions) - } - }) - - return ( - - - {/* 3D 效果 */} - {threeDimensional && - options.map((item, index) => { - return ( - - <>{item.text} - - ) - })} - {/* 平铺 */} - {!threeDimensional && - options.map((item, index) => { - return ( - - <>{item.text} - - ) - })} - - - - - ) -} -const PickerPanel = React.forwardRef< - { stopMomentum: () => void; moving: boolean }, - Partial ->(InternalPickerPanel) -export default PickerPanel diff --git a/src/packages/picker/pickerpanel.tsx b/src/packages/picker/pickerpanel.tsx deleted file mode 100644 index efe7e986fe..0000000000 --- a/src/packages/picker/pickerpanel.tsx +++ /dev/null @@ -1,305 +0,0 @@ -import React, { - useState, - useEffect, - useRef, - ForwardRefRenderFunction, - useImperativeHandle, -} from 'react' -import { PickerOption } from './types' -import { useTouch } from '@/hooks/use-touch' -import { passiveSupported } from '@/utils/supports-passive' - -interface PickerPanelProps { - keyIndex?: number - defaultValue?: string | number - options?: PickerOption[] - threeDimensional: boolean - duration: number | string - chooseItem?: (val: PickerOption, idx: number) => void -} - -const InternalPickerPanel: ForwardRefRenderFunction< - { stopMomentum: () => void; moving: boolean }, - Partial -> = (props, ref) => { - const { - keyIndex = 0, - defaultValue, - options = [], - threeDimensional = true, - duration = 1000, - chooseItem, - } = props - - const touch = useTouch() - const DEFAULT_DURATION = 200 - // 触发惯性滑动条件: - // 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_TIME` 且 move - // 距离大于 `MOMENTUM_DISTANCE` 时,执行惯性滑动 - const INERTIA_TIME = 300 - const INERTIA_DISTANCE = 15 - const [currIndex, setCurrIndex] = useState(1) - const lineSpacing = 36 - const [touchTime, setTouchTime] = useState(0) - const [touchDeg, setTouchDeg] = useState('0deg') - const rotation = 20 - const moving = useRef(false) - let timer: number | undefined - - const rollerRef = useRef(null) - const PickerPanelRef = useRef(null) - - const [startTime, setStartTime] = useState(0) - const [startY, setStartY] = useState(0) - - const transformY = useRef(0) - const [scrollDistance, setScrollDistance] = useState(0) - - const isHidden = (index: number) => { - if (index >= currIndex + 8 || index <= currIndex - 8) { - return true - } - return false - } - - const setTransform = ( - type: string, - deg: string, - time = DEFAULT_DURATION, - translateY = 0 - ) => { - let nTime = time - if (type !== 'end') { - nTime = 0 - } - setTouchTime(nTime) - setTouchDeg(deg) - setScrollDistance(translateY) - } - - const setMove = (move: number, type?: string, time?: number) => { - let updateMove = move + transformY.current - if (type === 'end') { - // 限定滚动距离 - if (updateMove > 0) { - updateMove = 0 - } - if (updateMove < -(options.length - 1) * lineSpacing) { - updateMove = -(options.length - 1) * lineSpacing - } - - // 设置滚动距离为lineSpacing的倍数值 - const endMove = Math.round(updateMove / lineSpacing) * lineSpacing - const deg = `${ - (Math.abs(Math.round(endMove / lineSpacing)) + 1) * rotation - }deg` - - setTransform(type, deg, time, endMove) - setCurrIndex(Math.abs(Math.round(endMove / lineSpacing)) + 1) - } else { - let deg = 0 - const currentDeg = (-updateMove / lineSpacing + 1) * rotation - - // picker 滚动的最大角度 - const maxDeg = (options.length + 1) * rotation - const minDeg = 0 - - deg = Math.min(Math.max(currentDeg, minDeg), maxDeg) - - if (minDeg < deg && deg < maxDeg) { - setTransform('', `${deg}deg`, undefined, updateMove) - setCurrIndex(Math.abs(Math.round(updateMove / lineSpacing)) + 1) - } - } - } - - const setChooseValue = (move: number) => { - chooseItem?.(options?.[Math.round(-move / lineSpacing)], keyIndex) - } - - // 开始滚动 - const touchStart = (event: React.TouchEvent) => { - touch.start(event) - setStartY(touch.deltaY.current) - setStartTime(Date.now()) - transformY.current = scrollDistance - } - - const touchMove = (event: React.TouchEvent) => { - touch.move(event) - if ((touch as any).isVertical) { - moving.current = true - preventDefault(event, true) - } - const move = touch.deltaY.current - startY - setMove(move) - } - - const touchEnd = () => { - if (!moving.current) return - const move = touch.deltaY.current - startY - const moveTime = Date.now() - startTime - // 区分是否为惯性滚动 - if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { - // 惯性滚动 - const distance = momentum(move, moveTime) - setMove(distance, 'end', +duration) - } else { - setMove(move, 'end') - } - setTimeout(() => { - touch.reset() - }, 0) - } - - // 惯性滚动 距离 - const momentum = (distance: number, duration: number) => { - let nDistance = distance - // 惯性滚动的速度 - const speed = Math.abs(nDistance / duration) - // 惯性滚动的距离 - nDistance = (speed / 0.003) * (nDistance < 0 ? -1 : 1) - return nDistance - } - - const modifyStatus = (type?: boolean, val?: string | number) => { - const value = val || defaultValue - let index = -1 - if (value) { - options.some((item, idx) => { - if (item.value === value) { - index = idx - return true - } - return false - }) - } else { - options.forEach((item, i) => { - if (item.value === defaultValue) { - index = i - } - }) - } - - setCurrIndex(index === -1 ? 1 : index + 1) - const move = index === -1 ? 0 : index * lineSpacing - type && setChooseValue(-move) - setMove(-move) - } - - // 惯性滚动结束 - const stopMomentum = () => { - moving.current = false - setTouchTime(0) - setChooseValue(scrollDistance) - } - // 阻止默认事件 - const preventDefault = ( - event: React.TouchEvent, - isStopPropagation?: boolean - ) => { - /* istanbul ignore else */ - if (typeof event.cancelable !== 'boolean' || event.cancelable) { - event.preventDefault() - } - - if (isStopPropagation) { - event.stopPropagation() - } - } - - const touchRollerStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `rotate3d(1, 0, 0, ${touchDeg})`, - } - } - const touchTileStyle = () => { - return { - transition: `transform ${touchTime}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, - transform: `translate3d(0, ${scrollDistance}px, 0)`, - } - } - - useEffect(() => { - setScrollDistance(0) - transformY.current = 0 - modifyStatus(false) - return () => { - clearTimeout(timer) - } - }, [options]) - - useImperativeHandle(ref, () => ({ - stopMomentum, - moving: moving.current, - })) - - useEffect(() => { - const options = passiveSupported ? { passive: false } : false - PickerPanelRef.current?.addEventListener('touchstart', touchStart, options) - PickerPanelRef.current?.addEventListener('touchmove', touchMove, options) - PickerPanelRef.current?.addEventListener('touchend', touchEnd, options) - return () => { - PickerPanelRef.current?.removeEventListener('touchstart', touchStart) - PickerPanelRef.current?.removeEventListener('touchmove', touchMove) - PickerPanelRef.current?.removeEventListener('touchend', touchEnd) - } - }) - - return ( -
-
- {/* 3D 效果 */} - {threeDimensional && - options.map((item, index) => { - return ( -
- <>{item.text} -
- ) - })} - {/* 平铺 */} - {!threeDimensional && - options.map((item, index) => { - return ( -
- <>{item.text} -
- ) - })} -
-
-
-
- ) -} -const PickerPanel = React.forwardRef< - { stopMomentum: () => void; moving: boolean }, - Partial ->(InternalPickerPanel) -export default PickerPanel diff --git a/src/packages/picker/types.ts b/src/packages/picker/types.ts index 5c7e00b846..ae6fabdd59 100644 --- a/src/packages/picker/types.ts +++ b/src/packages/picker/types.ts @@ -1,7 +1,6 @@ -export interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number +export type PickerRef = PickerActions +export type PickerActions = { + open: () => void + close: () => void } +export type ColumnsType = 'single' | 'multiple' | 'cascade' diff --git a/src/packages/pickerview/__test__/pickerview.spec.tsx b/src/packages/pickerview/__test__/pickerview.spec.tsx index 5dfd175869..6f67fe5c6c 100644 --- a/src/packages/pickerview/__test__/pickerview.spec.tsx +++ b/src/packages/pickerview/__test__/pickerview.spec.tsx @@ -133,7 +133,7 @@ test('should render with Multi Column', () => { test('should match onchange', async () => { const PenderContent = () => { - const [value, setValue] = useState([]) + const [value, setValue] = useState([] as number[]) const [options, setInnerOptions] = useState([]) useEffect(() => { diff --git a/src/packages/pickerview/doc.en-US.md b/src/packages/pickerview/doc.en-US.md index 42006732bd..0193489639 100644 --- a/src/packages/pickerview/doc.en-US.md +++ b/src/packages/pickerview/doc.en-US.md @@ -71,13 +71,13 @@ import { PickerView } from '@nutui/nutui-react' | duration | The duration of inertial rolling during rapid sliding, in ms | `string` \| `number` | `1000` | | onChange | Called when the value of each column changes | `({value, index, selectedOptions}) => void` | `-` | -### PickerOptionItem +### PickerOption | Property | Description | Type | Default | | --- | --- | --- | --- | | label | Text of column | `string` \| `number` | `-` | | value | Value of column | `string` \| `number` | `-` | -| children | Cascader Option | `PickerOptionItem[]` | `-` | +| children | Cascader Option | `PickerOptions` | `-` | ## Theming diff --git a/src/packages/pickerview/doc.md b/src/packages/pickerview/doc.md index ef3cb7dc18..378d917da3 100644 --- a/src/packages/pickerview/doc.md +++ b/src/packages/pickerview/doc.md @@ -71,13 +71,13 @@ import { PickerView } from '@nutui/nutui-react' | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | -### PickerOptionItem +### PickerOption | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | label | 选项的文字内容 | `string` \| `number` | `-` | | value | 选项对应的值,且唯一 | `string` \| `number` | `-` | -| children | 用于级联选项 | `PickerOptionItem[]` | `-` | +| children | 用于级联选项 | `PickerOptions` | `-` | ## 主题定制 diff --git a/src/packages/pickerview/doc.taro.md b/src/packages/pickerview/doc.taro.md index 344fc9dee3..cc0bcb4e1d 100644 --- a/src/packages/pickerview/doc.taro.md +++ b/src/packages/pickerview/doc.taro.md @@ -71,13 +71,13 @@ import { PickerView } from '@nutui/nutui-react-taro' | duration | 快速滑动时惯性滚动的时长,单位 ms | `string` \| `number` | `1000` | | onChange | 每一列值变更时调用 | `({value, index, selectedOptions}) => void` | `-` | -### PickerOptionItem +### PickerOption | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | label | 选项的文字内容 | `string` \| `number` | `-` | | value | 选项对应的值,且唯一 | `string` \| `number` | `-` | -| children | 用于级联选项 | `PickerOptionItem[]` | `-` | +| children | 用于级联选项 | `PickerOptions` | `-` | ## 主题定制 diff --git a/src/packages/pickerview/doc.zh-TW.md b/src/packages/pickerview/doc.zh-TW.md index 073f5b8e4f..cfe5e7360e 100644 --- a/src/packages/pickerview/doc.zh-TW.md +++ b/src/packages/pickerview/doc.zh-TW.md @@ -71,13 +71,13 @@ import { PickerView } from '@nutui/nutui-react-taro' | duration | 快速滑動時慣性滾動的時長,單位 ms | `string` \| `number` | `1000` | | onChange | 每一列值變更時調用 | `({value, index, selectedOptions}) => void` | `-` | -### PickerOptionItem +### PickerOption | 屬性 | 說明 | 類型 | 默認值 | | --- | --- | --- | --- | | label | 選項的文字內容 | `string` \| `number` | `-` | | value | 選項對應的值,且唯一 | `string` \| `number` | `-` | -| children | 用於級聯選項 | `PickerOptionItem[]` | `-` | +| children | 用於級聯選項 | `PickerOptions` | `-` | ## 主題定制 diff --git a/src/packages/pickerview/index.taro.ts b/src/packages/pickerview/index.taro.ts index 1bd5f2a397..8d492244e8 100644 --- a/src/packages/pickerview/index.taro.ts +++ b/src/packages/pickerview/index.taro.ts @@ -2,7 +2,7 @@ import PickerView from './pickerview.taro' export type { PickerViewProps, - PickerOptionItem, + PickerOption, PickerRollerProps, PickerValue, PickerOptions, diff --git a/src/packages/pickerview/index.ts b/src/packages/pickerview/index.ts index 15c781a272..bff2bf6714 100644 --- a/src/packages/pickerview/index.ts +++ b/src/packages/pickerview/index.ts @@ -2,7 +2,7 @@ import PickerView from './pickerview' export type { PickerViewProps, - PickerOptionItem, + PickerOption, PickerRollerProps, PickerValue, PickerOptions, diff --git a/src/packages/pickerview/pickerroller.taro.tsx b/src/packages/pickerview/pickerroller.taro.tsx index 10c38d145f..2acf57c520 100644 --- a/src/packages/pickerview/pickerroller.taro.tsx +++ b/src/packages/pickerview/pickerroller.taro.tsx @@ -8,7 +8,7 @@ import React, { import { View } from '@tarojs/components' import { useTouch } from '@/hooks/use-touch' import { passiveSupported } from '@/utils/supports-passive' -import { PickerRollerProps, PickerOptionItem } from './types' +import { PickerRollerProps, PickerOption } from './types' import { web } from '@/utils/platform-taro' import { preventDefault } from '@/utils' import { momentum, useStyles } from './utils' @@ -23,7 +23,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< threeDimensional = true, duration = 1000, onSelect, - renderLabel = (item: PickerOptionItem) => item.label, + renderLabel = (item: PickerOption) => item.label, } = props const DEFAULT_DURATION = 200 @@ -132,7 +132,9 @@ const InternalPickerRoller: ForwardRefRenderFunction< const updateStatus = (shouldSelect?: boolean, value?: string | number) => { const selectedValue = value || props.value - const index = options.findIndex((item) => item.value === selectedValue) + const index = options.findIndex( + (item: PickerOption) => item.value === selectedValue + ) setCurrentIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current shouldSelect && selectValue(-move) @@ -223,7 +225,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< > {/* 3D 效果 */} {threeDimensional && - options.map((item, index) => ( + options.map((item: PickerOption, index: number) => ( { + options.map((item: PickerOption, index: number) => { return ( item.label, + renderLabel = (item: PickerOption) => item.label, } = props const DEFAULT_DURATION = 200 @@ -130,7 +130,9 @@ const InternalPickerRoller: ForwardRefRenderFunction< const updateStatus = (shouldSelect?: boolean, value?: string | number) => { const selectedValue = value || props.value - const index = options.findIndex((item) => item.value === selectedValue) + const index = options.findIndex( + (item: PickerOption) => item.value === selectedValue + ) setCurrentIndex(index === -1 ? 1 : index + 1) const move = index * lineSpacing.current shouldSelect && selectValue(-move) @@ -210,7 +212,7 @@ const InternalPickerRoller: ForwardRefRenderFunction< > {/* 3D */} {threeDimensional && - options.map((item, index) => ( + options.map((item: PickerOption, index: number) => (
( + options.map((item: PickerOption, index: number) => (
item.label, + renderLabel: (item: PickerOption) => item.label, } as PickerViewProps const InternalPickerView: ForwardRefRenderFunction< @@ -77,7 +77,7 @@ const InternalPickerView: ForwardRefRenderFunction< if (!options.length) return [] // 如果 options 为空,直接返回空数组 const formatted: PickerOptions[] = [] - let columnOptions: PickerOptionItem = { + let columnOptions: PickerOption = { label: '', value: '', children: options, @@ -95,7 +95,7 @@ const InternalPickerView: ForwardRefRenderFunction< } else if (currentValue) { // 如果 currentValue 存在,查找匹配的项 const index = currentOptions.findIndex( - (columnItem) => columnItem.value === currentValue + (columnItem: PickerOption) => columnItem.value === currentValue ) columnOptions = currentOptions[index === -1 ? 0 : index] // 如果未找到,默认取第一个 } else { @@ -131,7 +131,7 @@ const InternalPickerView: ForwardRefRenderFunction< }, [selectedValue]) const handleSelect = useCallback( - (option: PickerOptionItem, index: number) => { + (option: PickerOption, index: number) => { const newValue = option?.value if (!newValue || innerValue[index] === newValue) return changeIndex.current = index @@ -159,7 +159,7 @@ const InternalPickerView: ForwardRefRenderFunction< ...values.splice(startIndex), ] setInnerValue([...combineResult]) - const optionFirst = props?.options?.[0] as PickerOptionItem[] + const optionFirst = props?.options?.[0] as PickerOptions if ( !isEqual( formatCascadeOptions(optionFirst, combineResult), @@ -177,12 +177,12 @@ const InternalPickerView: ForwardRefRenderFunction< return innerOptions .map((columnOptions, index) => { const selectedOption = columnOptions.find( - (item) => item.value === innerValue[index] + (item: PickerOption) => item.value === innerValue[index] ) return selectedOption // return selectedOption || columnOptions[0] }) - .filter(Boolean) as PickerOptionItem[] + .filter(Boolean) as PickerOptions }, [innerOptions, innerValue]) useEffect(() => { diff --git a/src/packages/pickerview/pickerview.tsx b/src/packages/pickerview/pickerview.tsx index 0fef411bea..fbf2189c52 100644 --- a/src/packages/pickerview/pickerview.tsx +++ b/src/packages/pickerview/pickerview.tsx @@ -12,7 +12,7 @@ import { ComponentDefaults } from '@/utils/typings' import { usePropsValue } from '@/hooks/use-props-value' import { PickerViewProps, - PickerOptionItem, + PickerOption, PickerValue, PickerOptions, } from './types' @@ -23,7 +23,7 @@ const defaultProps = { options: [], defaultValue: [], value: undefined, - renderLabel: (item: PickerOptionItem) => item.label, + renderLabel: (item: PickerOption) => item.label, } as PickerViewProps const InternalPickerView: ForwardRefRenderFunction< @@ -76,7 +76,7 @@ const InternalPickerView: ForwardRefRenderFunction< if (!options.length) return [] // 如果 options 为空,直接返回空数组 const formatted: PickerOptions[] = [] - let columnOptions: PickerOptionItem = { + let columnOptions: PickerOption = { label: '', value: '', children: options, @@ -94,7 +94,7 @@ const InternalPickerView: ForwardRefRenderFunction< } else if (currentValue) { // 如果 currentValue 存在,查找匹配的项 const index = currentOptions.findIndex( - (columnItem) => columnItem.value === currentValue + (columnItem: PickerOption) => columnItem.value === currentValue ) columnOptions = currentOptions[index === -1 ? 0 : index] // 如果未找到,默认取第一个 } else { @@ -130,7 +130,7 @@ const InternalPickerView: ForwardRefRenderFunction< }, [selectedValue]) const handleSelect = useCallback( - (option: PickerOptionItem, index: number) => { + (option: PickerOption, index: number) => { const newValue = option?.value if (!newValue || innerValue[index] === newValue) return changeIndex.current = index @@ -158,7 +158,7 @@ const InternalPickerView: ForwardRefRenderFunction< ...values.splice(startIndex), ] setInnerValue([...combineResult]) - const optionFirst = props?.options?.[0] as PickerOptionItem[] + const optionFirst = props?.options?.[0] as PickerOptions if ( !isEqual( formatCascadeOptions(optionFirst, combineResult), @@ -176,12 +176,12 @@ const InternalPickerView: ForwardRefRenderFunction< return innerOptions .map((columnOptions, index) => { const selectedOption = columnOptions.find( - (item) => item.value === innerValue[index] + (item: PickerOption) => item.value === innerValue[index] ) return selectedOption // return selectedOption || columnOptions[0] }) - .filter(Boolean) as PickerOptionItem[] + .filter(Boolean) as PickerOptions }, [innerOptions, innerValue]) useEffect(() => { diff --git a/src/packages/pickerview/types.ts b/src/packages/pickerview/types.ts index e25d83faf5..57efbd2b7e 100644 --- a/src/packages/pickerview/types.ts +++ b/src/packages/pickerview/types.ts @@ -2,28 +2,28 @@ import { BasicComponent } from '@/utils/typings' export type PickerValue = string | number | null -export interface PickerOptionItem { +export interface PickerOption { label: string | number value: string | number - children?: PickerOptionItem[] + children?: PickerOptions } -export type PickerOptions = PickerOptionItem[] +export type PickerOptions = PickerOption[] export interface PickerRollerProps { - options: PickerOptionItem[] + options: PickerOptions keyIndex: number value: PickerValue threeDimensional?: boolean duration?: number | string - onSelect: (option: PickerOptionItem, index: number) => void - renderLabel: (item: PickerOptionItem) => React.ReactNode + onSelect: (option: PickerOption, index: number) => void + renderLabel: (item: PickerOption) => React.ReactNode } export interface PickerOnChangeCallbackParameter { value: PickerValue[] index: number - selectedOptions: PickerOptionItem[] + selectedOptions: PickerOptions } export interface PickerViewProps extends BasicComponent { @@ -33,6 +33,6 @@ export interface PickerViewProps extends BasicComponent { defaultValue?: PickerValue[] threeDimensional?: boolean duration?: number | string - renderLabel: (item: PickerOptionItem) => React.ReactNode + renderLabel: (item: PickerOption) => React.ReactNode onChange?: (arg0: PickerOnChangeCallbackParameter) => void } From 639214134b5e93f44839c93f6c4603c63e95a136 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 14:04:21 +0800 Subject: [PATCH 49/54] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/calendar/demos/h5/demo6.tsx | 12 +++++++++--- src/packages/calendar/demos/taro/demo6.tsx | 5 +++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/packages/calendar/demos/h5/demo6.tsx b/src/packages/calendar/demos/h5/demo6.tsx index 767c99c492..083c8161f7 100644 --- a/src/packages/calendar/demos/h5/demo6.tsx +++ b/src/packages/calendar/demos/h5/demo6.tsx @@ -1,5 +1,11 @@ import React, { useRef, useState } from 'react' -import { Cell, Calendar, DatePicker, CalendarDay } from '@nutui/nutui-react' +import { + Cell, + Calendar, + DatePicker, + CalendarDay, + PickerValue, +} from '@nutui/nutui-react' const padZero = (num: number | string, targetLength = 2) => { let str = `${num}` @@ -35,11 +41,11 @@ const Demo6 = () => { const confirm = (values: PickerValue[], options: any[]) => { if (desc.current === 1) { setDesc1( - options.map((option) => padZero(parseInt(option.text))).join(':') + options.map((option) => padZero(parseInt(option.label))).join(':') ) } else { setDesc2( - options.map((option) => padZero(parseInt(option.text))).join(':') + options.map((option) => padZero(parseInt(option.label))).join(':') ) } } diff --git a/src/packages/calendar/demos/taro/demo6.tsx b/src/packages/calendar/demos/taro/demo6.tsx index 0c5a7f273e..831c17378b 100644 --- a/src/packages/calendar/demos/taro/demo6.tsx +++ b/src/packages/calendar/demos/taro/demo6.tsx @@ -4,6 +4,7 @@ import { Calendar, DatePicker, CalendarDay, + PickerValue, } from '@nutui/nutui-react-taro' import { View } from '@tarojs/components' @@ -36,11 +37,11 @@ const Demo6 = () => { const confirm = (values: PickerValue[], options: any[]) => { if (desc.current === 1) { setDesc1( - options.map((option) => padZero(parseInt(option.text))).join(':') + options.map((option) => padZero(parseInt(option.label))).join(':') ) } else { setDesc2( - options.map((option) => padZero(parseInt(option.text))).join(':') + options.map((option) => padZero(parseInt(option.label))).join(':') ) } } From b8546937afe2cec94da260f44601ae5c274b11f8 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 14:09:42 +0800 Subject: [PATCH 50/54] =?UTF-8?q?fix:=20demo=E4=BF=AE=E6=AD=A3=E5=90=8C?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/demos/h5/demo8.tsx | 4 ++-- src/packages/datepicker/demos/taro/demo8.tsx | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/packages/datepicker/demos/h5/demo8.tsx b/src/packages/datepicker/demos/h5/demo8.tsx index d4e8d8f9ab..f821923cec 100644 --- a/src/packages/datepicker/demos/h5/demo8.tsx +++ b/src/packages/datepicker/demos/h5/demo8.tsx @@ -30,7 +30,7 @@ const Demo8 = () => { } return options } - const formatter1 = (type: string, option: PickerOption) => { + const formatter = (type: string, option: PickerOption) => { switch (type) { case 'year': option.label += `年` @@ -63,7 +63,7 @@ const Demo8 = () => { endDate={endDate} visible={show} defaultValue={new Date(`${defaultDescription}`)} - formatter={formatter1} + formatter={formatter} minuteStep={5} filter={filter} onClose={() => setShow(false)} diff --git a/src/packages/datepicker/demos/taro/demo8.tsx b/src/packages/datepicker/demos/taro/demo8.tsx index 42441084f5..2031efe015 100644 --- a/src/packages/datepicker/demos/taro/demo8.tsx +++ b/src/packages/datepicker/demos/taro/demo8.tsx @@ -13,9 +13,13 @@ const Demo8 = () => { const defaultValue = new Date() const defaultDescription = `${defaultValue.getFullYear()}-${ defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` + }-${defaultValue.getDate()} 06:00` const [show, setShow] = useState(false) - const [desc, setDesc] = useState(`${defaultDescription} 00`) + const [desc, setDesc] = useState( + `${defaultValue.getFullYear()}年${ + defaultValue.getMonth() + 1 + }月${defaultValue.getDate()}日 06时` + ) const confirm = (values: PickerValue[], options: PickerOptions) => { setDesc(options.map((option) => option.label).join(' ')) @@ -48,7 +52,7 @@ const Demo8 = () => { return ( <> setShow(true)} /> From 749778714b0e5d828b285826c1c62e2d745a0d8b Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 14:15:58 +0800 Subject: [PATCH 51/54] =?UTF-8?q?fix:=20pickerprops=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/picker/index.taro.ts | 2 +- src/packages/picker/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/picker/index.taro.ts b/src/packages/picker/index.taro.ts index ff0b062781..6f6691f2b3 100644 --- a/src/packages/picker/index.taro.ts +++ b/src/packages/picker/index.taro.ts @@ -1,4 +1,4 @@ import Picker from './picker.taro' -export type { PickerProps } from './picker.taro' +export type { PickerProps } from './types.taro' export default Picker diff --git a/src/packages/picker/index.ts b/src/packages/picker/index.ts index 212b25cb51..1314b31597 100644 --- a/src/packages/picker/index.ts +++ b/src/packages/picker/index.ts @@ -1,4 +1,4 @@ import Picker from './picker' -export type { PickerProps } from './picker' +export type { PickerProps } from './types' export default Picker From 70abe4f86fe2223da7db7a57d54bc519d45bd01c Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 14:54:18 +0800 Subject: [PATCH 52/54] =?UTF-8?q?fix:=20popover=20demo=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/popover/demos/h5/demo4-1.tsx | 38 ++++++++-------- src/packages/popover/demos/h5/demo4.tsx | 49 ++++++++++----------- src/packages/popover/demos/taro/demo4-1.tsx | 41 ++++++++--------- src/packages/popover/demos/taro/demo4.tsx | 48 ++++++++++---------- 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/packages/popover/demos/h5/demo4-1.tsx b/src/packages/popover/demos/h5/demo4-1.tsx index 891b2f4928..0cced79b3d 100644 --- a/src/packages/popover/demos/h5/demo4-1.tsx +++ b/src/packages/popover/demos/h5/demo4-1.tsx @@ -1,15 +1,13 @@ import React, { useState } from 'react' -import { Popover, Cell, Picker, Toast } from '@nutui/nutui-react' +import { + Popover, + Cell, + Picker, + Toast, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react' import { Tips, Close } from '@nutui/icons-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} - const Demo41 = () => { const [baseDesc, setBaseDesc] = useState('') const [showPicker, setShowPicker] = useState(false) @@ -17,12 +15,14 @@ const Demo41 = () => { const [curPostion, setCurPostion] = useState('') const columns = [ - { text: 'top', value: 'top' }, - { text: 'top-start', value: 'top-start' }, - { text: 'top-end', value: 'top-end' }, - { text: 'bottom', value: 'bottom' }, - { text: 'bottom-start', value: 'bottom-start' }, - { text: 'bottom-end', value: 'bottom-end' }, + [ + { label: 'top', value: 'top' }, + { label: 'top-start', value: 'top-start' }, + { label: 'top-end', value: 'top-end' }, + { label: 'bottom', value: 'bottom' }, + { label: 'bottom-start', value: 'bottom-start' }, + { label: 'bottom-end', value: 'bottom-end' }, + ], ] const positionList = [ { @@ -60,13 +60,13 @@ const Demo41 = () => { onConfirm={(list) => { let description = '' list.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) setBaseDesc(description) }} - onChange={(options: PickerOption[]) => { - if (options[0]?.value) { - setCurPostion(options[0].value as string) + onChange={({ selectedOptions }: PickerOnChangeCallbackParameter) => { + if (selectedOptions[0]?.value) { + setCurPostion(selectedOptions[0].value as string) } }} onClose={() => { diff --git a/src/packages/popover/demos/h5/demo4.tsx b/src/packages/popover/demos/h5/demo4.tsx index 27ce80a3c9..d75b9bb232 100644 --- a/src/packages/popover/demos/h5/demo4.tsx +++ b/src/packages/popover/demos/h5/demo4.tsx @@ -1,13 +1,10 @@ import React, { useState } from 'react' -import { Popover, Cell, Picker } from '@nutui/nutui-react' - -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} +import { + Popover, + Cell, + Picker, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react' const Demo4 = () => { const [baseDesc, setBaseDesc] = useState('') @@ -16,18 +13,20 @@ const Demo4 = () => { const [curPostion, setCurPostion] = useState('') const columns = [ - { text: 'top', value: 'top' }, - { text: 'top-start', value: 'top-start' }, - { text: 'top-end', value: 'top-end' }, - { text: 'right', value: 'right' }, - { text: 'right-start', value: 'right-start' }, - { text: 'right-end', value: 'right-end' }, - { text: 'bottom', value: 'bottom' }, - { text: 'bottom-start', value: 'bottom-start' }, - { text: 'bottom-end', value: 'bottom-end' }, - { text: 'left', value: 'left' }, - { text: 'left-start', value: 'left-start' }, - { text: 'left-end', value: 'left-end' }, + [ + { label: 'top', value: 'top' }, + { label: 'top-start', value: 'top-start' }, + { label: 'top-end', value: 'top-end' }, + { label: 'right', value: 'right' }, + { label: 'right-start', value: 'right-start' }, + { label: 'right-end', value: 'right-end' }, + { label: 'bottom', value: 'bottom' }, + { label: 'bottom-start', value: 'bottom-start' }, + { label: 'bottom-end', value: 'bottom-end' }, + { label: 'left', value: 'left' }, + { label: 'left-start', value: 'left-start' }, + { label: 'left-end', value: 'left-end' }, + ], ] const positionList = [ { @@ -57,13 +56,13 @@ const Demo4 = () => { onConfirm={(list) => { let description = '' list.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) setBaseDesc(description) }} - onChange={(options: PickerOption[]) => { - if (options[0]?.value) { - setCurPostion(options[0].value as string) + onChange={({ selectedOptions }: PickerOnChangeCallbackParameter) => { + if (selectedOptions[0]?.value) { + setCurPostion(selectedOptions[0].value as string) } }} onClose={() => { diff --git a/src/packages/popover/demos/taro/demo4-1.tsx b/src/packages/popover/demos/taro/demo4-1.tsx index 04147e9466..5d3f4d4326 100644 --- a/src/packages/popover/demos/taro/demo4-1.tsx +++ b/src/packages/popover/demos/taro/demo4-1.tsx @@ -1,15 +1,14 @@ import React, { useState } from 'react' import { View } from '@tarojs/components' -import { Popover, Cell, Picker, Toast } from '@nutui/nutui-react-taro' +import { + Popover, + Cell, + Picker, + Toast, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react-taro' import { Tips, Close } from '@nutui/icons-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo4 = () => { const [baseDesc, setBaseDesc] = useState('') const [showPicker, setShowPicker] = useState(false) @@ -18,14 +17,16 @@ const Demo4 = () => { const [showToast, SetShowToast] = useState(false) const columns = [ - { text: 'top', value: 'top' }, - { text: 'top-start', value: 'top-start' }, - { text: 'top-end', value: 'top-end' }, - { text: 'right', value: 'right' }, - { text: 'bottom', value: 'bottom' }, - { text: 'bottom-start', value: 'bottom-start' }, - { text: 'bottom-end', value: 'bottom-end' }, - { text: 'left', value: 'left' }, + [ + { label: 'top', value: 'top' }, + { label: 'top-start', value: 'top-start' }, + { label: 'top-end', value: 'top-end' }, + { label: 'right', value: 'right' }, + { label: 'bottom', value: 'bottom' }, + { label: 'bottom-start', value: 'bottom-start' }, + { label: 'bottom-end', value: 'bottom-end' }, + { label: 'left', value: 'left' }, + ], ] const positionList = [ { @@ -63,13 +64,13 @@ const Demo4 = () => { onConfirm={(list) => { let description = '' list.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) setBaseDesc(description) }} - onChange={(options: PickerOption[]) => { - if (options[0]?.value) { - setCurPostion(options[0].value as string) + onChange={({ selectedOptions }: PickerOnChangeCallbackParameter) => { + if (selectedOptions[0]?.value) { + setCurPostion(selectedOptions[0].value as string) } }} onClose={() => { diff --git a/src/packages/popover/demos/taro/demo4.tsx b/src/packages/popover/demos/taro/demo4.tsx index 633170b9aa..430160cf20 100644 --- a/src/packages/popover/demos/taro/demo4.tsx +++ b/src/packages/popover/demos/taro/demo4.tsx @@ -1,14 +1,12 @@ import React, { useState } from 'react' import { View } from '@tarojs/components' -import { Popover, Cell, Picker } from '@nutui/nutui-react-taro' +import { + Popover, + Cell, + Picker, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo4 = () => { const [baseDesc, setBaseDesc] = useState('') const [showPicker, setShowPicker] = useState(false) @@ -16,18 +14,20 @@ const Demo4 = () => { const [curPostion, setCurPostion] = useState('') const columns = [ - { text: 'top', value: 'top' }, - { text: 'top-start', value: 'top-start' }, - { text: 'top-end', value: 'top-end' }, - { text: 'right', value: 'right' }, - { text: 'right-start', value: 'right-start' }, - { text: 'right-end', value: 'right-end' }, - { text: 'bottom', value: 'bottom' }, - { text: 'bottom-start', value: 'bottom-start' }, - { text: 'bottom-end', value: 'bottom-end' }, - { text: 'left', value: 'left' }, - { text: 'left-start', value: 'left-start' }, - { text: 'left-end', value: 'left-end' }, + [ + { label: 'top', value: 'top' }, + { label: 'top-start', value: 'top-start' }, + { label: 'top-end', value: 'top-end' }, + { label: 'right', value: 'right' }, + { label: 'right-start', value: 'right-start' }, + { label: 'right-end', value: 'right-end' }, + { label: 'bottom', value: 'bottom' }, + { label: 'bottom-start', value: 'bottom-start' }, + { label: 'bottom-end', value: 'bottom-end' }, + { label: 'left', value: 'left' }, + { label: 'left-start', value: 'left-start' }, + { label: 'left-end', value: 'left-end' }, + ], ] const positionList = [ { @@ -57,13 +57,13 @@ const Demo4 = () => { onConfirm={(list) => { let description = '' list.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) setBaseDesc(description) }} - onChange={(options: PickerOption[]) => { - if (options[0]?.value) { - setCurPostion(options[0].value as string) + onChange={({ selectedOptions }: PickerOnChangeCallbackParameter) => { + if (selectedOptions[0]?.value) { + setCurPostion(selectedOptions[0].value as string) } }} onClose={() => { From 2db1df17429907bb9b7a70662c6f07b050c62b8a Mon Sep 17 00:00:00 2001 From: songsong <353833373@qq.com> Date: Thu, 27 Feb 2025 15:09:28 +0800 Subject: [PATCH 53/54] =?UTF-8?q?fix:=20picker=E7=9A=84onchange=E4=BF=AE?= =?UTF-8?q?=E6=94=B9popover=20demo=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0=20(?= =?UTF-8?q?#3025)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 初始化pickerview * feat: add pickerView * feat: add tiled demo * refactor: 重构h5 * fix: 适配demo * feat: 增加级联功能 * fix: 级联优化 * feat: pickerview增加级联效果 * fix: 更新taro版本 * fix: 移除冗余代码+解决form中滚动未结束死循环问题 * fix: 移除console * fix: 更新pickerview * fix: update test * fix: update error * fix: update error * fix: update demo * fix: update test * feat: 添加异步demo * fix: update doc * fix: 同步pickerview变更 * fix: 解决小程序控制条样式问题+demo7取消不还原问题 * fix: datepicker类型同步更新 * fix: update test * fix: 修改标签 * feat: add test * fix: update doc and test * fix: adjust the dependency package location * fix: adjust the dependency package path * fix: adjust the dependency package path * fix: adjust the dependency package path * fix: update doc props type * fix: adjust note * fix: adjust the dependency package path * fix: optimize details * fix: update test * fix: path not compilable * fix: key error * fix: update datepicker test * fix: hidden entry * fix: modify config * fix: remove repeat config * fix: 修改组件引入路径 * fix: ts类型和单测修正 * fix: 弹窗动效还原 * fix: remove console * fix: picker的onchange修改popover demo同步更新 --- src/packages/popover/demos/h5/demo4-1.tsx | 38 ++++++++-------- src/packages/popover/demos/h5/demo4.tsx | 49 ++++++++++----------- src/packages/popover/demos/taro/demo4-1.tsx | 41 ++++++++--------- src/packages/popover/demos/taro/demo4.tsx | 48 ++++++++++---------- 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/packages/popover/demos/h5/demo4-1.tsx b/src/packages/popover/demos/h5/demo4-1.tsx index 891b2f4928..0cced79b3d 100644 --- a/src/packages/popover/demos/h5/demo4-1.tsx +++ b/src/packages/popover/demos/h5/demo4-1.tsx @@ -1,15 +1,13 @@ import React, { useState } from 'react' -import { Popover, Cell, Picker, Toast } from '@nutui/nutui-react' +import { + Popover, + Cell, + Picker, + Toast, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react' import { Tips, Close } from '@nutui/icons-react' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} - const Demo41 = () => { const [baseDesc, setBaseDesc] = useState('') const [showPicker, setShowPicker] = useState(false) @@ -17,12 +15,14 @@ const Demo41 = () => { const [curPostion, setCurPostion] = useState('') const columns = [ - { text: 'top', value: 'top' }, - { text: 'top-start', value: 'top-start' }, - { text: 'top-end', value: 'top-end' }, - { text: 'bottom', value: 'bottom' }, - { text: 'bottom-start', value: 'bottom-start' }, - { text: 'bottom-end', value: 'bottom-end' }, + [ + { label: 'top', value: 'top' }, + { label: 'top-start', value: 'top-start' }, + { label: 'top-end', value: 'top-end' }, + { label: 'bottom', value: 'bottom' }, + { label: 'bottom-start', value: 'bottom-start' }, + { label: 'bottom-end', value: 'bottom-end' }, + ], ] const positionList = [ { @@ -60,13 +60,13 @@ const Demo41 = () => { onConfirm={(list) => { let description = '' list.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) setBaseDesc(description) }} - onChange={(options: PickerOption[]) => { - if (options[0]?.value) { - setCurPostion(options[0].value as string) + onChange={({ selectedOptions }: PickerOnChangeCallbackParameter) => { + if (selectedOptions[0]?.value) { + setCurPostion(selectedOptions[0].value as string) } }} onClose={() => { diff --git a/src/packages/popover/demos/h5/demo4.tsx b/src/packages/popover/demos/h5/demo4.tsx index 27ce80a3c9..d75b9bb232 100644 --- a/src/packages/popover/demos/h5/demo4.tsx +++ b/src/packages/popover/demos/h5/demo4.tsx @@ -1,13 +1,10 @@ import React, { useState } from 'react' -import { Popover, Cell, Picker } from '@nutui/nutui-react' - -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} +import { + Popover, + Cell, + Picker, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react' const Demo4 = () => { const [baseDesc, setBaseDesc] = useState('') @@ -16,18 +13,20 @@ const Demo4 = () => { const [curPostion, setCurPostion] = useState('') const columns = [ - { text: 'top', value: 'top' }, - { text: 'top-start', value: 'top-start' }, - { text: 'top-end', value: 'top-end' }, - { text: 'right', value: 'right' }, - { text: 'right-start', value: 'right-start' }, - { text: 'right-end', value: 'right-end' }, - { text: 'bottom', value: 'bottom' }, - { text: 'bottom-start', value: 'bottom-start' }, - { text: 'bottom-end', value: 'bottom-end' }, - { text: 'left', value: 'left' }, - { text: 'left-start', value: 'left-start' }, - { text: 'left-end', value: 'left-end' }, + [ + { label: 'top', value: 'top' }, + { label: 'top-start', value: 'top-start' }, + { label: 'top-end', value: 'top-end' }, + { label: 'right', value: 'right' }, + { label: 'right-start', value: 'right-start' }, + { label: 'right-end', value: 'right-end' }, + { label: 'bottom', value: 'bottom' }, + { label: 'bottom-start', value: 'bottom-start' }, + { label: 'bottom-end', value: 'bottom-end' }, + { label: 'left', value: 'left' }, + { label: 'left-start', value: 'left-start' }, + { label: 'left-end', value: 'left-end' }, + ], ] const positionList = [ { @@ -57,13 +56,13 @@ const Demo4 = () => { onConfirm={(list) => { let description = '' list.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) setBaseDesc(description) }} - onChange={(options: PickerOption[]) => { - if (options[0]?.value) { - setCurPostion(options[0].value as string) + onChange={({ selectedOptions }: PickerOnChangeCallbackParameter) => { + if (selectedOptions[0]?.value) { + setCurPostion(selectedOptions[0].value as string) } }} onClose={() => { diff --git a/src/packages/popover/demos/taro/demo4-1.tsx b/src/packages/popover/demos/taro/demo4-1.tsx index 04147e9466..5d3f4d4326 100644 --- a/src/packages/popover/demos/taro/demo4-1.tsx +++ b/src/packages/popover/demos/taro/demo4-1.tsx @@ -1,15 +1,14 @@ import React, { useState } from 'react' import { View } from '@tarojs/components' -import { Popover, Cell, Picker, Toast } from '@nutui/nutui-react-taro' +import { + Popover, + Cell, + Picker, + Toast, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react-taro' import { Tips, Close } from '@nutui/icons-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo4 = () => { const [baseDesc, setBaseDesc] = useState('') const [showPicker, setShowPicker] = useState(false) @@ -18,14 +17,16 @@ const Demo4 = () => { const [showToast, SetShowToast] = useState(false) const columns = [ - { text: 'top', value: 'top' }, - { text: 'top-start', value: 'top-start' }, - { text: 'top-end', value: 'top-end' }, - { text: 'right', value: 'right' }, - { text: 'bottom', value: 'bottom' }, - { text: 'bottom-start', value: 'bottom-start' }, - { text: 'bottom-end', value: 'bottom-end' }, - { text: 'left', value: 'left' }, + [ + { label: 'top', value: 'top' }, + { label: 'top-start', value: 'top-start' }, + { label: 'top-end', value: 'top-end' }, + { label: 'right', value: 'right' }, + { label: 'bottom', value: 'bottom' }, + { label: 'bottom-start', value: 'bottom-start' }, + { label: 'bottom-end', value: 'bottom-end' }, + { label: 'left', value: 'left' }, + ], ] const positionList = [ { @@ -63,13 +64,13 @@ const Demo4 = () => { onConfirm={(list) => { let description = '' list.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) setBaseDesc(description) }} - onChange={(options: PickerOption[]) => { - if (options[0]?.value) { - setCurPostion(options[0].value as string) + onChange={({ selectedOptions }: PickerOnChangeCallbackParameter) => { + if (selectedOptions[0]?.value) { + setCurPostion(selectedOptions[0].value as string) } }} onClose={() => { diff --git a/src/packages/popover/demos/taro/demo4.tsx b/src/packages/popover/demos/taro/demo4.tsx index 633170b9aa..430160cf20 100644 --- a/src/packages/popover/demos/taro/demo4.tsx +++ b/src/packages/popover/demos/taro/demo4.tsx @@ -1,14 +1,12 @@ import React, { useState } from 'react' import { View } from '@tarojs/components' -import { Popover, Cell, Picker } from '@nutui/nutui-react-taro' +import { + Popover, + Cell, + Picker, + PickerOnChangeCallbackParameter, +} from '@nutui/nutui-react-taro' -interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} const Demo4 = () => { const [baseDesc, setBaseDesc] = useState('') const [showPicker, setShowPicker] = useState(false) @@ -16,18 +14,20 @@ const Demo4 = () => { const [curPostion, setCurPostion] = useState('') const columns = [ - { text: 'top', value: 'top' }, - { text: 'top-start', value: 'top-start' }, - { text: 'top-end', value: 'top-end' }, - { text: 'right', value: 'right' }, - { text: 'right-start', value: 'right-start' }, - { text: 'right-end', value: 'right-end' }, - { text: 'bottom', value: 'bottom' }, - { text: 'bottom-start', value: 'bottom-start' }, - { text: 'bottom-end', value: 'bottom-end' }, - { text: 'left', value: 'left' }, - { text: 'left-start', value: 'left-start' }, - { text: 'left-end', value: 'left-end' }, + [ + { label: 'top', value: 'top' }, + { label: 'top-start', value: 'top-start' }, + { label: 'top-end', value: 'top-end' }, + { label: 'right', value: 'right' }, + { label: 'right-start', value: 'right-start' }, + { label: 'right-end', value: 'right-end' }, + { label: 'bottom', value: 'bottom' }, + { label: 'bottom-start', value: 'bottom-start' }, + { label: 'bottom-end', value: 'bottom-end' }, + { label: 'left', value: 'left' }, + { label: 'left-start', value: 'left-start' }, + { label: 'left-end', value: 'left-end' }, + ], ] const positionList = [ { @@ -57,13 +57,13 @@ const Demo4 = () => { onConfirm={(list) => { let description = '' list.forEach((option: any) => { - description += ` ${option.text}` + description += ` ${option.label}` }) setBaseDesc(description) }} - onChange={(options: PickerOption[]) => { - if (options[0]?.value) { - setCurPostion(options[0].value as string) + onChange={({ selectedOptions }: PickerOnChangeCallbackParameter) => { + if (selectedOptions[0]?.value) { + setCurPostion(selectedOptions[0].value as string) } }} onClose={() => { From 438950a160484325c23f31486388d26fce0fcdcb Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Fri, 28 Feb 2025 14:44:04 +0800 Subject: [PATCH 54/54] =?UTF-8?q?fix:=20taro=E5=AF=BC=E5=87=BA=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=BF=AE=E6=94=B9+demo=20ts=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/calendar/demos/h5/demo6.tsx | 11 ++++++++--- src/packages/calendar/demos/taro/demo6.tsx | 11 ++++++++--- src/packages/datepicker/index.taro.ts | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/packages/calendar/demos/h5/demo6.tsx b/src/packages/calendar/demos/h5/demo6.tsx index 083c8161f7..2e0d5486dd 100644 --- a/src/packages/calendar/demos/h5/demo6.tsx +++ b/src/packages/calendar/demos/h5/demo6.tsx @@ -5,6 +5,7 @@ import { DatePicker, CalendarDay, PickerValue, + PickerOptions, } from '@nutui/nutui-react' const padZero = (num: number | string, targetLength = 2) => { @@ -38,14 +39,18 @@ const Demo6 = () => { const dateArr = [...[chooseData[0][3], chooseData[1][3]]] setDate([...dateArr]) } - const confirm = (values: PickerValue[], options: any[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { if (desc.current === 1) { setDesc1( - options.map((option) => padZero(parseInt(option.label))).join(':') + options + .map((option) => padZero(parseInt(option.label as string))) + .join(':') ) } else { setDesc2( - options.map((option) => padZero(parseInt(option.label))).join(':') + options + .map((option) => padZero(parseInt(option.label as string))) + .join(':') ) } } diff --git a/src/packages/calendar/demos/taro/demo6.tsx b/src/packages/calendar/demos/taro/demo6.tsx index 831c17378b..1e9172218d 100644 --- a/src/packages/calendar/demos/taro/demo6.tsx +++ b/src/packages/calendar/demos/taro/demo6.tsx @@ -5,6 +5,7 @@ import { DatePicker, CalendarDay, PickerValue, + PickerOptions, } from '@nutui/nutui-react-taro' import { View } from '@tarojs/components' @@ -34,14 +35,18 @@ const Demo6 = () => { const dateArr = [...[chooseData[0][3], chooseData[1][3]]] setDate([...dateArr]) } - const confirm = (values: PickerValue[], options: any[]) => { + const confirm = (values: PickerValue[], options: PickerOptions) => { if (desc.current === 1) { setDesc1( - options.map((option) => padZero(parseInt(option.label))).join(':') + options + .map((option) => padZero(parseInt(option.label as string))) + .join(':') ) } else { setDesc2( - options.map((option) => padZero(parseInt(option.label))).join(':') + options + .map((option) => padZero(parseInt(option.label as string))) + .join(':') ) } } diff --git a/src/packages/datepicker/index.taro.ts b/src/packages/datepicker/index.taro.ts index e5aef5354a..d50f1c96e8 100644 --- a/src/packages/datepicker/index.taro.ts +++ b/src/packages/datepicker/index.taro.ts @@ -1,4 +1,4 @@ -import DatePicker from './datepicker' +import DatePicker from './datepicker.taro' export type { DatePickerProps } from './types.taro' export default DatePicker