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/72] =?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/72] 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/72] 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/72] =?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/72] =?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/72] =?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/72] =?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/72] =?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/72] =?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/72] =?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/72] =?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/72] =?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/72] 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/72] 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/72] 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/72] 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/72] 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/72] =?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/72] 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/72] =?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/72] =?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/72] =?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/72] 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/72] =?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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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/72] 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 2b2f34e7b1a5733da2f9800c003cc3ae5b71dbef Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 20 Feb 2025 11:41:14 +0800 Subject: [PATCH 42/72] =?UTF-8?q?fix:=20datepicker=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 401 ++++++++++--------------- src/packages/datepicker/utils.ts | 247 +++++++++++++++ 2 files changed, 414 insertions(+), 234 deletions(-) create mode 100644 src/packages/datepicker/utils.ts diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 47011cdc27..8fffbbf4f4 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -1,30 +1,36 @@ -import React, { FunctionComponent, useState, useEffect } from 'react' -import Picker from '@/packages/picker' -import { PickerOption, 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 React, { FunctionComponent, useState, useEffect } from 'react' // 导入 React 和相关钩子 +import Picker from '@/packages/picker' // 导入 Picker 组件 +import { PickerOption, PickerProps } from '@/packages/picker/index' // 导入 Picker 的类型定义 +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 { + generateDatePickerRanges, + generatePickerColumnWithCallback, + getDatePartValue, + getLastDayOfMonth, +} from './utils' +import { PickerValue } from '../pickerview/types' export interface DatePickerProps extends BasicComponent { - value?: Date - defaultValue?: Date - visible: boolean - title: string - type: - | 'date' + value?: Date // 当前选中的日期 + defaultValue?: Date // 默认选中的日期 + visible: boolean // 是否显示 Picker + title: string // Picker 的标题 + type: // 日期选择器的类型 + | 'date' | 'time' | 'year-month' | 'month-day' | 'datehour' | 'datetime' | 'hour-minutes' - showChinese: boolean - minuteStep: number - startDate: Date - endDate: Date - threeDimensional: boolean + showChinese: boolean // 是否显示中文文本 + minuteStep: number // 分钟步长 + startDate: Date // 日期范围的开始日期 + endDate: Date // 日期范围的结束日期 + threeDimensional: boolean // 是否启用 3D 效果 pickerProps: Partial< Omit< PickerProps, @@ -38,38 +44,44 @@ export interface DatePickerProps extends BasicComponent { | 'onChange' > > - formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] - onClose: () => void - onCancel: () => void + formatter: (type: string, option: PickerOption) => PickerOption // 格式化选项的函数 + filter: (type: string, option: PickerOption[]) => PickerOption[] // 过滤选项的函数 + onClose: () => void // Picker 关闭时的回调 + onCancel: () => void // Picker 取消时的回调 onConfirm: ( + // Picker 确认时的回调 selectedOptions: PickerOption[], selectedValue: (string | number)[] ) => void onChange?: ( + // Picker 值变化时的回调 selectedOptions: PickerOption[], selectedValue: (string | number)[], columnIndex: number ) => void } +// 获取当前年份 const currentYear = new Date().getFullYear() + +// 定义默认属性 const defaultProps = { - ...ComponentDefaults, - visible: false, - title: '', - type: 'date', - showChinese: false, - threeDimensional: true, - minuteStep: 1, - startDate: new Date(currentYear - 10, 0, 1), - endDate: new Date(currentYear + 10, 11, 31), + ...ComponentDefaults, // 继承基础组件的默认值 + visible: false, // 默认不显示 Picker + title: '', // 默认标题为空 + type: 'date', // 默认类型为日期选择器 + showChinese: false, // 默认不显示中文文本 + threeDimensional: true, // 默认启用 3D 效果 + minuteStep: 1, // 默认分钟步长为 1 + startDate: new Date(currentYear - 10, 0, 1), // 默认开始日期为当前年份的前 10 年 + endDate: new Date(currentYear + 10, 11, 31), // 默认结束日期为当前年份的后 10 年 } as DatePickerProps export const DatePicker: FunctionComponent< Partial & Omit, 'onChange' | 'defaultValue'> > = (props) => { + // 解构传入的属性,并合并默认属性 const { startDate, endDate, @@ -94,8 +106,12 @@ export const DatePicker: FunctionComponent< ...defaultProps, ...props, } + + // 获取语言配置 const { locale } = useConfig() const lang = locale.datepicker + + // 定义中文文本映射 const zhCNType: { [key: string]: string } = { day: lang.day, year: lang.year, @@ -104,296 +120,213 @@ export const DatePicker: FunctionComponent< minute: lang.min, seconds: lang.seconds, } + + // 定义 Picker 的值和选项的状态 const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) const [pickerOptions, setPickerOptions] = useState([]) + + // 格式化日期值,确保其在 startDate 和 endDate 之间 const formatValue = (value: Date | null) => { if (!value || (value && !isDate(value))) { - value = startDate + value = startDate // 如果值无效,使用 startDate } return Math.min( Math.max(value.getTime(), startDate.getTime()), endDate.getTime() - ) + ) // 确保日期在范围内 } + + // 使用 usePropsValue 管理选中的日期值 const [selectedDate, setSelectedDate] = usePropsValue({ - value: props.value && formatValue(props.value), - defaultValue: props.defaultValue && formatValue(props.defaultValue), - finalValue: 0, + value: props.value && formatValue(props.value), // 当前值 + defaultValue: props.defaultValue && formatValue(props.defaultValue), // 默认值 + finalValue: 0, // 最终值 }) - function getMonthEndDay(year: number, month: number): number { - return new Date(year, month, 0).getDate() - } - - const getBoundary = (type: string, value: Date) => { - const boundary = type === 'min' ? startDate : endDate - const year = boundary.getFullYear() - let month = 1 - let date = 1 - let hour = 0 - let minute = 0 - - if (type === 'max') { - month = 12 - date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1) - hour = 23 - minute = 59 - } - const seconds = minute - if (value.getFullYear() === year) { - month = boundary.getMonth() + 1 - if (value.getMonth() + 1 === month) { - date = boundary.getDate() - if (value.getDate() === date) { - hour = boundary.getHours() - if (value.getHours() === hour) { - minute = boundary.getMinutes() - } - } - } - } - return { - [`${type}Year`]: year, - [`${type}Month`]: month, - [`${type}Date`]: date, - [`${type}Hour`]: hour, - [`${type}Minute`]: minute, - [`${type}Seconds`]: seconds, - } - } - const ranges = () => { - const selected = new Date(selectedDate) - if (!selected) return [] - const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = - getBoundary('max', selected) - const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = - getBoundary('min', selected) - const result = [ - { - type: 'year', - range: [minYear, maxYear], - }, - { - type: 'month', - range: [minMonth, maxMonth], - }, - { - type: 'day', - range: [minDate, maxDate], - }, - { - type: 'hour', - range: [minHour, maxHour], - }, - { - type: 'minute', - range: [minMinute, maxMinute], - }, - { - type: 'seconds', - range: [minSeconds, maxSeconds], - }, - ] - - switch (type.toLocaleLowerCase()) { - case 'date': - return result.slice(0, 3) - case 'datetime': - return result.slice(0, 5) - case 'time': - return result.slice(3, 6) - case 'year-month': - return result.slice(0, 2) - case 'hour-minutes': - return result.slice(3, 5) - case 'month-day': - return result.slice(1, 3) - case 'datehour': - return result.slice(0, 4) - default: - return result - } - } - - const compareDateChange = ( + /** + * 比较新旧日期,如果不同则更新选中日期并触发 onChange 回调 + * @param currentDate 当前选中的日期时间戳 + * @param newDate 新选中的日期对象 + * @param selectedOptions 选中的选项数组 + * @param index 当前列的索引 + */ + const handleDateComparison = ( currentDate: number, newDate: Date | null, selectedOptions: PickerOption[], index: number ) => { + // 比较当前日期和新日期的时间戳是否相同 const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() + + // 如果新日期有效且与当前日期不同 if (newDate && isDate(newDate)) { if (!isEqual) { - setSelectedDate(formatValue(newDate as Date)) + // 更新选中的日期 + setSelectedDate(formatValue(newDate)) } + + // 触发 onChange 回调,传递选中的选项和日期信息 onChange?.( selectedOptions, [ - String(newDate.getFullYear()), - String(newDate.getMonth() + 1), - String(newDate.getDate()), + String(newDate.getFullYear()), // 年份 + String(newDate.getMonth() + 1), // 月份(注意:getMonth() 返回 0-11,需要加 1) + String(newDate.getDate()), // 日期 ], - index + index // 当前列的索引 ) } } - const handlePickerChange = ( + + /** + * 处理 Picker 值变化的逻辑 + * @param selectedOptions 选中的选项数组 + * @param selectedValue 选中的值数组 + * @param index 当前列的索引 + */ + const handlePickerValueChange = ( selectedOptions: PickerOption[], selectedValue: (number | string)[], index: number ) => { - const rangeType = type.toLocaleLowerCase() + const rangeType = type.toLocaleLowerCase() // 获取日期选择器的类型并转换为小写 + + // 处理日期相关的类型(如 'date', 'datetime', 'datehour' 等) if ( ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( rangeType ) ) { - const formatDate: (number | string)[] = [] + const formattedDate: PickerValue[] = [] + + // 将选中的值转换为数组 selectedValue.forEach((item) => { - formatDate.push(item) + formattedDate.push(item) }) - if (rangeType === 'month-day' && formatDate.length < 3) { - formatDate.unshift( + + // 如果类型是 'month-day' 且缺少年份,补充当前年份 + if (rangeType === 'month-day' && formattedDate.length < 3) { + formattedDate.unshift( new Date(defaultValue || startDate || endDate).getFullYear() ) } - if (rangeType === 'year-month' && formatDate.length < 3) { - formatDate.push( + // 如果类型是 'year-month' 且缺少日期,补充当前日期 + if (rangeType === 'year-month' && formattedDate.length < 3) { + formattedDate.push( new Date(defaultValue || startDate || endDate).getDate() ) } - const year = Number(formatDate[0]) - const month = Number(formatDate[1]) - 1 + // 解析年、月、日 + const year = Number(formattedDate[0]) + const month = Number(formattedDate[1]) - 1 // 月份从 0 开始 const day = Math.min( - Number(formatDate[2]), - getMonthEndDay(Number(formatDate[0]), Number(formatDate[1])) + Number(formattedDate[2]), + getLastDayOfMonth(year, month + 1) // 获取当前月份的最后一天 ) + let date: Date | null = null + + // 根据类型创建日期对象 if ( rangeType === 'date' || rangeType === 'month-day' || rangeType === 'year-month' ) { - date = new Date(year, month, day) + date = new Date(year, month, day) // 仅包含年、月、日 } else if (rangeType === 'datetime') { date = new Date( year, month, day, - Number(formatDate[3]), - Number(formatDate[4]) - ) + Number(formattedDate[3]), + Number(formattedDate[4]) + ) // 包含年、月、日、时、分 } else if (rangeType === 'datehour') { - date = new Date(year, month, day, Number(formatDate[3])) + date = new Date(year, month, day, Number(formattedDate[3])) // 包含年、月、日、时 } - compareDateChange(selectedDate, date, selectedOptions, index) + // 比较并处理日期变化 + handleDateComparison(selectedDate, date, selectedOptions, index) } else { - // 'hour-minutes' 'time' + // 处理时间相关的类型(如 'hour-minutes', 'time') const [hour, minute, seconds] = selectedValue + + // 获取当前日期的年、月、日 const currentDate = new Date(selectedDate) const year = currentDate.getFullYear() const month = currentDate.getMonth() const day = currentDate.getDate() + // 创建日期对象 const date = new Date( year, month, day, Number(hour), Number(minute), - rangeType === 'time' ? Number(seconds) : 0 + rangeType === 'time' ? Number(seconds) : 0 // 如果是 'time' 类型,包含秒数 ) - compareDateChange(selectedDate, date, selectedOptions, index) - } - } - const formatOption = (type: string, value: string | number) => { - if (formatter) { - return formatter(type, { - text: padZero(value, 2), - value: padZero(value, 2), - }) + // 比较并处理日期变化 + handleDateComparison(selectedDate, date, selectedOptions, index) } - const padMin = padZero(value, 2) - const fatter = showChinese ? zhCNType[type] : '' - return { text: padMin + fatter, value: padMin } } - const generateColumn = ( - min: number, - max: number, - val: number | string, - type: string, - columnIndex: number - ) => { - let cmin = min - const arr: Array = [] - let index = 0 - while (cmin <= max) { - arr.push(formatOption(type, cmin)) - - if (type === 'minute') { - cmin += minuteStep - } else { - cmin++ - } - - if (cmin <= Number(val)) { - index++ - } - } + /** + * 生成 Picker 的列数据 + * @returns 返回生成的 Picker 列数据数组 + */ + const generatePickerColumns = (): PickerOption[][] => { + // 生成日期选择器的范围配置 + const dateRanges = generateDatePickerRanges( + type, + selectedDate, + startDate, + endDate + ) - pickerValue[columnIndex] = arr[index]?.value - setPickerValue([...pickerValue]) + // 遍历范围配置,生成每一列的选项 + const columns = dateRanges.map((rangeConfig, columnIndex) => { + // 获取当前列的类型和选中值 + const { type: columnType, range } = rangeConfig + const selectedValue = getDatePartValue(columnType, selectedDate) - if (filter?.(type, arr)) { - return filter?.(type, arr) - } - return arr - } + // 生成当前列的选项,并设置选中值 + const pickerColumn = generatePickerColumnWithCallback( + range[0], // 最小值 + range[1], // 最大值 + selectedValue, // 当前选中的值 + columnType, // 列的类型 + minuteStep, // 分钟步长 + (selectedIndex, options) => { + // 更新 pickerValue 中对应列的值 + pickerValue[columnIndex] = options[selectedIndex]?.value + setPickerValue([...pickerValue]) + }, + showChinese, // 是否显示中文 + zhCNType, // 中文文本映射 + formatter // 自定义格式化函数 + ) - const getDateIndex = (type: string) => { - const date = new Date(selectedDate) - if (!selectedDate) return 0 - if (type === 'year') { - return date.getFullYear() - } - if (type === 'month') { - return date.getMonth() + 1 - } - if (type === 'day') { - return date.getDate() - } - if (type === 'hour') { - return date.getHours() - } - if (type === 'minute') { - return date.getMinutes() - } - if (type === 'seconds') { - return date.getSeconds() - } - return 0 - } + // 如果提供了 filter 函数,则对选项进行过滤 + if (filter?.(columnType, pickerColumn)) { + return filter(columnType, pickerColumn) + } - const columns = () => { - const val = ranges().map((res, columnIndex) => { - return generateColumn( - res.range[0], - res.range[1], - getDateIndex(res.type), - res.type, - columnIndex - ) + // 返回当前列的选项 + return pickerColumn }) - return val || [] + + // 返回生成的列数据,如果为空则返回空数组 + return columns || [] } + // 当 selectedDate、startDate 或 endDate 变化时,重新生成 Picker 列数据 useEffect(() => { - setPickerOptions(columns()) + setPickerOptions(generatePickerColumns()) }, [selectedDate, startDate, endDate]) return ( @@ -414,7 +347,7 @@ export const DatePicker: FunctionComponent< options: PickerOption[], value: (number | string)[], index: number - ) => handlePickerChange(options, value, index)} + ) => handlePickerValueChange(options, value, index)} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepicker/utils.ts b/src/packages/datepicker/utils.ts new file mode 100644 index 0000000000..27d0887647 --- /dev/null +++ b/src/packages/datepicker/utils.ts @@ -0,0 +1,247 @@ +import { padZero } from '@/utils/pad-zero' +import { PickerOption } from '../picker/types' + +/** + * 获取指定年份和月份的最后一天 + * @param year - 年份 + * @param month - 月份(1 到 12) + * @returns 返回该月份的最后一天 + */ +export function getLastDayOfMonth(year: number, month: number): number { + return new Date(year, month, 0).getDate() +} + +/** + * 根据类型和日期值,计算并返回日期边界值(年、月、日、时、分、秒) + * @param type 边界类型:'min' 表示最小值,'max' 表示最大值 + * @param value 传入的日期值 + * @param startDate 传入的开始时间 + * @param endDate 传入的结束时间 + * @returns 返回包含边界值的对象 + */ +export const calculateDateBoundary = ( + type: 'min' | 'max', + value: Date, + startDate: Date, + endDate: Date +) => { + // 根据类型选择边界日期:'min' 使用 startDate,'max' 使用 endDate + const boundary = type === 'min' ? startDate : endDate + + // 获取边界日期的年份 + const year = boundary.getFullYear() + + // 初始化月份、日期、小时和分钟 + const isMax = type === 'max' // 是否为 'max' 类型 + let month = isMax ? 12 : 1 // 'max' 时月份为 12,否则为 1 + let date = isMax + ? getLastDayOfMonth(value.getFullYear(), value.getMonth() + 1) + : 1 // 'max' 时日期为当前月份的最后一天,否则为 1 + let hour = isMax ? 23 : 0 // 'max' 时小时为 23,否则为 0 + let minute = isMax ? 59 : 0 // 'max' 时分钟为 59,否则为 0 + + // 如果传入日期的年份与边界日期的年份相同 + if (value.getFullYear() === year) { + month = boundary.getMonth() + 1 // 使用边界日期的月份 + + // 如果传入日期的月份与边界日期的月份相同 + if (value.getMonth() + 1 === month) { + date = boundary.getDate() // 使用边界日期的日期 + + // 如果传入日期的日期与边界日期的日期相同 + if (value.getDate() === date) { + hour = boundary.getHours() // 使用边界日期的小时 + + // 如果传入日期的小时与边界日期的小时相同 + if (value.getHours() === hour) { + minute = boundary.getMinutes() // 使用边界日期的分钟 + } + } + } + } + + // 返回边界值的对象 + return { + [`${type}Year`]: year, // 返回年份 + [`${type}Month`]: month, // 返回月份 + [`${type}Date`]: date, // 返回日期 + [`${type}Hour`]: hour, // 返回小时 + [`${type}Minute`]: minute, // 返回分钟 + [`${type}Seconds`]: minute, // 返回秒数(与分钟相同) + } +} + +/** + * 根据选中的日期和类型,生成日期选择器的范围配置 + * @returns {Array} 返回日期选择器的范围配置数组 + */ +export const generateDatePickerRanges = ( + type: string, + selectedDate: number, + startDate: Date, + endDate: Date +) => { + // 将选中的日期转换为 Date 对象 + const selected = new Date(selectedDate) + if (!selected) return [] // 如果选中的日期无效,返回空数组 + + // 获取最大和最小边界值 + const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = + calculateDateBoundary('max', selected, startDate, endDate) + const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = + calculateDateBoundary('min', selected, startDate, endDate) + + // 定义完整的日期范围配置 + const fullRanges = [ + { type: 'year', range: [minYear, maxYear] }, // 年份范围 + { type: 'month', range: [minMonth, maxMonth] }, // 月份范围 + { type: 'day', range: [minDate, maxDate] }, // 日期范围 + { type: 'hour', range: [minHour, maxHour] }, // 小时范围 + { type: 'minute', range: [minMinute, maxMinute] }, // 分钟范围 + { type: 'seconds', range: [minSeconds, maxSeconds] }, // 秒数范围 + ] + + // 根据类型返回对应的范围配置 + switch (type.toLocaleLowerCase()) { + case 'date': + return fullRanges.slice(0, 3) // 返回年、月、日 + case 'datetime': + return fullRanges.slice(0, 5) // 返回年、月、日、时、分 + case 'time': + return fullRanges.slice(3, 6) // 返回时、分、秒 + case 'year-month': + return fullRanges.slice(0, 2) // 返回年、月 + case 'hour-minutes': + return fullRanges.slice(3, 5) // 返回时、分 + case 'month-day': + return fullRanges.slice(1, 3) // 返回月、日 + case 'datehour': + return fullRanges.slice(0, 4) // 返回年、月、日、时 + default: + return fullRanges // 返回完整范围 + } +} + +/** + * 根据类型获取日期对象中对应的值 + * @param type 需要获取的日期部分(如 'year', 'month', 'day' 等) + * @param selectedDate 选中的日期时间戳 + * @returns 返回日期对象中对应部分的值,如果类型无效或日期无效,返回 0 + */ +export const getDatePartValue = ( + type: string, + selectedDate: number +): number => { + // 将时间戳转换为 Date 对象 + const date = new Date(selectedDate) + + // 如果选中的日期无效,返回 0 + if (!selectedDate) return 0 + + // 根据类型返回对应的日期部分值 + switch (type) { + case 'year': + return date.getFullYear() // 返回年份 + case 'month': + return date.getMonth() + 1 // 返回月份(注意:getMonth() 返回 0-11,需要加 1) + case 'day': + return date.getDate() // 返回日期 + case 'hour': + return date.getHours() // 返回小时 + case 'minute': + return date.getMinutes() // 返回分钟 + case 'seconds': + return date.getSeconds() // 返回秒数 + default: + return 0 // 如果类型无效,返回 0 + } +} + +/** + * 生成 Picker 的列数据,并触发回调函数返回选中索引 + * @param min 最小值 + * @param max 最大值 + * @param currentValue 当前选中的值 + * @param type 列的类型(如 'year', 'month', 'minute' 等) + * @param minuteStep 分钟步长(仅当类型为 'minute' 时生效) + * @param callback 回调函数,用于返回选中索引 + * @returns 返回生成的 Picker 列数据 + */ +export const generatePickerColumnWithCallback = ( + min: number, + max: number, + currentValue: number | string, + type: string, + minuteStep: number, + callback: (selectedIndex: number, options: PickerOption[]) => void, + showChinese: boolean, + zhCNType: { [key: string]: string }, + formatter?: (type: string, option: PickerOption) => PickerOption +): PickerOption[] => { + let currentMin = min // 当前最小值 + const options: PickerOption[] = [] // 存储生成的选项 + let selectedIndex = 0 // 当前选中值的索引 + + // 遍历从最小值到最大值的范围 + while (currentMin <= max) { + // 将当前值格式化为 PickerOption 并添加到数组中 + options.push( + formatPickerOption(type, currentMin, showChinese, zhCNType, formatter) + ) + + // 根据类型决定步长:如果是分钟,使用 minuteStep,否则步长为 1 + if (type === 'minute') { + currentMin += minuteStep + } else { + currentMin++ + } + + // 如果当前值小于等于选中的值,更新选中索引 + if (currentMin <= Number(currentValue)) { + selectedIndex++ + } + } + + // 触发回调函数,返回选中索引 + callback(selectedIndex, options) + + // 返回生成的选项数组 + return options +} + +/** + * 格式化 Picker 选项 + * @param type 选项类型(如 'year', 'month', 'minute' 等) + * @param value 选项的值 + * @param showChinese 是否显示中文文本 + * @param zhCNType 中文文本映射对象 + * @param formatter 自定义格式化函数 + * @returns 返回格式化后的 Picker 选项 + */ +export const formatPickerOption = ( + type: string, + value: string | number, + showChinese: boolean, + zhCNType: { [key: string]: string }, + formatter?: (type: string, option: PickerOption) => PickerOption +): PickerOption => { + // 如果提供了自定义格式化函数,则使用该函数格式化选项 + if (formatter) { + return formatter(type, { + text: padZero(value, 2), // 补零后的文本 + value: padZero(value, 2), // 补零后的值 + }) + } + + // 补零后的值 + const paddedValue = padZero(value, 2) + + // 如果需要显示中文,添加对应的中文文本 + const chineseText = showChinese ? zhCNType[type] : '' + + // 返回格式化后的选项 + return { + text: paddedValue + chineseText, // 文本 = 补零后的值 + 中文文本 + value: paddedValue, // 值 = 补零后的值 + } +} From 3ccf9bc19d108180980b2df6aefc7477d07ba5f7 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 20 Feb 2025 13:52:45 +0800 Subject: [PATCH 43/72] =?UTF-8?q?fix:=20datepicker=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 126 ++++--------------------- src/packages/datepicker/utils.ts | 118 +++++++++++++++++++++++ 2 files changed, 135 insertions(+), 109 deletions(-) diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 8fffbbf4f4..f626a2482a 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -6,12 +6,12 @@ import { usePropsValue } from '@/hooks/use-props-value' // 导入自定义钩子 import { BasicComponent, ComponentDefaults } from '@/utils/typings' // 导入基础组件类型和默认值 import { isDate } from '@/utils/is-date' // 导入日期验证工具函数 import { + formatValue, generateDatePickerRanges, generatePickerColumnWithCallback, getDatePartValue, - getLastDayOfMonth, + handlePickerValueChange, } from './utils' -import { PickerValue } from '../pickerview/types' export interface DatePickerProps extends BasicComponent { value?: Date // 当前选中的日期 @@ -125,21 +125,11 @@ export const DatePicker: FunctionComponent< const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) const [pickerOptions, setPickerOptions] = useState([]) - // 格式化日期值,确保其在 startDate 和 endDate 之间 - const formatValue = (value: Date | null) => { - if (!value || (value && !isDate(value))) { - value = startDate // 如果值无效,使用 startDate - } - return Math.min( - Math.max(value.getTime(), startDate.getTime()), - endDate.getTime() - ) // 确保日期在范围内 - } - // 使用 usePropsValue 管理选中的日期值 const [selectedDate, setSelectedDate] = usePropsValue({ - value: props.value && formatValue(props.value), // 当前值 - defaultValue: props.defaultValue && formatValue(props.defaultValue), // 默认值 + value: props.value && formatValue(props.value, startDate, endDate), // 当前值 + defaultValue: + props.defaultValue && formatValue(props.defaultValue, startDate, endDate), // 默认值 finalValue: 0, // 最终值 }) @@ -151,19 +141,18 @@ export const DatePicker: FunctionComponent< * @param index 当前列的索引 */ const handleDateComparison = ( - currentDate: number, newDate: Date | null, selectedOptions: PickerOption[], index: number ) => { // 比较当前日期和新日期的时间戳是否相同 - const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() + const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() // 如果新日期有效且与当前日期不同 if (newDate && isDate(newDate)) { if (!isEqual) { // 更新选中的日期 - setSelectedDate(formatValue(newDate)) + setSelectedDate(formatValue(newDate, startDate, endDate)) } // 触发 onChange 回调,传递选中的选项和日期信息 @@ -179,100 +168,19 @@ export const DatePicker: FunctionComponent< } } - /** - * 处理 Picker 值变化的逻辑 - * @param selectedOptions 选中的选项数组 - * @param selectedValue 选中的值数组 - * @param index 当前列的索引 - */ - const handlePickerValueChange = ( + const handleChange = ( selectedOptions: PickerOption[], selectedValue: (number | string)[], index: number ) => { - const rangeType = type.toLocaleLowerCase() // 获取日期选择器的类型并转换为小写 - - // 处理日期相关的类型(如 'date', 'datetime', 'datehour' 等) - if ( - ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( - rangeType - ) - ) { - const formattedDate: PickerValue[] = [] - - // 将选中的值转换为数组 - selectedValue.forEach((item) => { - formattedDate.push(item) - }) - - // 如果类型是 'month-day' 且缺少年份,补充当前年份 - if (rangeType === 'month-day' && formattedDate.length < 3) { - formattedDate.unshift( - new Date(defaultValue || startDate || endDate).getFullYear() - ) - } - - // 如果类型是 'year-month' 且缺少日期,补充当前日期 - if (rangeType === 'year-month' && formattedDate.length < 3) { - formattedDate.push( - new Date(defaultValue || startDate || endDate).getDate() - ) - } - - // 解析年、月、日 - const year = Number(formattedDate[0]) - const month = Number(formattedDate[1]) - 1 // 月份从 0 开始 - const day = Math.min( - Number(formattedDate[2]), - getLastDayOfMonth(year, month + 1) // 获取当前月份的最后一天 - ) - - let date: Date | null = null - - // 根据类型创建日期对象 - if ( - rangeType === 'date' || - rangeType === 'month-day' || - rangeType === 'year-month' - ) { - date = new Date(year, month, day) // 仅包含年、月、日 - } else if (rangeType === 'datetime') { - date = new Date( - year, - month, - day, - Number(formattedDate[3]), - Number(formattedDate[4]) - ) // 包含年、月、日、时、分 - } else if (rangeType === 'datehour') { - date = new Date(year, month, day, Number(formattedDate[3])) // 包含年、月、日、时 - } - - // 比较并处理日期变化 - handleDateComparison(selectedDate, date, selectedOptions, index) - } else { - // 处理时间相关的类型(如 'hour-minutes', 'time') - const [hour, minute, seconds] = selectedValue - - // 获取当前日期的年、月、日 - const currentDate = new Date(selectedDate) - const year = currentDate.getFullYear() - const month = currentDate.getMonth() - const day = currentDate.getDate() - - // 创建日期对象 - const date = new Date( - year, - month, - day, - Number(hour), - Number(minute), - rangeType === 'time' ? Number(seconds) : 0 // 如果是 'time' 类型,包含秒数 - ) - - // 比较并处理日期变化 - handleDateComparison(selectedDate, date, selectedOptions, index) - } + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) } /** @@ -347,7 +255,7 @@ export const DatePicker: FunctionComponent< options: PickerOption[], value: (number | string)[], index: number - ) => handlePickerValueChange(options, value, index)} + ) => handleChange(options, value, index)} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepicker/utils.ts b/src/packages/datepicker/utils.ts index 27d0887647..2caf265623 100644 --- a/src/packages/datepicker/utils.ts +++ b/src/packages/datepicker/utils.ts @@ -1,5 +1,7 @@ import { padZero } from '@/utils/pad-zero' +import { isDate } from '@/utils/is-date' import { PickerOption } from '../picker/types' +import { PickerValue } from '../pickerview/types' /** * 获取指定年份和月份的最后一天 @@ -245,3 +247,119 @@ export const formatPickerOption = ( value: paddedValue, // 值 = 补零后的值 } } + +/** + * 格式化日期值,确保其在 startDate 和 endDate 之间 + */ +export const formatValue = ( + value: Date | null, + startDate: Date, + endDate: Date +) => { + if (!value || (value && !isDate(value))) { + value = startDate // 如果值无效,使用 startDate + } + return Math.min( + Math.max(value.getTime(), startDate.getTime()), + endDate.getTime() + ) // 确保日期在范围内 +} + +/** + * 处理 Picker 值变化的逻辑 + * @param selectedOptions 选中的选项数组 + * @param selectedValue 选中的值数组 + * @param index 当前列的索引 + */ +export const handlePickerValueChange = ( + selectedOptions: PickerOption[], + selectedValue: PickerValue[], + index: number, + type: string, + defaultDate: Date, + handleDateComparison: ( + newDate: Date | null, + selectedOptions: PickerOption[], + index: number + ) => void +) => { + const rangeType = type.toLocaleLowerCase() // 获取日期选择器的类型并转换为小写 + + // 处理日期相关的类型(如 'date', 'datetime', 'datehour' 等) + if ( + ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( + rangeType + ) + ) { + const formattedDate: PickerValue[] = [] + + // 将选中的值转换为数组 + selectedValue.forEach((item) => { + formattedDate.push(item) + }) + + // 如果类型是 'month-day' 且缺少年份,补充当前年份 + if (rangeType === 'month-day' && formattedDate.length < 3) { + formattedDate.unshift(new Date(defaultDate).getFullYear()) + } + + // 如果类型是 'year-month' 且缺少日期,补充当前日期 + if (rangeType === 'year-month' && formattedDate.length < 3) { + formattedDate.push(new Date(defaultDate).getDate()) + } + + // 解析年、月、日 + const year = Number(formattedDate[0]) + const month = Number(formattedDate[1]) - 1 // 月份从 0 开始 + const day = Math.min( + Number(formattedDate[2]), + getLastDayOfMonth(year, month + 1) // 获取当前月份的最后一天 + ) + + let date: Date | null = null + + // 根据类型创建日期对象 + if ( + rangeType === 'date' || + rangeType === 'month-day' || + rangeType === 'year-month' + ) { + date = new Date(year, month, day) // 仅包含年、月、日 + } else if (rangeType === 'datetime') { + date = new Date( + year, + month, + day, + Number(formattedDate[3]), + Number(formattedDate[4]) + ) // 包含年、月、日、时、分 + } else if (rangeType === 'datehour') { + date = new Date(year, month, day, Number(formattedDate[3])) // 包含年、月、日、时 + } + + // 比较并处理日期变化 + handleDateComparison(date, selectedOptions, index) + } else { + // 处理时间相关的类型(如 'hour-minutes', 'time') + const [hour, minute, seconds] = selectedValue + + // 获取当前日期的年、月、日 + const currentDate = new Date(defaultDate) + const year = currentDate.getFullYear() + const month = currentDate.getMonth() + const day = currentDate.getDate() + + // 创建日期对象 + const date = new Date( + year, + month, + day, + Number(hour), + Number(minute), + rangeType === 'time' ? Number(seconds) : 0 // 如果是 'time' 类型,包含秒数 + ) + + // 比较并处理日期变化 + handleDateComparison(date, selectedOptions, index) + } +} From 9d2681698317dad624a9b49fa73cccb076e598f9 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 20 Feb 2025 17:00:27 +0800 Subject: [PATCH 44/72] =?UTF-8?q?fix:=20=E5=8F=96=E6=B6=88=E5=90=8E?= =?UTF-8?q?=E8=BF=98=E5=8E=9F=E5=88=9D=E5=A7=8B=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 57 +++++++++++++++++----- src/packages/datepicker/demos/h5/demo8.tsx | 10 ++-- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index f626a2482a..c7940ecb6f 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -12,6 +12,7 @@ import { getDatePartValue, handlePickerValueChange, } from './utils' +import { PickerValue } from '../nutui.react.build.taro' export interface DatePickerProps extends BasicComponent { value?: Date // 当前选中的日期 @@ -51,12 +52,12 @@ export interface DatePickerProps extends BasicComponent { onConfirm: ( // Picker 确认时的回调 selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedValue: PickerValue[] ) => void onChange?: ( // Picker 值变化时的回调 selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedValue: PickerValue[], columnIndex: number ) => void } @@ -133,6 +134,11 @@ export const DatePicker: FunctionComponent< finalValue: 0, // 最终值 }) + const [innerDate, setInnerDate] = useState(selectedDate) + useEffect(() => { + setInnerDate(selectedDate) + }, [selectedDate]) + /** * 比较新旧日期,如果不同则更新选中日期并触发 onChange 回调 * @param currentDate 当前选中的日期时间戳 @@ -146,13 +152,13 @@ export const DatePicker: FunctionComponent< index: number ) => { // 比较当前日期和新日期的时间戳是否相同 - const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() + const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() // 如果新日期有效且与当前日期不同 if (newDate && isDate(newDate)) { if (!isEqual) { // 更新选中的日期 - setSelectedDate(formatValue(newDate, startDate, endDate)) + setInnerDate(formatValue(newDate, startDate, endDate)) } // 触发 onChange 回调,传递选中的选项和日期信息 @@ -168,6 +174,20 @@ export const DatePicker: FunctionComponent< } } + const handleDateComparison1 = ( + newDate: Date | null, + selectedOptions: PickerOption[], + index: number + ) => { + if (newDate && isDate(newDate)) { + const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() + if (!isEqual) { + console.log('确认时修改值', formatValue(newDate, startDate, endDate)) + setSelectedDate(formatValue(newDate, startDate, endDate)) + } + } + } + const handleChange = ( selectedOptions: PickerOption[], selectedValue: (number | string)[], @@ -191,7 +211,7 @@ export const DatePicker: FunctionComponent< // 生成日期选择器的范围配置 const dateRanges = generateDatePickerRanges( type, - selectedDate, + innerDate, startDate, endDate ) @@ -200,7 +220,7 @@ export const DatePicker: FunctionComponent< const columns = dateRanges.map((rangeConfig, columnIndex) => { // 获取当前列的类型和选中值 const { type: columnType, range } = rangeConfig - const selectedValue = getDatePartValue(columnType, selectedDate) + const selectedValue = getDatePartValue(columnType, innerDate) // 生成当前列的选项,并设置选中值 const pickerColumn = generatePickerColumnWithCallback( @@ -235,7 +255,24 @@ export const DatePicker: FunctionComponent< // 当 selectedDate、startDate 或 endDate 变化时,重新生成 Picker 列数据 useEffect(() => { setPickerOptions(generatePickerColumns()) - }, [selectedDate, startDate, endDate]) + }, [innerDate, startDate, endDate]) + + const handleCancel = () => { + setInnerDate(selectedDate) + onCancel?.() + } + + const handleConfirm = (options: PickerOption[], value: PickerValue[]) => { + handlePickerValueChange( + options, + value, + 0, + type, + defaultValue || startDate || endDate, + handleDateComparison1 + ) + onConfirm?.(options, value) + } return (
@@ -246,11 +283,9 @@ export const DatePicker: FunctionComponent< visible={visible} options={pickerOptions} onClose={onClose} - onCancel={onCancel} + onCancel={handleCancel} value={pickerValue} - onConfirm={(options: PickerOption[], value: (string | number)[]) => - onConfirm && onConfirm(options, value) - } + onConfirm={handleConfirm} onChange={( options: PickerOption[], value: (number | string)[], diff --git a/src/packages/datepicker/demos/h5/demo8.tsx b/src/packages/datepicker/demos/h5/demo8.tsx index a5cce7f3eb..84f2b853e3 100644 --- a/src/packages/datepicker/demos/h5/demo8.tsx +++ b/src/packages/datepicker/demos/h5/demo8.tsx @@ -7,9 +7,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: (string | number)[], options: PickerOption[]) => { setDesc(options.map((option) => option.text).join(' ')) @@ -42,7 +46,7 @@ const Demo8 = () => { return ( <> setShow(true)} /> From 3e208b14512bd7be653029dcfc7ddfeee792fc13 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Fri, 21 Feb 2025 15:18:11 +0800 Subject: [PATCH 45/72] =?UTF-8?q?fix:=20=E5=8F=97=E6=8E=A7=E7=9A=84?= =?UTF-8?q?=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 247 ++++++++------------- src/packages/datepicker/demos/h5/demo1.tsx | 74 +++--- src/packages/datepicker/demos/h5/demo2.tsx | 6 +- src/packages/datepicker/index.ts | 4 +- src/packages/datepicker/types.ts | 55 +++++ src/packages/datepicker/utils.ts | 5 +- 6 files changed, 207 insertions(+), 184 deletions(-) create mode 100644 src/packages/datepicker/types.ts diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index c7940ecb6f..20519663f1 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -1,10 +1,14 @@ -import React, { FunctionComponent, useState, useEffect } from 'react' // 导入 React 和相关钩子 -import Picker from '@/packages/picker' // 导入 Picker 组件 -import { PickerOption, PickerProps } from '@/packages/picker/index' // 导入 Picker 的类型定义 -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 React, { + useState, + useEffect, + ForwardRefRenderFunction, + useImperativeHandle, +} from 'react' +import Picker, { PickerOption } from '@/packages/picker' +import { useConfig } from '@/packages/configprovider' +import { usePropsValue } from '@/hooks/use-props-value' +import { ComponentDefaults } from '@/utils/typings' +import { isDate } from '@/utils/is-date' import { formatValue, generateDatePickerRanges, @@ -12,77 +16,26 @@ import { getDatePartValue, handlePickerValueChange, } from './utils' -import { PickerValue } from '../nutui.react.build.taro' +import { DatePickerActions, DatePickerProps, DatePickerRef } from './types' -export interface DatePickerProps extends BasicComponent { - value?: Date // 当前选中的日期 - defaultValue?: Date // 默认选中的日期 - visible: boolean // 是否显示 Picker - title: string // Picker 的标题 - type: // 日期选择器的类型 - | 'date' - | 'time' - | 'year-month' - | 'month-day' - | 'datehour' - | 'datetime' - | 'hour-minutes' - showChinese: boolean // 是否显示中文文本 - minuteStep: number // 分钟步长 - startDate: Date // 日期范围的开始日期 - endDate: Date // 日期范围的结束日期 - threeDimensional: boolean // 是否启用 3D 效果 - pickerProps: Partial< - Omit< - PickerProps, - | 'defaultValue' - | 'threeDimensional' - | 'title' - | 'value' - | 'onConfirm' - | 'onClose' - | 'onCancel' - | 'onChange' - > - > - formatter: (type: string, option: PickerOption) => PickerOption // 格式化选项的函数 - filter: (type: string, option: PickerOption[]) => PickerOption[] // 过滤选项的函数 - onClose: () => void // Picker 关闭时的回调 - onCancel: () => void // Picker 取消时的回调 - onConfirm: ( - // Picker 确认时的回调 - selectedOptions: PickerOption[], - selectedValue: PickerValue[] - ) => void - onChange?: ( - // Picker 值变化时的回调 - selectedOptions: PickerOption[], - selectedValue: PickerValue[], - columnIndex: number - ) => void -} - -// 获取当前年份 const currentYear = new Date().getFullYear() -// 定义默认属性 const defaultProps = { - ...ComponentDefaults, // 继承基础组件的默认值 - visible: false, // 默认不显示 Picker - title: '', // 默认标题为空 - type: 'date', // 默认类型为日期选择器 - showChinese: false, // 默认不显示中文文本 - threeDimensional: true, // 默认启用 3D 效果 - minuteStep: 1, // 默认分钟步长为 1 - startDate: new Date(currentYear - 10, 0, 1), // 默认开始日期为当前年份的前 10 年 - endDate: new Date(currentYear + 10, 11, 31), // 默认结束日期为当前年份的后 10 年 + ...ComponentDefaults, + visible: false, + title: '', + type: 'date', + showChinese: false, + threeDimensional: true, + minuteStep: 1, + startDate: new Date(currentYear - 10, 0, 1), + endDate: new Date(currentYear + 10, 11, 31), } as DatePickerProps -export const DatePicker: FunctionComponent< - Partial & - Omit, 'onChange' | 'defaultValue'> -> = (props) => { - // 解构传入的属性,并合并默认属性 +const InternalPicker: ForwardRefRenderFunction< + DatePickerRef, + Partial +> = (props, ref) => { const { startDate, endDate, @@ -108,11 +61,9 @@ export const DatePicker: FunctionComponent< ...props, } - // 获取语言配置 const { locale } = useConfig() const lang = locale.datepicker - // 定义中文文本映射 const zhCNType: { [key: string]: string } = { day: lang.day, year: lang.year, @@ -122,75 +73,94 @@ export const DatePicker: FunctionComponent< seconds: lang.seconds, } - // 定义 Picker 的值和选项的状态 const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) const [pickerOptions, setPickerOptions] = useState([]) - // 使用 usePropsValue 管理选中的日期值 const [selectedDate, setSelectedDate] = usePropsValue({ - value: props.value && formatValue(props.value, startDate, endDate), // 当前值 + value: props.value && formatValue(props.value, startDate, endDate), defaultValue: - props.defaultValue && formatValue(props.defaultValue, startDate, endDate), // 默认值 - finalValue: 0, // 最终值 + props.defaultValue && formatValue(props.defaultValue, startDate, endDate), + finalValue: 0, }) const [innerDate, setInnerDate] = useState(selectedDate) - useEffect(() => { - setInnerDate(selectedDate) - }, [selectedDate]) - /** - * 比较新旧日期,如果不同则更新选中日期并触发 onChange 回调 - * @param currentDate 当前选中的日期时间戳 - * @param newDate 新选中的日期对象 - * @param selectedOptions 选中的选项数组 - * @param index 当前列的索引 - */ + const [innerVisible, setInnerVisible] = usePropsValue({ + value: props.visible, + defaultValue: false, + finalValue: false, + }) + + const actions: DatePickerActions = { + open: () => { + setInnerVisible(true) + }, + close: () => { + setInnerVisible(false) + }, + } + + useImperativeHandle(ref, () => actions) + const handleDateComparison = ( newDate: Date | null, selectedOptions: PickerOption[], index: number ) => { - // 比较当前日期和新日期的时间戳是否相同 const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() - - // 如果新日期有效且与当前日期不同 if (newDate && isDate(newDate)) { if (!isEqual) { - // 更新选中的日期 setInnerDate(formatValue(newDate, startDate, endDate)) } - - // 触发 onChange 回调,传递选中的选项和日期信息 onChange?.( selectedOptions, [ - String(newDate.getFullYear()), // 年份 - String(newDate.getMonth() + 1), // 月份(注意:getMonth() 返回 0-11,需要加 1) - String(newDate.getDate()), // 日期 + String(newDate.getFullYear()), + String(newDate.getMonth() + 1), + String(newDate.getDate()), ], - index // 当前列的索引 + index ) } } - const handleDateComparison1 = ( - newDate: Date | null, - selectedOptions: PickerOption[], - index: number - ) => { + const handleConfirmDateComparison = (newDate: Date | null) => { if (newDate && isDate(newDate)) { const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() if (!isEqual) { - console.log('确认时修改值', formatValue(newDate, startDate, endDate)) setSelectedDate(formatValue(newDate, startDate, endDate)) } } } + const handleCancel = () => { + setInnerDate(selectedDate) + onCancel?.() + } + + const handleClose = () => { + setInnerVisible(false) + onClose?.() + } + + const handleConfirm = ( + options: PickerOption[], + value: (string | number)[] + ) => { + handlePickerValueChange( + options, + value, + 0, + type, + defaultValue || startDate || endDate, + handleConfirmDateComparison + ) + onConfirm?.(options, value) + } + const handleChange = ( selectedOptions: PickerOption[], - selectedValue: (number | string)[], + selectedValue: (string | number)[], index: number ) => { handlePickerValueChange( @@ -203,12 +173,7 @@ export const DatePicker: FunctionComponent< ) } - /** - * 生成 Picker 的列数据 - * @returns 返回生成的 Picker 列数据数组 - */ const generatePickerColumns = (): PickerOption[][] => { - // 生成日期选择器的范围配置 const dateRanges = generateDatePickerRanges( type, innerDate, @@ -216,79 +181,58 @@ export const DatePicker: FunctionComponent< endDate ) - // 遍历范围配置,生成每一列的选项 const columns = dateRanges.map((rangeConfig, columnIndex) => { - // 获取当前列的类型和选中值 const { type: columnType, range } = rangeConfig const selectedValue = getDatePartValue(columnType, innerDate) - // 生成当前列的选项,并设置选中值 const pickerColumn = generatePickerColumnWithCallback( - range[0], // 最小值 - range[1], // 最大值 - selectedValue, // 当前选中的值 - columnType, // 列的类型 - minuteStep, // 分钟步长 + range[0], + range[1], + selectedValue, + columnType, + minuteStep, (selectedIndex, options) => { - // 更新 pickerValue 中对应列的值 pickerValue[columnIndex] = options[selectedIndex]?.value setPickerValue([...pickerValue]) }, - showChinese, // 是否显示中文 - zhCNType, // 中文文本映射 - formatter // 自定义格式化函数 + showChinese, + zhCNType, + formatter ) - // 如果提供了 filter 函数,则对选项进行过滤 if (filter?.(columnType, pickerColumn)) { return filter(columnType, pickerColumn) } - // 返回当前列的选项 return pickerColumn }) - // 返回生成的列数据,如果为空则返回空数组 return columns || [] } - // 当 selectedDate、startDate 或 endDate 变化时,重新生成 Picker 列数据 useEffect(() => { - setPickerOptions(generatePickerColumns()) - }, [innerDate, startDate, endDate]) - - const handleCancel = () => { setInnerDate(selectedDate) - onCancel?.() - } + }, [selectedDate]) - const handleConfirm = (options: PickerOption[], value: PickerValue[]) => { - handlePickerValueChange( - options, - value, - 0, - type, - defaultValue || startDate || endDate, - handleDateComparison1 - ) - onConfirm?.(options, value) - } + useEffect(() => { + setPickerOptions(generatePickerColumns()) + }, [innerDate, startDate, endDate]) return (
- {pickerOptions.length > 0 && ( + {pickerOptions.length && innerVisible && ( handleChange(options, value, index)} threeDimensional={threeDimensional} @@ -298,4 +242,7 @@ export const DatePicker: FunctionComponent< ) } -DatePicker.displayName = 'NutDatePicker' +const DatePicker = React.forwardRef>( + InternalPicker +) +export default DatePicker diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index f227a06824..ff87453056 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -1,25 +1,50 @@ import React, { useState } from 'react' import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import isEqual from 'react-fast-compare' + +const useDatePicker = (initialDate: Date) => { + const defaultDateObj = { + year: initialDate.getFullYear(), + month: initialDate.getMonth() + 1, + day: initialDate.getDate(), + } + + const defaultDesc = `${defaultDateObj.year}年${defaultDateObj.month}月${defaultDateObj.day}日` + const defaultValue = Object.values(defaultDateObj).join('-') + + return { defaultDesc, defaultValue } +} const Demo1 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` - const [show, setShow1] = useState(false) - const [desc1, setDesc1] = useState(defaultDescription) - - const [value, setValue] = useState('2023/01/01') + const defaultDate = new Date() + const { defaultDesc: defaultDesc1, defaultValue: defaultValue1 } = + useDatePicker(defaultDate) + const { defaultDesc: defaultDesc2, defaultValue: defaultValue2 } = + useDatePicker(defaultDate) + + const [show1, setShow1] = useState(false) + const [desc1, setDesc1] = useState(defaultDesc1) + + const [value, setValue] = useState(defaultValue2) const [show2, setShow2] = useState(false) - const [desc2, setDesc2] = useState('') - const confirm1 = (values: (string | number)[], options: PickerOption[]) => { - setDesc1(options.map((option) => option.text).join(' ')) - } - const change = (options: PickerOption[], values: (string | number)[]) => { - const v = values.join('/') - setValue(v) - setDesc2(options.map((option) => option.text).join(' ')) - } + const [desc2, setDesc2] = useState(defaultDesc2) + + const handleConfirm = + (setDesc: (desc: string) => void, setValue?: (value: string) => void) => + (options: PickerOption[], values: (string | number)[]) => { + if (setValue) { + if (isEqual(values, ['2026', '02', '21'])) { + setValue('2026/03/22') + setDesc('2026年03月22日') + } else { + setValue(values.join('/')) + setDesc(options.map((option) => option.text).join('')) + } + } else { + setDesc(options.map((option) => option.text).join('')) + } + } + return ( <> { /> setShow1(false)} - onConfirm={(options, values) => { - setShow1(false) - confirm1(values, options) - console.log('onconfirm') - }} + onClose={() => setShow1(false)} + onConfirm={handleConfirm(setDesc1)} /> { showChinese onClose={() => setShow2(false)} threeDimensional={false} - onChange={(options, values) => change(options, values)} + onConfirm={handleConfirm(setDesc2, setValue)} /> ) } + export default Demo1 diff --git a/src/packages/datepicker/demos/h5/demo2.tsx b/src/packages/datepicker/demos/h5/demo2.tsx index bd4350dcde..60a695dc40 100644 --- a/src/packages/datepicker/demos/h5/demo2.tsx +++ b/src/packages/datepicker/demos/h5/demo2.tsx @@ -20,9 +20,9 @@ const Demo2 = () => { /> setShow(false)} diff --git a/src/packages/datepicker/index.ts b/src/packages/datepicker/index.ts index 0c83b853f5..976678232c 100644 --- a/src/packages/datepicker/index.ts +++ b/src/packages/datepicker/index.ts @@ -1,4 +1,4 @@ -import { DatePicker } from './datepicker' +import DatePicker from './datepicker' -export type { DatePickerProps } from './datepicker' +export type { DatePickerProps } from './types' export default DatePicker diff --git a/src/packages/datepicker/types.ts b/src/packages/datepicker/types.ts new file mode 100644 index 0000000000..6e406cc582 --- /dev/null +++ b/src/packages/datepicker/types.ts @@ -0,0 +1,55 @@ +import { BasicComponent } from '@/utils/typings' +import { PickerProps } from '../picker' +import { PickerOption } from '../picker/types' + +export type DatePickerRef = DatePickerActions +export type DatePickerActions = { + open: () => void + close: () => void +} + +export interface DatePickerProps extends BasicComponent { + value?: Date + defaultValue?: Date + visible: boolean + title: string + type: + | 'date' + | 'time' + | 'year-month' + | 'month-day' + | 'datehour' + | 'datetime' + | 'hour-minutes' + showChinese: boolean + minuteStep: number + startDate: Date + endDate: Date + threeDimensional: boolean + pickerProps: Partial< + Omit< + PickerProps, + | 'defaultValue' + | 'threeDimensional' + | 'title' + | 'value' + | 'onConfirm' + | 'onClose' + | 'onCancel' + | 'onChange' + > + > + formatter: (type: string, option: PickerOption) => PickerOption + filter: (type: string, option: PickerOption[]) => PickerOption[] + onClose: () => void + onCancel: () => void + onConfirm: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[] + ) => void + onChange?: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[], + columnIndex: number + ) => void +} diff --git a/src/packages/datepicker/utils.ts b/src/packages/datepicker/utils.ts index 2caf265623..512c4d86a3 100644 --- a/src/packages/datepicker/utils.ts +++ b/src/packages/datepicker/utils.ts @@ -1,7 +1,6 @@ import { padZero } from '@/utils/pad-zero' import { isDate } from '@/utils/is-date' import { PickerOption } from '../picker/types' -import { PickerValue } from '../pickerview/types' /** * 获取指定年份和月份的最后一天 @@ -273,7 +272,7 @@ export const formatValue = ( */ export const handlePickerValueChange = ( selectedOptions: PickerOption[], - selectedValue: PickerValue[], + selectedValue: (string | number)[], index: number, type: string, defaultDate: Date, @@ -291,7 +290,7 @@ export const handlePickerValueChange = ( rangeType ) ) { - const formattedDate: PickerValue[] = [] + const formattedDate: (string | number)[] = [] // 将选中的值转换为数组 selectedValue.forEach((item) => { From f0e4d6cedce4f65f08dbb49370c0b610a574efef Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Fri, 21 Feb 2025 16:00:17 +0800 Subject: [PATCH 46/72] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=E6=8C=87?= =?UTF-8?q?=E4=BB=A4=E5=BC=8F=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 44 ++++++++++++++------------ src/packages/datepicker/types.ts | 1 + src/packages/form/demos/h5/demo7.tsx | 35 ++++++++++++++++++++ 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 20519663f1..4d3d9e00a7 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -55,6 +55,7 @@ const InternalPicker: ForwardRefRenderFunction< threeDimensional, className, style, + children, ...rest } = { ...defaultProps, @@ -219,26 +220,29 @@ const InternalPicker: ForwardRefRenderFunction< }, [innerDate, startDate, endDate]) return ( -
- {pickerOptions.length && innerVisible && ( - handleChange(options, value, index)} - threeDimensional={threeDimensional} - /> - )} -
+ <> + {typeof children === 'function' && children(selectedDate)} +
+ {pickerOptions.length && innerVisible && ( + handleChange(options, value, index)} + threeDimensional={threeDimensional} + /> + )} +
+ ) } diff --git a/src/packages/datepicker/types.ts b/src/packages/datepicker/types.ts index 6e406cc582..1ff1bc5517 100644 --- a/src/packages/datepicker/types.ts +++ b/src/packages/datepicker/types.ts @@ -52,4 +52,5 @@ export interface DatePickerProps extends BasicComponent { selectedValue: (string | number)[], columnIndex: number ) => void + children?: any } diff --git a/src/packages/form/demos/h5/demo7.tsx b/src/packages/form/demos/h5/demo7.tsx index f0387f47a7..ddd6ac960d 100644 --- a/src/packages/form/demos/h5/demo7.tsx +++ b/src/packages/form/demos/h5/demo7.tsx @@ -12,6 +12,7 @@ import { Rate, Range, Toast, + DatePicker, } from '@nutui/nutui-react' import { ArrowRight } from '@nutui/icons-react' @@ -116,6 +117,40 @@ const Demo7 = () => { }}
+ { + console.log('sssss', args[0]) + return new Date(args[1].join('/')) + }} + onClick={(event, ref: any) => { + ref.open() + }} + initialValue={new Date()} + > + + {(value: any) => { + return ( + } + align="center" + /> + ) + }} + + Date: Fri, 21 Feb 2025 16:54:41 +0800 Subject: [PATCH 47/72] refactor: datepicker taro --- src/packages/datepicker/datepicker.taro.tsx | 475 ++++++------------- src/packages/datepicker/demos/taro/demo1.tsx | 68 ++- src/packages/datepicker/demos/taro/demo2.tsx | 7 +- src/packages/datepicker/index.taro.ts | 4 +- src/packages/form/demos/taro/demo7.tsx | 35 ++ 5 files changed, 235 insertions(+), 354 deletions(-) diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 702f1e4e07..56dafee7e7 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -1,59 +1,26 @@ -import React, { FunctionComponent, useState, useEffect } from 'react' +import React, { + useState, + useEffect, + ForwardRefRenderFunction, + useImperativeHandle, +} from 'react' import { View } from '@tarojs/components' -import Picker, { PickerOption, PickerProps } from '@/packages/picker/index.taro' +import Picker, { PickerOption } 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 { ComponentDefaults } from '@/utils/typings' import { isDate } from '@/utils/is-date' -import { padZero } from '@/utils/pad-zero' - -export interface DatePickerProps extends BasicComponent { - value?: Date - defaultValue?: Date - visible: boolean - title: string - type: - | 'date' - | 'time' - | 'year-month' - | 'month-day' - | 'datehour' - | 'datetime' - | 'hour-minutes' - showChinese: boolean - minuteStep: number - startDate: Date - endDate: Date - threeDimensional: boolean - pickerProps: Partial< - Omit< - PickerProps, - | 'defaultValue' - | 'threeDimensional' - | 'title' - | 'value' - | 'onConfirm' - | 'onClose' - | 'onCancel' - | 'onChange' - > - > - formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] - onClose: () => void - onCancel: () => void - onConfirm: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => void - onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => void -} +import { + formatValue, + generateDatePickerRanges, + generatePickerColumnWithCallback, + getDatePartValue, + handlePickerValueChange, +} from './utils' +import { DatePickerActions, DatePickerProps, DatePickerRef } from './types' const currentYear = new Date().getFullYear() + const defaultProps = { ...ComponentDefaults, visible: false, @@ -66,10 +33,10 @@ const defaultProps = { endDate: new Date(currentYear + 10, 11, 31), } as DatePickerProps -export const DatePicker: FunctionComponent< - Partial & - Omit, 'onChange' | 'defaultValue'> -> = (props) => { +const InternalPicker: ForwardRefRenderFunction< + DatePickerRef, + Partial +> = (props, ref) => { const { startDate, endDate, @@ -89,13 +56,16 @@ export const DatePicker: FunctionComponent< threeDimensional, className, style, + children, ...rest } = { ...defaultProps, ...props, } + const { locale } = useConfig() const lang = locale.datepicker + const zhCNType: { [key: string]: string } = { day: lang.day, year: lang.year, @@ -104,127 +74,45 @@ export const DatePicker: FunctionComponent< minute: lang.min, seconds: lang.seconds, } + const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) const [pickerOptions, setPickerOptions] = useState([]) - const formatValue = (value: Date | null) => { - if (!value || (value && !isDate(value))) { - value = startDate - } - return Math.min( - Math.max(value.getTime(), startDate.getTime()), - endDate.getTime() - ) - } + const [selectedDate, setSelectedDate] = usePropsValue({ - value: props.value && formatValue(props.value), - defaultValue: props.defaultValue && formatValue(props.defaultValue), + value: props.value && formatValue(props.value, startDate, endDate), + defaultValue: + props.defaultValue && formatValue(props.defaultValue, startDate, endDate), finalValue: 0, }) - function getMonthEndDay(year: number, month: number): number { - return new Date(year, month, 0).getDate() - } + const [innerDate, setInnerDate] = useState(selectedDate) - const getBoundary = (type: string, value: Date) => { - const boundary = type === 'min' ? startDate : endDate - const year = boundary.getFullYear() - let month = 1 - let date = 1 - let hour = 0 - let minute = 0 + const [innerVisible, setInnerVisible] = usePropsValue({ + value: props.visible, + defaultValue: false, + finalValue: false, + }) - if (type === 'max') { - month = 12 - date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1) - hour = 23 - minute = 59 - } - const seconds = minute - if (value.getFullYear() === year) { - month = boundary.getMonth() + 1 - if (value.getMonth() + 1 === month) { - date = boundary.getDate() - if (value.getDate() === date) { - hour = boundary.getHours() - if (value.getHours() === hour) { - minute = boundary.getMinutes() - } - } - } - } - return { - [`${type}Year`]: year, - [`${type}Month`]: month, - [`${type}Date`]: date, - [`${type}Hour`]: hour, - [`${type}Minute`]: minute, - [`${type}Seconds`]: seconds, - } + const actions: DatePickerActions = { + open: () => { + setInnerVisible(true) + }, + close: () => { + setInnerVisible(false) + }, } - const ranges = () => { - const selected = new Date(selectedDate) - if (!selected) return [] - const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = - getBoundary('max', selected) - const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = - getBoundary('min', selected) - const result = [ - { - type: 'year', - range: [minYear, maxYear], - }, - { - type: 'month', - range: [minMonth, maxMonth], - }, - { - type: 'day', - range: [minDate, maxDate], - }, - { - type: 'hour', - range: [minHour, maxHour], - }, - { - type: 'minute', - range: [minMinute, maxMinute], - }, - { - type: 'seconds', - range: [minSeconds, maxSeconds], - }, - ] - switch (type.toLocaleLowerCase()) { - case 'date': - return result.slice(0, 3) - case 'datetime': - return result.slice(0, 5) - case 'time': - return result.slice(3, 6) - case 'year-month': - return result.slice(0, 2) - case 'hour-minutes': - return result.slice(3, 5) - case 'month-day': - return result.slice(1, 3) - case 'datehour': - return result.slice(0, 4) - default: - return result - } - } + useImperativeHandle(ref, () => actions) - const compareDateChange = ( - currentDate: number, + const handleDateComparison = ( newDate: Date | null, selectedOptions: PickerOption[], index: number ) => { - const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() + const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() if (newDate && isDate(newDate)) { if (!isEqual) { - setSelectedDate(formatValue(newDate as Date)) + setInnerDate(formatValue(newDate, startDate, endDate)) } onChange?.( selectedOptions, @@ -237,194 +125,129 @@ export const DatePicker: FunctionComponent< ) } } - const handlePickerChange = ( - selectedOptions: PickerOption[], - selectedValue: (number | string)[], - index: number - ) => { - const rangeType = type.toLocaleLowerCase() - if ( - ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( - rangeType - ) - ) { - const formatDate: (number | string)[] = [] - selectedValue.forEach((item) => { - formatDate.push(item) - }) - if (rangeType === 'month-day' && formatDate.length < 3) { - formatDate.unshift( - new Date(defaultValue || startDate || endDate).getFullYear() - ) - } - - if (rangeType === 'year-month' && formatDate.length < 3) { - formatDate.push( - new Date(defaultValue || startDate || endDate).getDate() - ) - } - const year = Number(formatDate[0]) - const month = Number(formatDate[1]) - 1 - const day = Math.min( - Number(formatDate[2]), - getMonthEndDay(Number(formatDate[0]), Number(formatDate[1])) - ) - let date: Date | null = null - if ( - rangeType === 'date' || - rangeType === 'month-day' || - rangeType === 'year-month' - ) { - date = new Date(year, month, day) - } else if (rangeType === 'datetime') { - date = new Date( - year, - month, - day, - Number(formatDate[3]), - Number(formatDate[4]) - ) - } else if (rangeType === 'datehour') { - date = new Date(year, month, day, Number(formatDate[3])) + const handleConfirmDateComparison = (newDate: Date | null) => { + if (newDate && isDate(newDate)) { + const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() + if (!isEqual) { + setSelectedDate(formatValue(newDate, startDate, endDate)) } + } + } - compareDateChange(selectedDate, date, selectedOptions, index) - } else { - // 'hour-minutes' 'time' - const [hour, minute, seconds] = selectedValue - const currentDate = new Date(selectedDate) - const year = currentDate.getFullYear() - const month = currentDate.getMonth() - const day = currentDate.getDate() + const handleCancel = () => { + setInnerDate(selectedDate) + onCancel?.() + } - const date = new Date( - year, - month, - day, - Number(hour), - Number(minute), - rangeType === 'time' ? Number(seconds) : 0 - ) - compareDateChange(selectedDate, date, selectedOptions, index) - } + const handleClose = () => { + setInnerVisible(false) + onClose?.() } - const formatOption = (type: string, value: string | number) => { - if (formatter) { - return formatter(type, { - text: padZero(value, 2), - value: padZero(value, 2), - }) - } - const padMin = padZero(value, 2) - const fatter = showChinese ? zhCNType[type] : '' - return { text: padMin + fatter, value: padMin } + const handleConfirm = ( + options: PickerOption[], + value: (string | number)[] + ) => { + handlePickerValueChange( + options, + value, + 0, + type, + defaultValue || startDate || endDate, + handleConfirmDateComparison + ) + onConfirm?.(options, value) } - const generateColumn = ( - min: number, - max: number, - val: number | string, - type: string, - columnIndex: number + const handleChange = ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[], + index: number ) => { - let cmin = min - const arr: Array = [] - let index = 0 - while (cmin <= max) { - arr.push(formatOption(type, cmin)) + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) + } - if (type === 'minute') { - cmin += minuteStep - } else { - cmin++ - } + const generatePickerColumns = (): PickerOption[][] => { + const dateRanges = generateDatePickerRanges( + type, + innerDate, + startDate, + endDate + ) - if (cmin <= Number(val)) { - index++ - } - } + const columns = dateRanges.map((rangeConfig, columnIndex) => { + const { type: columnType, range } = rangeConfig + const selectedValue = getDatePartValue(columnType, innerDate) + + const pickerColumn = generatePickerColumnWithCallback( + range[0], + range[1], + selectedValue, + columnType, + minuteStep, + (selectedIndex, options) => { + pickerValue[columnIndex] = options[selectedIndex]?.value + setPickerValue([...pickerValue]) + }, + showChinese, + zhCNType, + formatter + ) - pickerValue[columnIndex] = arr[index]?.value - setPickerValue([...pickerValue]) + if (filter?.(columnType, pickerColumn)) { + return filter(columnType, pickerColumn) + } - if (filter?.(type, arr)) { - return filter?.(type, arr) - } - return arr - } + return pickerColumn + }) - const getDateIndex = (type: string) => { - const date = new Date(selectedDate) - if (!selectedDate) return 0 - if (type === 'year') { - return date.getFullYear() - } - if (type === 'month') { - return date.getMonth() + 1 - } - if (type === 'day') { - return date.getDate() - } - if (type === 'hour') { - return date.getHours() - } - if (type === 'minute') { - return date.getMinutes() - } - if (type === 'seconds') { - return date.getSeconds() - } - return 0 + return columns || [] } - const columns = () => { - const val = ranges().map((res, columnIndex) => { - return generateColumn( - res.range[0], - res.range[1], - getDateIndex(res.type), - res.type, - columnIndex - ) - }) - return val || [] - } + useEffect(() => { + setInnerDate(selectedDate) + }, [selectedDate]) useEffect(() => { - setPickerOptions(columns()) - }, [selectedDate, startDate, endDate]) + setPickerOptions(generatePickerColumns()) + }, [innerDate, startDate, endDate]) return ( - - {pickerOptions.length > 0 && ( - - onConfirm && onConfirm(options, value) - } - onChange={( - options: PickerOption[], - value: (number | string)[], - index: number - ) => handlePickerChange(options, value, index)} - threeDimensional={threeDimensional} - /> - )} - + <> + {typeof children === 'function' && children(selectedDate)} + + {pickerOptions.length && innerVisible && ( + handleChange(options, value, index)} + threeDimensional={threeDimensional} + /> + )} + + ) } -DatePicker.displayName = 'NutDatePicker' +const DatePicker = React.forwardRef>( + InternalPicker +) +export default DatePicker diff --git a/src/packages/datepicker/demos/taro/demo1.tsx b/src/packages/datepicker/demos/taro/demo1.tsx index dadd406cb0..a81c77dfc5 100644 --- a/src/packages/datepicker/demos/taro/demo1.tsx +++ b/src/packages/datepicker/demos/taro/demo1.tsx @@ -1,25 +1,50 @@ import React, { useState } from 'react' import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import isEqual from 'react-fast-compare' + +const useDatePicker = (initialDate: Date) => { + const defaultDateObj = { + year: initialDate.getFullYear(), + month: initialDate.getMonth() + 1, + day: initialDate.getDate(), + } + + const defaultDesc = `${defaultDateObj.year}年${defaultDateObj.month}月${defaultDateObj.day}日` + const defaultValue = Object.values(defaultDateObj).join('-') + + return { defaultDesc, defaultValue } +} const Demo1 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` + const defaultDate = new Date() + const { defaultDesc: defaultDesc1, defaultValue: defaultValue1 } = + useDatePicker(defaultDate) + const { defaultDesc: defaultDesc2, defaultValue: defaultValue2 } = + useDatePicker(defaultDate) + const [show1, setShow1] = useState(false) - const [desc1, setDesc1] = useState(defaultDescription) + const [desc1, setDesc1] = useState(defaultDesc1) - const [value, setValue] = useState('2023/01/01') + const [value, setValue] = useState(defaultValue2) const [show2, setShow2] = useState(false) - const [desc2, setDesc2] = useState('') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc1(options.map((option) => option.text).join(' ')) - } - const change = (options: PickerOption[], values: (string | number)[]) => { - const v = values.join('/') - setValue(v) - setDesc2(options.map((option) => option.text).join(' ')) - } + const [desc2, setDesc2] = useState(defaultDesc2) + + const handleConfirm = + (setDesc: (desc: string) => void, setValue?: (value: string) => void) => + (options: PickerOption[], values: (string | number)[]) => { + if (setValue) { + if (isEqual(values, ['2026', '02', '21'])) { + setValue('2026/03/22') + setDesc('2026年03月22日') + } else { + setValue(values.join('/')) + setDesc(options.map((option) => option.text).join('')) + } + } else { + setDesc(options.map((option) => option.text).join('')) + } + } + return ( <> { pickerProps={{ popupProps: { zIndex: 1220 }, }} - defaultValue={new Date(`${defaultDescription}`)} + defaultValue={new Date(defaultValue1)} showChinese - onCancel={() => setShow1(false)} - onConfirm={(options, values) => { - setShow1(false) - confirm(values, options) - console.log('onconfirm') - }} + onClose={() => setShow1(false)} + onConfirm={handleConfirm(setDesc1)} /> { showChinese onClose={() => setShow2(false)} threeDimensional={false} - onChange={(options, values) => change(options, values)} + onConfirm={handleConfirm(setDesc2, setValue)} /> ) } + export default Demo1 diff --git a/src/packages/datepicker/demos/taro/demo2.tsx b/src/packages/datepicker/demos/taro/demo2.tsx index 7d3b42edaf..947d497e95 100644 --- a/src/packages/datepicker/demos/taro/demo2.tsx +++ b/src/packages/datepicker/demos/taro/demo2.tsx @@ -7,6 +7,7 @@ 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('-')) } @@ -19,9 +20,9 @@ const Demo2 = () => { /> setShow(false)} diff --git a/src/packages/datepicker/index.taro.ts b/src/packages/datepicker/index.taro.ts index d990ce8459..976678232c 100644 --- a/src/packages/datepicker/index.taro.ts +++ b/src/packages/datepicker/index.taro.ts @@ -1,4 +1,4 @@ -import { DatePicker } from './datepicker.taro' +import DatePicker from './datepicker' -export type { DatePickerProps } from './datepicker.taro' +export type { DatePickerProps } from './types' export default DatePicker diff --git a/src/packages/form/demos/taro/demo7.tsx b/src/packages/form/demos/taro/demo7.tsx index bee2dae660..34141a06fd 100644 --- a/src/packages/form/demos/taro/demo7.tsx +++ b/src/packages/form/demos/taro/demo7.tsx @@ -12,6 +12,7 @@ import { Button, Rate, Range, + DatePicker, } from '@nutui/nutui-react-taro' import { ArrowRight } from '@nutui/icons-react-taro' import { View } from '@tarojs/components' @@ -117,6 +118,40 @@ const Demo7 = () => { }} + { + console.log('sssss', args[0]) + return new Date(args[1].join('/')) + }} + onClick={(event, ref: any) => { + ref.open() + }} + initialValue={new Date()} + > + + {(value: any) => { + return ( + } + align="center" + /> + ) + }} + + Date: Mon, 24 Feb 2025 14:52:31 +0800 Subject: [PATCH 48/72] =?UTF-8?q?chore:=20=E7=A7=BB=E9=99=A4=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/utils.ts | 136 ++++++++++++------------------- 1 file changed, 52 insertions(+), 84 deletions(-) diff --git a/src/packages/datepicker/utils.ts b/src/packages/datepicker/utils.ts index 512c4d86a3..cc82353bc2 100644 --- a/src/packages/datepicker/utils.ts +++ b/src/packages/datepicker/utils.ts @@ -26,36 +26,27 @@ export const calculateDateBoundary = ( startDate: Date, endDate: Date ) => { - // 根据类型选择边界日期:'min' 使用 startDate,'max' 使用 endDate const boundary = type === 'min' ? startDate : endDate - - // 获取边界日期的年份 const year = boundary.getFullYear() - - // 初始化月份、日期、小时和分钟 - const isMax = type === 'max' // 是否为 'max' 类型 - let month = isMax ? 12 : 1 // 'max' 时月份为 12,否则为 1 + const isMax = type === 'max' + let month = isMax ? 12 : 1 let date = isMax ? getLastDayOfMonth(value.getFullYear(), value.getMonth() + 1) - : 1 // 'max' 时日期为当前月份的最后一天,否则为 1 - let hour = isMax ? 23 : 0 // 'max' 时小时为 23,否则为 0 - let minute = isMax ? 59 : 0 // 'max' 时分钟为 59,否则为 0 + : 1 + let hour = isMax ? 23 : 0 + let minute = isMax ? 59 : 0 - // 如果传入日期的年份与边界日期的年份相同 if (value.getFullYear() === year) { - month = boundary.getMonth() + 1 // 使用边界日期的月份 + month = boundary.getMonth() + 1 - // 如果传入日期的月份与边界日期的月份相同 if (value.getMonth() + 1 === month) { - date = boundary.getDate() // 使用边界日期的日期 + date = boundary.getDate() - // 如果传入日期的日期与边界日期的日期相同 if (value.getDate() === date) { - hour = boundary.getHours() // 使用边界日期的小时 + hour = boundary.getHours() - // 如果传入日期的小时与边界日期的小时相同 if (value.getHours() === hour) { - minute = boundary.getMinutes() // 使用边界日期的分钟 + minute = boundary.getMinutes() } } } @@ -63,11 +54,11 @@ export const calculateDateBoundary = ( // 返回边界值的对象 return { - [`${type}Year`]: year, // 返回年份 - [`${type}Month`]: month, // 返回月份 - [`${type}Date`]: date, // 返回日期 - [`${type}Hour`]: hour, // 返回小时 - [`${type}Minute`]: minute, // 返回分钟 + [`${type}Year`]: year, + [`${type}Month`]: month, + [`${type}Date`]: date, + [`${type}Hour`]: hour, + [`${type}Minute`]: minute, [`${type}Seconds`]: minute, // 返回秒数(与分钟相同) } } @@ -82,9 +73,8 @@ export const generateDatePickerRanges = ( startDate: Date, endDate: Date ) => { - // 将选中的日期转换为 Date 对象 const selected = new Date(selectedDate) - if (!selected) return [] // 如果选中的日期无效,返回空数组 + if (!selected) return [] // 获取最大和最小边界值 const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = @@ -92,34 +82,33 @@ export const generateDatePickerRanges = ( const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = calculateDateBoundary('min', selected, startDate, endDate) - // 定义完整的日期范围配置 const fullRanges = [ - { type: 'year', range: [minYear, maxYear] }, // 年份范围 - { type: 'month', range: [minMonth, maxMonth] }, // 月份范围 - { type: 'day', range: [minDate, maxDate] }, // 日期范围 - { type: 'hour', range: [minHour, maxHour] }, // 小时范围 - { type: 'minute', range: [minMinute, maxMinute] }, // 分钟范围 - { type: 'seconds', range: [minSeconds, maxSeconds] }, // 秒数范围 + { type: 'year', range: [minYear, maxYear] }, + { type: 'month', range: [minMonth, maxMonth] }, + { type: 'day', range: [minDate, maxDate] }, + { type: 'hour', range: [minHour, maxHour] }, + { type: 'minute', range: [minMinute, maxMinute] }, + { type: 'seconds', range: [minSeconds, maxSeconds] }, ] // 根据类型返回对应的范围配置 switch (type.toLocaleLowerCase()) { case 'date': - return fullRanges.slice(0, 3) // 返回年、月、日 + return fullRanges.slice(0, 3) case 'datetime': - return fullRanges.slice(0, 5) // 返回年、月、日、时、分 + return fullRanges.slice(0, 5) case 'time': - return fullRanges.slice(3, 6) // 返回时、分、秒 + return fullRanges.slice(3, 6) case 'year-month': - return fullRanges.slice(0, 2) // 返回年、月 + return fullRanges.slice(0, 2) case 'hour-minutes': - return fullRanges.slice(3, 5) // 返回时、分 + return fullRanges.slice(3, 5) case 'month-day': - return fullRanges.slice(1, 3) // 返回月、日 + return fullRanges.slice(1, 3) case 'datehour': - return fullRanges.slice(0, 4) // 返回年、月、日、时 + return fullRanges.slice(0, 4) default: - return fullRanges // 返回完整范围 + return fullRanges } } @@ -133,28 +122,25 @@ export const getDatePartValue = ( type: string, selectedDate: number ): number => { - // 将时间戳转换为 Date 对象 const date = new Date(selectedDate) - // 如果选中的日期无效,返回 0 if (!selectedDate) return 0 - // 根据类型返回对应的日期部分值 switch (type) { case 'year': - return date.getFullYear() // 返回年份 + return date.getFullYear() case 'month': - return date.getMonth() + 1 // 返回月份(注意:getMonth() 返回 0-11,需要加 1) + return date.getMonth() + 1 case 'day': - return date.getDate() // 返回日期 + return date.getDate() case 'hour': - return date.getHours() // 返回小时 + return date.getHours() case 'minute': - return date.getMinutes() // 返回分钟 + return date.getMinutes() case 'seconds': - return date.getSeconds() // 返回秒数 + return date.getSeconds() default: - return 0 // 如果类型无效,返回 0 + return 0 } } @@ -179,9 +165,9 @@ export const generatePickerColumnWithCallback = ( zhCNType: { [key: string]: string }, formatter?: (type: string, option: PickerOption) => PickerOption ): PickerOption[] => { - let currentMin = min // 当前最小值 - const options: PickerOption[] = [] // 存储生成的选项 - let selectedIndex = 0 // 当前选中值的索引 + let currentMin = min + const options: PickerOption[] = [] + let selectedIndex = 0 // 遍历从最小值到最大值的范围 while (currentMin <= max) { @@ -203,10 +189,8 @@ export const generatePickerColumnWithCallback = ( } } - // 触发回调函数,返回选中索引 callback(selectedIndex, options) - // 返回生成的选项数组 return options } @@ -226,24 +210,20 @@ export const formatPickerOption = ( zhCNType: { [key: string]: string }, formatter?: (type: string, option: PickerOption) => PickerOption ): PickerOption => { - // 如果提供了自定义格式化函数,则使用该函数格式化选项 if (formatter) { return formatter(type, { - text: padZero(value, 2), // 补零后的文本 - value: padZero(value, 2), // 补零后的值 + text: padZero(value, 2), + value: padZero(value, 2), }) } - // 补零后的值 const paddedValue = padZero(value, 2) - // 如果需要显示中文,添加对应的中文文本 const chineseText = showChinese ? zhCNType[type] : '' - // 返回格式化后的选项 return { - text: paddedValue + chineseText, // 文本 = 补零后的值 + 中文文本 - value: paddedValue, // 值 = 补零后的值 + text: paddedValue + chineseText, + value: paddedValue, } } @@ -256,12 +236,12 @@ export const formatValue = ( endDate: Date ) => { if (!value || (value && !isDate(value))) { - value = startDate // 如果值无效,使用 startDate + value = startDate } return Math.min( Math.max(value.getTime(), startDate.getTime()), endDate.getTime() - ) // 确保日期在范围内 + ) } /** @@ -282,9 +262,8 @@ export const handlePickerValueChange = ( index: number ) => void ) => { - const rangeType = type.toLocaleLowerCase() // 获取日期选择器的类型并转换为小写 + const rangeType = type.toLocaleLowerCase() - // 处理日期相关的类型(如 'date', 'datetime', 'datehour' 等) if ( ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( rangeType @@ -292,38 +271,33 @@ export const handlePickerValueChange = ( ) { const formattedDate: (string | number)[] = [] - // 将选中的值转换为数组 selectedValue.forEach((item) => { formattedDate.push(item) }) - // 如果类型是 'month-day' 且缺少年份,补充当前年份 if (rangeType === 'month-day' && formattedDate.length < 3) { formattedDate.unshift(new Date(defaultDate).getFullYear()) } - // 如果类型是 'year-month' 且缺少日期,补充当前日期 if (rangeType === 'year-month' && formattedDate.length < 3) { formattedDate.push(new Date(defaultDate).getDate()) } - // 解析年、月、日 const year = Number(formattedDate[0]) - const month = Number(formattedDate[1]) - 1 // 月份从 0 开始 + const month = Number(formattedDate[1]) - 1 const day = Math.min( Number(formattedDate[2]), - getLastDayOfMonth(year, month + 1) // 获取当前月份的最后一天 + getLastDayOfMonth(year, month + 1) ) let date: Date | null = null - // 根据类型创建日期对象 if ( rangeType === 'date' || rangeType === 'month-day' || rangeType === 'year-month' ) { - date = new Date(year, month, day) // 仅包含年、月、日 + date = new Date(year, month, day) } else if (rangeType === 'datetime') { date = new Date( year, @@ -331,34 +305,28 @@ export const handlePickerValueChange = ( day, Number(formattedDate[3]), Number(formattedDate[4]) - ) // 包含年、月、日、时、分 + ) } else if (rangeType === 'datehour') { - date = new Date(year, month, day, Number(formattedDate[3])) // 包含年、月、日、时 + date = new Date(year, month, day, Number(formattedDate[3])) } - // 比较并处理日期变化 handleDateComparison(date, selectedOptions, index) } else { - // 处理时间相关的类型(如 'hour-minutes', 'time') const [hour, minute, seconds] = selectedValue - - // 获取当前日期的年、月、日 const currentDate = new Date(defaultDate) const year = currentDate.getFullYear() const month = currentDate.getMonth() const day = currentDate.getDate() - // 创建日期对象 const date = new Date( year, month, day, Number(hour), Number(minute), - rangeType === 'time' ? Number(seconds) : 0 // 如果是 'time' 类型,包含秒数 + rangeType === 'time' ? Number(seconds) : 0 ) - // 比较并处理日期变化 handleDateComparison(date, selectedOptions, index) } } From a3d2345012cd457558707a8942d142ccc6b7deb6 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Mon, 24 Feb 2025 15:15:54 +0800 Subject: [PATCH 49/72] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E6=B5=8B=E5=BC=95=E5=85=A5=E6=96=B9=E5=BC=8F+picker=E6=8F=90?= =?UTF-8?q?=E5=8F=96pickerProps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datepicker/__test__/datepicker.spec.tsx | 2 +- src/packages/datepicker/types.ts | 3 +- src/packages/picker/index.taro.ts | 3 +- src/packages/picker/index.ts | 3 +- src/packages/picker/picker.taro.tsx | 41 ++----------------- src/packages/picker/picker.tsx | 41 ++----------------- src/packages/picker/types.ts | 38 +++++++++++++++++ 7 files changed, 48 insertions(+), 83 deletions(-) diff --git a/src/packages/datepicker/__test__/datepicker.spec.tsx b/src/packages/datepicker/__test__/datepicker.spec.tsx index 2bfbba3dcc..9952adc4b2 100644 --- a/src/packages/datepicker/__test__/datepicker.spec.tsx +++ b/src/packages/datepicker/__test__/datepicker.spec.tsx @@ -1,7 +1,7 @@ import React from 'react' import { render, waitFor, fireEvent } from '@testing-library/react' import '@testing-library/jest-dom' -import { DatePicker } from '../datepicker' +import DatePicker from '../datepicker' const currentYear = new Date().getFullYear() test('Show Chinese', async () => { diff --git a/src/packages/datepicker/types.ts b/src/packages/datepicker/types.ts index 1ff1bc5517..0af55e6625 100644 --- a/src/packages/datepicker/types.ts +++ b/src/packages/datepicker/types.ts @@ -1,6 +1,5 @@ import { BasicComponent } from '@/utils/typings' -import { PickerProps } from '../picker' -import { PickerOption } from '../picker/types' +import { PickerOption, PickerProps } from '../picker/types' export type DatePickerRef = DatePickerActions export type DatePickerActions = { diff --git a/src/packages/picker/index.taro.ts b/src/packages/picker/index.taro.ts index edc33812b3..596e835ed6 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 type { PickerOption, PickerProps } from './types' export default Picker diff --git a/src/packages/picker/index.ts b/src/packages/picker/index.ts index 2f320e8d33..266c22b803 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 type { PickerOption, PickerProps } from './types' export default Picker diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 19c1fff276..0946696e95 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -2,60 +2,25 @@ import React, { useState, useEffect, useRef, - RefObject, ForwardRefRenderFunction, useImperativeHandle, } from 'react' import classNames from 'classnames' import Taro from '@tarojs/taro' import { View, PickerView, PickerViewColumn } from '@tarojs/components' -import Popup, { PopupProps } from '@/packages/popup/index.taro' +import Popup from '@/packages/popup/index.taro' import PickerPanel from './pickerpanel.taro' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider/index.taro' -import { PickerOption } from './types' +import { PickerOption, PickerProps } from './types' import { usePropsValue } from '@/hooks/use-props-value' -import { BasicComponent, ComponentDefaults } from '@/utils/typings' +import { ComponentDefaults } from '@/utils/typings' export type PickerActions = { open: () => void close: () => void } -export interface PickerProps extends Omit { - visible?: boolean | undefined - title?: string - options: (PickerOption | PickerOption[])[] - value?: (number | string)[] - defaultValue?: (number | string)[] - threeDimensional?: boolean - duration: number | string - closeOnOverlayClick: boolean - popupProps: Partial< - Omit - > - onConfirm?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => 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 - ) => void - children?: any -} - const defaultProps = { ...ComponentDefaults, visible: false, diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 3ffbb5c2e0..e8faab6f04 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -2,59 +2,24 @@ import React, { useState, useEffect, useRef, - RefObject, ForwardRefRenderFunction, useImperativeHandle, } from 'react' import classNames from 'classnames' -import Popup, { PopupProps } from '@/packages/popup/index' +import Popup from '@/packages/popup/index' import { SafeArea } from '@/packages/safearea/safearea' import PickerPanel from './pickerpanel' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider' -import { PickerOption } from './types' +import { PickerOption, PickerProps } from './types' import { usePropsValue } from '@/hooks/use-props-value' -import { BasicComponent, ComponentDefaults } from '@/utils/typings' +import { ComponentDefaults } from '@/utils/typings' export type PickerActions = { open: () => void close: () => void } -export interface PickerProps extends Omit { - visible?: boolean | undefined - title?: string - options: (PickerOption | PickerOption[])[] - value?: (number | string)[] - defaultValue?: (number | string)[] - threeDimensional?: boolean - duration: number | string - closeOnOverlayClick: boolean - popupProps: Partial< - Omit - > - onConfirm?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => 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 - ) => void - children?: any -} - const defaultProps = { ...ComponentDefaults, title: '', diff --git a/src/packages/picker/types.ts b/src/packages/picker/types.ts index 5c7e00b846..f59149fe30 100644 --- a/src/packages/picker/types.ts +++ b/src/packages/picker/types.ts @@ -1,3 +1,7 @@ +import { RefObject } from 'react' +import { PopupProps } from '../popup/types' +import { BasicComponent } from '@/utils/typings' + export interface PickerOption { text: string | number value: string | number @@ -5,3 +9,37 @@ export interface PickerOption { children?: PickerOption[] className?: string | number } + +export interface PickerProps extends Omit { + visible?: boolean | undefined + title?: string + options: (PickerOption | PickerOption[])[] + value?: (number | string)[] + defaultValue?: (number | string)[] + threeDimensional?: boolean + duration: number | string + closeOnOverlayClick: boolean + popupProps: Partial< + Omit + > + onConfirm?: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[] + ) => 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 + ) => void + children?: any +} From fb6dfae502b32b67e3eb754d5fba3c7f526e5cd6 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Mon, 24 Feb 2025 19:20:51 +0800 Subject: [PATCH 50/72] =?UTF-8?q?fix:=20ts=E7=B1=BB=E5=9E=8B=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.taro.tsx | 3 ++- src/packages/datepicker/index.taro.ts | 2 +- src/packages/datepicker/types.taro.ts | 18 ++++++++++++++++++ src/packages/picker/index.taro.ts | 3 ++- src/packages/picker/picker.taro.tsx | 3 ++- src/packages/picker/types.taro.ts | 8 ++++++++ 6 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/packages/datepicker/types.taro.ts create mode 100644 src/packages/picker/types.taro.ts diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 56dafee7e7..837e904f4a 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -17,7 +17,8 @@ import { getDatePartValue, handlePickerValueChange, } from './utils' -import { DatePickerActions, DatePickerProps, DatePickerRef } from './types' +import { DatePickerActions, DatePickerRef } from './types' +import { DatePickerProps } from './types.taro' const currentYear = new Date().getFullYear() diff --git a/src/packages/datepicker/index.taro.ts b/src/packages/datepicker/index.taro.ts index 976678232c..e5aef5354a 100644 --- a/src/packages/datepicker/index.taro.ts +++ b/src/packages/datepicker/index.taro.ts @@ -1,4 +1,4 @@ import DatePicker from './datepicker' -export type { DatePickerProps } from './types' +export type { DatePickerProps } from './types.taro' export default DatePicker diff --git a/src/packages/datepicker/types.taro.ts b/src/packages/datepicker/types.taro.ts new file mode 100644 index 0000000000..d4bfaa00d5 --- /dev/null +++ b/src/packages/datepicker/types.taro.ts @@ -0,0 +1,18 @@ +import { PickerProps } from '@/packages/picker/types.taro' +import { DatePickerProps as DatePickerWebProps } from './types' + +export type DatePickerProps = Omit & { + pickerProps: Partial< + Omit< + PickerProps, + | 'defaultValue' + | 'threeDimensional' + | 'title' + | 'value' + | 'onConfirm' + | 'onClose' + | 'onCancel' + | 'onChange' + > + > +} diff --git a/src/packages/picker/index.taro.ts b/src/packages/picker/index.taro.ts index 596e835ed6..2dc0c9af3c 100644 --- a/src/packages/picker/index.taro.ts +++ b/src/packages/picker/index.taro.ts @@ -1,4 +1,5 @@ import Picker from './picker.taro' -export type { PickerOption, PickerProps } from './types' +export type { PickerOption } from './types' +export type { PickerProps } from './types.taro' export default Picker diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 0946696e95..14283dbfe7 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -12,9 +12,10 @@ import Popup from '@/packages/popup/index.taro' import PickerPanel from './pickerpanel.taro' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider/index.taro' -import { PickerOption, PickerProps } from './types' +import { PickerOption } from './types' import { usePropsValue } from '@/hooks/use-props-value' import { ComponentDefaults } from '@/utils/typings' +import { PickerProps } from './types.taro' export type PickerActions = { open: () => void diff --git a/src/packages/picker/types.taro.ts b/src/packages/picker/types.taro.ts new file mode 100644 index 0000000000..e2b2ea9777 --- /dev/null +++ b/src/packages/picker/types.taro.ts @@ -0,0 +1,8 @@ +import { PopupProps } from '@/packages/popup/types.taro' +import { PickerProps as PickerWebProps } from './types' + +export type PickerProps = Omit & { + popupProps: Partial< + Omit + > +} 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 51/72] =?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 52/72] =?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 53/72] =?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 54/72] 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 6e041002841f969dc3a18f050ef58f5c87853e3d Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 25 Feb 2025 16:03:33 +0800 Subject: [PATCH 55/72] =?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/datepicker/datepicker.taro.tsx | 19 ++++++++++--------- src/packages/datepicker/datepicker.tsx | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 837e904f4a..afdcf64d92 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -166,14 +166,15 @@ const InternalPicker: ForwardRefRenderFunction< selectedValue: (string | number)[], index: number ) => { - handlePickerValueChange( - selectedOptions, - selectedValue, - index, - type, - defaultValue || startDate || endDate, - handleDateComparison - ) + innerVisible && + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) } const generatePickerColumns = (): PickerOption[][] => { @@ -225,7 +226,7 @@ const InternalPicker: ForwardRefRenderFunction< <> {typeof children === 'function' && children(selectedDate)} - {pickerOptions.length && innerVisible && ( + {pickerOptions.length && ( { - handlePickerValueChange( - selectedOptions, - selectedValue, - index, - type, - defaultValue || startDate || endDate, - handleDateComparison - ) + innerVisible && + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) } const generatePickerColumns = (): PickerOption[][] => { @@ -223,7 +224,7 @@ const InternalPicker: ForwardRefRenderFunction< <> {typeof children === 'function' && children(selectedDate)}
- {pickerOptions.length && innerVisible && ( + {pickerOptions.length && ( Date: Wed, 26 Feb 2025 09:51:03 +0800 Subject: [PATCH 56/72] =?UTF-8?q?feat:=20datepickerview=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.json | 36 ++-- src/packages/datepicker/demos/h5/demo1.tsx | 20 +- .../__test__/datepickerview.spec.tsx | 9 + .../datepickerview/datepickerview.scss | 2 + .../datepickerview/datepickerview.taro.tsx | 28 +++ .../datepickerview/datepickerview.tsx | 180 ++++++++++++++++++ src/packages/datepickerview/demo.taro.tsx | 31 +++ src/packages/datepickerview/demo.tsx | 25 +++ .../datepickerview/demos/h5/demo1.tsx | 63 ++++++ .../datepickerview/demos/taro/demo1.tsx | 13 ++ src/packages/datepickerview/doc.md | 36 ++++ src/packages/datepickerview/index.taro.ts | 4 + src/packages/datepickerview/index.ts | 4 + src/packages/datepickerview/types.ts | 27 +++ 14 files changed, 450 insertions(+), 28 deletions(-) create mode 100644 src/packages/datepickerview/__test__/datepickerview.spec.tsx create mode 100644 src/packages/datepickerview/datepickerview.scss create mode 100644 src/packages/datepickerview/datepickerview.taro.tsx create mode 100644 src/packages/datepickerview/datepickerview.tsx create mode 100644 src/packages/datepickerview/demo.taro.tsx create mode 100644 src/packages/datepickerview/demo.tsx create mode 100644 src/packages/datepickerview/demos/h5/demo1.tsx create mode 100644 src/packages/datepickerview/demos/taro/demo1.tsx create mode 100644 src/packages/datepickerview/doc.md create mode 100644 src/packages/datepickerview/index.taro.ts create mode 100644 src/packages/datepickerview/index.ts create mode 100644 src/packages/datepickerview/types.ts diff --git a/src/config.json b/src/config.json index 70976e92f3..4185876156 100644 --- a/src/config.json +++ b/src/config.json @@ -170,7 +170,6 @@ "name": "布局组件", "enName": "layout", "packages": [ - { "version": "3.0.0", "name": "Divider", @@ -224,17 +223,17 @@ "dd": true }, { - "version": "3.0.0", - "name": "Col", - "type": "component", - "cName": "列", - "desc": "布局组件中的列", - "sort": 3, - "show": false, - "exportEmpty": true, - "author": "yushuang24", - "dd": true - }, + "version": "3.0.0", + "name": "Col", + "type": "component", + "cName": "列", + "desc": "布局组件中的列", + "sort": 3, + "show": false, + "exportEmpty": true, + "author": "yushuang24", + "dd": true + }, { "version": "3.0.0", "name": "Row", @@ -642,6 +641,19 @@ "name": "数据录入", "enName": "dataentry", "packages": [ + { + "version": "3.0.0", + "name": "DatePickerView", + "type": "component", + "cName": "选择器视图", + "desc": "DatePickerView 是 DatePicker 的内容区域", + "sort": 16, + "show": true, + "taro": true, + "v15": false, + "dd": false, + "author": "songsong" + }, { "version": "2.0.0", "name": "Menu", diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index ff87453056..9580f4fb02 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -47,22 +47,10 @@ const Demo1 = () => { return ( <> - setShow1(true)} - /> - setShow1(false)} - onConfirm={handleConfirm(setDesc1)} - /> + + + + { + const { container } = render() + expect(container).toMatchSnapshot() +}) diff --git a/src/packages/datepickerview/datepickerview.scss b/src/packages/datepickerview/datepickerview.scss new file mode 100644 index 0000000000..972cefa123 --- /dev/null +++ b/src/packages/datepickerview/datepickerview.scss @@ -0,0 +1,2 @@ +.nut-datepickerview { +} diff --git a/src/packages/datepickerview/datepickerview.taro.tsx b/src/packages/datepickerview/datepickerview.taro.tsx new file mode 100644 index 0000000000..d6f56c529c --- /dev/null +++ b/src/packages/datepickerview/datepickerview.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 { DatePickerViewProps } from './types' + +// import { useConfig } from '@/packages/configprovider/configprovider.taro' +// import { useRtl } from '@/packages/configprovider/index.taro' + +const defaultProps = { + ...ComponentDefaults, +} as DatePickerViewProps +export const DatePickerView: FunctionComponent< + Partial & React.HTMLAttributes +> = (props) => { + // const { locale } = useConfig() + // const rtl = useRtl() + const { className, style } = { ...defaultProps, ...props } + const classPrefix = 'nut-datepickerview' + const cls = classNames(classPrefix, className) + return ( + + DatePickerView + + ) +} + +DatePickerView.displayName = 'NutDatePickerView' diff --git a/src/packages/datepickerview/datepickerview.tsx b/src/packages/datepickerview/datepickerview.tsx new file mode 100644 index 0000000000..177e10a281 --- /dev/null +++ b/src/packages/datepickerview/datepickerview.tsx @@ -0,0 +1,180 @@ +import React, { useState, useEffect, FunctionComponent } from 'react' +import classNames from 'classnames' +import { PickerOptionItem, PickerValue } from '@/packages/pickerview/index' +import { useConfig } from '@/packages/configprovider' +import { usePropsValue } from '@/hooks/use-props-value' +import { ComponentDefaults } from '@/utils/typings' +import { isDate } from '@/utils/is-date' +import { + formatValue, + generateDatePickerRanges, + generatePickerColumnWithCallback, + getDatePartValue, + handlePickerValueChange, +} from '@/packages/datepicker/utils' +import { DatePickerViewProps } from './types' + +const currentYear = new Date().getFullYear() + +const defaultProps = { + ...ComponentDefaults, + type: 'date', + showChinese: false, + threeDimensional: true, + minuteStep: 1, + startDate: new Date(currentYear - 10, 0, 1), + endDate: new Date(currentYear + 10, 11, 31), +} as DatePickerViewProps + +export const DatePickerView: FunctionComponent< + Partial & React.HTMLAttributes +> = (props) => { + const { + startDate, + endDate, + type, + showChinese, + minuteStep, + defaultValue, + formatter, + filter, + onChange, + threeDimensional, + className, + style, + } = { + ...defaultProps, + ...props, + } + + const classPrefix = 'nut-datepickerview' + const cls = classNames(classPrefix, className) + + const { locale } = useConfig() + const lang = locale.datepicker + + const zhCNType: { [key: string]: string } = { + day: lang.day, + year: lang.year, + month: lang.month, + hour: lang.hour, + minute: lang.min, + seconds: lang.seconds, + } + + const [pickerValue, setPickerValue] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) + + const [selectedDate, setSelectedDate] = usePropsValue({ + value: props.value && formatValue(props.value, startDate, endDate), + defaultValue: + props.defaultValue && formatValue(props.defaultValue, startDate, endDate), + finalValue: 0, + }) + + const [innerDate, setInnerDate] = useState(selectedDate) + + const handleDateComparison = ( + newDate: Date | null, + selectedOptions: PickerOption[], + index: number + ) => { + const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() + if (newDate && isDate(newDate)) { + if (!isEqual) { + setInnerDate(formatValue(newDate, startDate, endDate)) + } + onChange?.( + selectedOptions, + [ + String(newDate.getFullYear()), + String(newDate.getMonth() + 1), + String(newDate.getDate()), + ], + index + ) + } + } + + const handleChange = ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[], + index: number + ) => { + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) + } + + const generatePickerColumns = (): PickerOption[][] => { + const dateRanges = generateDatePickerRanges( + type, + innerDate, + startDate, + endDate + ) + + const columns = dateRanges.map((rangeConfig, columnIndex) => { + const { type: columnType, range } = rangeConfig + const selectedValue = getDatePartValue(columnType, innerDate) + + const pickerColumn = generatePickerColumnWithCallback( + range[0], + range[1], + selectedValue, + columnType, + minuteStep, + (selectedIndex, options) => { + pickerValue[columnIndex] = options[selectedIndex]?.value + setPickerValue([...pickerValue]) + }, + showChinese, + zhCNType, + formatter + ) + + if (filter?.(columnType, pickerColumn)) { + return filter(columnType, pickerColumn) + } + + return pickerColumn + }) + + return columns || [] + } + + // useEffect(() => { + // setInnerDate(selectedDate) + // }, [selectedDate]) + + useEffect(() => { + console.log('ssss', innerDate) + setPickerOptions(generatePickerColumns()) + }, [innerDate, startDate, endDate]) + console.log('pickerValue', pickerValue, pickerOptions) + return ( + <> +
+ {pickerOptions.length && ( + handleChange(options, value, index)} + threeDimensional={threeDimensional} + /> + )} +
+ + ) +} + +DatePickerView.displayName = 'NutDatePickerView' diff --git a/src/packages/datepickerview/demo.taro.tsx b/src/packages/datepickerview/demo.taro.tsx new file mode 100644 index 0000000000..e03d997bfb --- /dev/null +++ b/src/packages/datepickerview/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 DatePickerViewDemo = () => { + const [translated] = useTranslate({ + 'zh-CN': { + title: '基础用法', + }, + 'en-US': { + title: 'Basic Usage', + }, + 'zh-TW': { + title: '基礎用法', + }, + }) + return ( + <> +
+ + {translated.title} + + + + ) +} + +export default DatePickerViewDemo diff --git a/src/packages/datepickerview/demo.tsx b/src/packages/datepickerview/demo.tsx new file mode 100644 index 0000000000..bde58f472d --- /dev/null +++ b/src/packages/datepickerview/demo.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { useTranslate } from '@/sites/assets/locale' +import Demo1 from './demos/h5/demo1' + +const DatePickerViewDemo = () => { + const [translated] = useTranslate({ + 'zh-CN': { + title: '基础用法', + }, + 'en-US': { + title: 'Basic Usage', + }, + 'zh-TW': { + title: '基礎用法', + }, + }) + return ( +
+

{translated.title}

+ +
+ ) +} + +export default DatePickerViewDemo diff --git a/src/packages/datepickerview/demos/h5/demo1.tsx b/src/packages/datepickerview/demos/h5/demo1.tsx new file mode 100644 index 0000000000..cc936b8c5c --- /dev/null +++ b/src/packages/datepickerview/demos/h5/demo1.tsx @@ -0,0 +1,63 @@ +import React, { useState } from 'react' +import { DatePickerView, Cell, type PickerOption } from '@nutui/nutui-react' +import isEqual from 'react-fast-compare' + +const useDatePicker = (initialDate: Date) => { + const defaultDateObj = { + year: initialDate.getFullYear(), + month: initialDate.getMonth() + 1, + day: initialDate.getDate(), + } + + const defaultDesc = `${defaultDateObj.year}年${defaultDateObj.month}月${defaultDateObj.day}日` + const defaultValue = Object.values(defaultDateObj).join('-') + + return { defaultDesc, defaultValue } +} + +const Demo1 = () => { + const defaultDate = new Date() + const { defaultDesc: defaultDesc1, defaultValue: defaultValue1 } = + useDatePicker(defaultDate) + const { defaultDesc: defaultDesc2, defaultValue: defaultValue2 } = + useDatePicker(defaultDate) + + const [desc1, setDesc1] = useState(defaultDesc1) + + const [value, setValue] = useState(defaultValue2) + const [desc2, setDesc2] = useState(defaultDesc2) + + const handleChange = + (setDesc: (desc: string) => void, setValue?: (value: string) => void) => + (options: PickerOption[], values: (string | number)[]) => { + if (setValue) { + if (isEqual(values, ['2026', '02', '21'])) { + setValue('2026/03/22') + setDesc('2026年03月22日') + } else { + setValue(values.join('/')) + setDesc(options.map((option) => option.text).join('')) + } + } else { + setDesc(options.map((option) => option.text).join('')) + } + } + + return ( + <> + + + + + + {/* */} + + + ) +} + +export default Demo1 diff --git a/src/packages/datepickerview/demos/taro/demo1.tsx b/src/packages/datepickerview/demos/taro/demo1.tsx new file mode 100644 index 0000000000..208ba8615a --- /dev/null +++ b/src/packages/datepickerview/demos/taro/demo1.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { Cell, DatePickerView } from '@nutui/nutui-react-taro' +// import { Dongdong } from '@nutui/icons-react-taro' + +const Demo1 = () => { + return ( + + + + ) +} + +export default Demo1 diff --git a/src/packages/datepickerview/doc.md b/src/packages/datepickerview/doc.md new file mode 100644 index 0000000000..3ed0771ca6 --- /dev/null +++ b/src/packages/datepickerview/doc.md @@ -0,0 +1,36 @@ +# DatePickerView 选择器视图 + +DatePickerView 是 DatePicker 的内容区域 + +## 引入 + +```tsx +import { name } from '@nutui/nutui-react' +``` + +## 示例代码 + +### 基础用法 + +:::demo + + + +::: + +## DatePickerView + +### Props + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| name | 图标名 | String | - | + +## 主题定制 + +### 样式变量 + +组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。 +| 名称 | 说明 | 默认值 | +| --- | --- | --- | +| --nutui-datepickerview-height | badge 的高度 | `14px` | diff --git a/src/packages/datepickerview/index.taro.ts b/src/packages/datepickerview/index.taro.ts new file mode 100644 index 0000000000..0a801ae90f --- /dev/null +++ b/src/packages/datepickerview/index.taro.ts @@ -0,0 +1,4 @@ +import { DatePickerView } from './datepickerview.taro' + +export type { DatePickerViewProps } from './types' +export default DatePickerView diff --git a/src/packages/datepickerview/index.ts b/src/packages/datepickerview/index.ts new file mode 100644 index 0000000000..a200f20d23 --- /dev/null +++ b/src/packages/datepickerview/index.ts @@ -0,0 +1,4 @@ +import { DatePickerView } from './datepickerview' + +export type { DatePickerViewProps } from './types' +export default DatePickerView diff --git a/src/packages/datepickerview/types.ts b/src/packages/datepickerview/types.ts new file mode 100644 index 0000000000..423b6cb367 --- /dev/null +++ b/src/packages/datepickerview/types.ts @@ -0,0 +1,27 @@ +import { BasicComponent } from '@/utils/typings' +import { PickerOption } from '@/packages/picker/types' + +export interface DatePickerViewProps extends BasicComponent { + value?: Date + defaultValue?: Date + type: + | 'date' + | 'time' + | 'year-month' + | 'month-day' + | 'datehour' + | 'datetime' + | 'hour-minutes' + showChinese: boolean + minuteStep: number + startDate: Date + endDate: Date + threeDimensional: boolean + formatter: (type: string, option: PickerOption) => PickerOption + filter: (type: string, option: PickerOption[]) => PickerOption[] + onChange?: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[], + columnIndex: number + ) => void +} From 7f8309d7c15d07cec97e46b5a152d3b52c6986d2 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 26 Feb 2025 11:10:35 +0800 Subject: [PATCH 57/72] =?UTF-8?q?fix:=20=E5=90=88=E5=B9=B6picker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.taro.tsx | 5 +++-- src/packages/datepicker/demos/h5/demo1.tsx | 20 ++++++++++++++++---- src/packages/datepicker/utils.ts | 8 ++++++-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 2330fbad98..4cf3c985eb 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -5,7 +5,7 @@ import React, { useImperativeHandle, } from 'react' import { View } from '@tarojs/components' -import Picker from '@/packages/picker/index' +import Picker from '@/packages/picker/index.taro' import { useConfig } from '@/packages/configprovider/index.taro' import { usePropsValue } from '@/hooks/use-props-value' import { ComponentDefaults } from '@/utils/typings' @@ -17,7 +17,8 @@ import { getDatePartValue, handlePickerValueChange, } from './utils' -import { DatePickerActions, DatePickerProps, DatePickerRef } from './types' +import { DatePickerProps } from './types.taro' +import { DatePickerActions, DatePickerRef } from './types' import { PickerOptions, PickerValue } from '@/packages/pickerview/types' const currentYear = new Date().getFullYear() diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index da6fa8b4e5..186e9306b0 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -52,10 +52,22 @@ const Demo1 = () => { return ( <> - - - - + setShow1(true)} + /> + setShow1(false)} + onConfirm={handleConfirm(setDesc1)} + /> PickerOption + formatter?: (type: string, option: PickerOption) => PickerOption ): PickerOption => { if (formatter) { return formatter(type, { From fd9d3ec8f2b2291fbb1fc67540b35108fb2bc490 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 26 Feb 2025 11:31:44 +0800 Subject: [PATCH 58/72] fix: ts fixed --- .../datepickerview/datepickerview.tsx | 12 ++++++------ src/packages/datepickerview/types.ts | 13 +++++++++---- src/packages/picker/types.ts | 19 ++++++------------- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/packages/datepickerview/datepickerview.tsx b/src/packages/datepickerview/datepickerview.tsx index 177e10a281..0beabcbc00 100644 --- a/src/packages/datepickerview/datepickerview.tsx +++ b/src/packages/datepickerview/datepickerview.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, FunctionComponent } from 'react' import classNames from 'classnames' -import { PickerOptionItem, PickerValue } from '@/packages/pickerview/index' +import { PickerOptions, PickerValue } from '@/packages/pickerview/index' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/hooks/use-props-value' import { ComponentDefaults } from '@/utils/typings' @@ -63,7 +63,7 @@ export const DatePickerView: FunctionComponent< } const [pickerValue, setPickerValue] = useState([]) - const [pickerOptions, setPickerOptions] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) const [selectedDate, setSelectedDate] = usePropsValue({ value: props.value && formatValue(props.value, startDate, endDate), @@ -76,7 +76,7 @@ export const DatePickerView: FunctionComponent< const handleDateComparison = ( newDate: Date | null, - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, index: number ) => { const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() @@ -97,7 +97,7 @@ export const DatePickerView: FunctionComponent< } const handleChange = ( - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, selectedValue: (string | number)[], index: number ) => { @@ -111,7 +111,7 @@ export const DatePickerView: FunctionComponent< ) } - const generatePickerColumns = (): PickerOption[][] => { + const generatePickerColumns = (): PickerOptions[] => { const dateRanges = generateDatePickerRanges( type, innerDate, @@ -165,7 +165,7 @@ export const DatePickerView: FunctionComponent< value={pickerValue} options={pickerOptions} // onChange={( - // options: PickerOption[], + // options: PickerOptions, // value: (string | number)[], // index: number // ) => handleChange(options, value, index)} diff --git a/src/packages/datepickerview/types.ts b/src/packages/datepickerview/types.ts index 423b6cb367..aa12f3da49 100644 --- a/src/packages/datepickerview/types.ts +++ b/src/packages/datepickerview/types.ts @@ -1,5 +1,9 @@ import { BasicComponent } from '@/utils/typings' -import { PickerOption } from '@/packages/picker/types' +import { + PickerOptions, + PickerValue, + PickerOption, +} from '@/packages/pickerview/types' export interface DatePickerViewProps extends BasicComponent { value?: Date @@ -18,10 +22,11 @@ export interface DatePickerViewProps extends BasicComponent { endDate: Date threeDimensional: boolean formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] + filter: (type: string, options: PickerOptions) => PickerOptions + onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedOptions: PickerOptions, + selectedValue: PickerValue[], columnIndex: number ) => void } diff --git a/src/packages/picker/types.ts b/src/packages/picker/types.ts index 5d41357ef7..adc36c5edb 100644 --- a/src/packages/picker/types.ts +++ b/src/packages/picker/types.ts @@ -1,6 +1,7 @@ import { RefObject } from 'react' import { PopupProps } from '@/packages/popup/types' import { BasicComponent } from '@/utils/typings' +import { PickerOption, PickerOptions } from '@/packages/pickerview/types' export type PickerRef = PickerActions export type PickerActions = { @@ -9,18 +10,10 @@ export type PickerActions = { } export type ColumnsType = 'single' | 'multiple' | 'cascade' -export interface PickerOption { - text: string | number - value: string | number - disabled?: boolean - children?: PickerOption[] - className?: string | number -} - export interface PickerProps extends Omit { visible?: boolean | undefined title?: string - options: (PickerOption | PickerOption[])[] + options: (PickerOption | PickerOptions)[] value?: (number | string)[] defaultValue?: (number | string)[] threeDimensional?: boolean @@ -30,21 +23,21 @@ export interface PickerProps extends Omit { Omit > onConfirm?: ( - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, selectedValue: (string | number)[] ) => void onCancel?: () => void onClose?: ( - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, selectedValue: (string | number)[] ) => void afterClose?: ( - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, selectedValue: (string | number)[], pickerRef: RefObject ) => void onChange?: ( - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, selectedValue: (string | number)[], columnIndex: number ) => void From c1bea9344f9ef877565fd8720a7b833c815dbe78 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 26 Feb 2025 11:52:25 +0800 Subject: [PATCH 59/72] fix: ts lint --- src/packages/datepicker/demos/h5/demo1.tsx | 4 ++-- src/packages/datepicker/demos/taro/demo1.tsx | 9 +++++++-- src/packages/datepicker/doc.en-US.md | 2 +- src/packages/datepicker/doc.md | 2 +- src/packages/datepicker/doc.taro.md | 2 +- src/packages/datepicker/doc.zh-TW.md | 2 +- src/packages/datepicker/types.ts | 4 ++-- .../datepickerview/datepickerview.tsx | 17 +++++++++-------- .../datepickerview/demos/h5/demo1.tsx | 19 ++++++++++++------- 9 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index 186e9306b0..82b73fcd4e 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -2,8 +2,8 @@ import React, { useState } from 'react' import { DatePicker, Cell, - type PickerOption, PickerValue, + PickerOptions, } from '@nutui/nutui-react' import isEqual from 'react-fast-compare' @@ -36,7 +36,7 @@ const Demo1 = () => { const handleConfirm = (setDesc: (desc: string) => void, setValue?: (value: string) => void) => - (options: PickerOption[], values: PickerValue[]) => { + (options: PickerOptions, values: PickerValue[]) => { if (setValue) { if (isEqual(values, ['2026', '02', '21'])) { setValue('2026/03/22') diff --git a/src/packages/datepicker/demos/taro/demo1.tsx b/src/packages/datepicker/demos/taro/demo1.tsx index 82c6c07338..a5690b6864 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, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react-taro' import isEqual from 'react-fast-compare' const useDatePicker = (initialDate: Date) => { @@ -31,7 +36,7 @@ const Demo1 = () => { const handleConfirm = (setDesc: (desc: string) => void, setValue?: (value: string) => void) => - (options: PickerOption[], values: (string | number)[]) => { + (options: PickerOptions, values: PickerValue[]) => { if (setValue) { if (isEqual(values, ['2026', '02', '21'])) { setValue('2026/03/22') diff --git a/src/packages/datepicker/doc.en-US.md b/src/packages/datepicker/doc.en-US.md index 03fa3aff30..3bc1ff0192 100644 --- a/src/packages/datepicker/doc.en-US.md +++ b/src/packages/datepicker/doc.en-US.md @@ -89,7 +89,7 @@ import { DatePicker } from '@nutui/nutui' | endDate | End date | `Date` | `Ten years later on December 31` | | pickerProps | picker props | `object` | `-` | | formatter | Option text formatter | `(type: string, option: PickerOption) => PickerOption` | `-` | -| filter | Option filter | `(type: string, option: PickerOption) => PickerOption[]` | `-` | +| filter | Option filter | `(type: string, option: PickerOption) => PickerOptions` | `-` | | threeDimensional | Turn on 3D effects | `boolean` | `true` | | onConfirm | Emitted when click confirm button. | `(options, value) => void` | `-` | | onCancel | Emitted when click cancel button. | `() => void` | `-` | diff --git a/src/packages/datepicker/doc.md b/src/packages/datepicker/doc.md index c7b3d741cd..c022ff68c7 100644 --- a/src/packages/datepicker/doc.md +++ b/src/packages/datepicker/doc.md @@ -99,7 +99,7 @@ DatetimePicker 通过 type 属性来定义需要选择的时间类型。将 type | endDate | 结束日期 | `Date` | `十年后` | | pickerProps | 透传picker属性 | `object` | `-` | | formatter | 选项格式化函数 | `(type: string, option: PickerOption) => PickerOption` | `-` | -| filter | 选项过滤函数 | `(type: string, option: PickerOption) => PickerOption[]` | `-` | +| filter | 选项过滤函数 | `(type: string, option: PickerOption) => PickerOptions` | `-` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | onConfirm | 点击确定按钮时触发 | `(options, value) => void` | `-` | | onCancel | 点击取消按钮时触发 | `() => void` | `-` | diff --git a/src/packages/datepicker/doc.taro.md b/src/packages/datepicker/doc.taro.md index 766a1230b4..5957b3b220 100644 --- a/src/packages/datepicker/doc.taro.md +++ b/src/packages/datepicker/doc.taro.md @@ -99,7 +99,7 @@ DatetimePicker 通过 type 属性来定义需要选择的时间类型。将 type | endDate | 结束日期 | `Date` | `十年后` | | formatter | 选项格式化函数 | `(type: string, option: PickerOption) => PickerOption` | `-` | | pickerProps | 透传picker属性 | `object` | `-` | -| filter | 选项过滤函数 | `(type: string, option: PickerOption) => PickerOption[]` | `-` | +| filter | 选项过滤函数 | `(type: string, option: PickerOption) => PickerOptions` | `-` | | threeDimensional | 是否开启3D效果 | `boolean` | `true` | | onConfirm | 点击确定按钮时触发 | `(options, value) => void` | `-` | | onCancel | 点击取消按钮时触发 | `() => void` | `-` | diff --git a/src/packages/datepicker/doc.zh-TW.md b/src/packages/datepicker/doc.zh-TW.md index c364f20277..5d21efc10d 100644 --- a/src/packages/datepicker/doc.zh-TW.md +++ b/src/packages/datepicker/doc.zh-TW.md @@ -99,7 +99,7 @@ DatetimePicker 通過 type 屬性來定義需要選擇的時間類型。將 type | endDate | 結束日期 | `Date` | `十年後` | | formatter | 選項格式化函數 | `(type: string, option: PickerOption) => PickerOption` | `-` | | pickerProps | 透传 picker 屬性 | `object` | `-` | -| filter | 選項過濾函數 | `(type: string, option: PickerOption) => PickerOption[]` | `-` | +| filter | 選項過濾函數 | `(type: string, option: PickerOption) => PickerOptions` | `-` | | threeDimensional | 是否開啟3D效果 | `boolean` | `true` | | onConfirm | 點擊確定按鈕時觸發 | `(options, value) => void` | `-` | | onCancel | 點擊取消按鈕時觸發 | `() => void` | `-` | diff --git a/src/packages/datepicker/types.ts b/src/packages/datepicker/types.ts index 62b68cfe51..6d2a44ac12 100644 --- a/src/packages/datepicker/types.ts +++ b/src/packages/datepicker/types.ts @@ -44,11 +44,11 @@ export interface DatePickerProps extends BasicComponent { onClose: () => void onCancel: () => void onConfirm: ( - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, selectedValue: PickerValue[] ) => void onChange?: ( - selectedOptions: PickerOption[], + selectedOptions: PickerOptions, selectedValue: PickerValue[], columnIndex: number ) => void diff --git a/src/packages/datepickerview/datepickerview.tsx b/src/packages/datepickerview/datepickerview.tsx index 0beabcbc00..4a3be2b474 100644 --- a/src/packages/datepickerview/datepickerview.tsx +++ b/src/packages/datepickerview/datepickerview.tsx @@ -1,6 +1,9 @@ import React, { useState, useEffect, FunctionComponent } from 'react' import classNames from 'classnames' -import { PickerOptions, PickerValue } from '@/packages/pickerview/index' +import PickerView, { + PickerOptions, + PickerValue, +} from '@/packages/pickerview/index' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/hooks/use-props-value' import { ComponentDefaults } from '@/utils/typings' @@ -98,7 +101,7 @@ export const DatePickerView: FunctionComponent< const handleChange = ( selectedOptions: PickerOptions, - selectedValue: (string | number)[], + selectedValue: PickerValue[], index: number ) => { handlePickerValueChange( @@ -161,14 +164,12 @@ export const DatePickerView: FunctionComponent< <>
{pickerOptions.length && ( - handleChange(options, value, index)} + onChange={({ value, index, selectedOptions }) => { + handleChange(selectedOptions, value, index) + }} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepickerview/demos/h5/demo1.tsx b/src/packages/datepickerview/demos/h5/demo1.tsx index cc936b8c5c..87e2a24cab 100644 --- a/src/packages/datepickerview/demos/h5/demo1.tsx +++ b/src/packages/datepickerview/demos/h5/demo1.tsx @@ -1,5 +1,10 @@ import React, { useState } from 'react' -import { DatePickerView, Cell, type PickerOption } from '@nutui/nutui-react' +import { + DatePickerView, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react' import isEqual from 'react-fast-compare' const useDatePicker = (initialDate: Date) => { @@ -29,17 +34,17 @@ const Demo1 = () => { const handleChange = (setDesc: (desc: string) => void, setValue?: (value: string) => void) => - (options: PickerOption[], values: (string | number)[]) => { + (options: PickerOptions, values: PickerValue[]) => { if (setValue) { if (isEqual(values, ['2026', '02', '21'])) { setValue('2026/03/22') setDesc('2026年03月22日') } else { setValue(values.join('/')) - setDesc(options.map((option) => option.text).join('')) + setDesc(options.map((option) => option.label).join('')) } } else { - setDesc(options.map((option) => option.text).join('')) + setDesc(options.map((option) => option.label).join('')) } } @@ -50,11 +55,11 @@ const Demo1 = () => { - {/* */} + onChange={handleChange(setDesc2, setValue)} + /> ) From 6c1111529c413c50f3abf6b1757b7bb74f2361c2 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 26 Feb 2025 20:20:37 +0800 Subject: [PATCH 60/72] =?UTF-8?q?fix:=20=E5=88=9D=E7=89=88=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datepickerview/datepickerview.scss | 2 + .../datepickerview/datepickerview.taro.tsx | 172 +++++++++++++++++- .../datepickerview/datepickerview.tsx | 78 ++++---- src/packages/datepickerview/demo.tsx | 64 ++++++- .../datepickerview/demos/h5/demo1.tsx | 43 +++-- .../datepickerview/demos/h5/demo2.tsx | 34 ++++ .../datepickerview/demos/h5/demo3.tsx | 44 +++++ .../datepickerview/demos/h5/demo4.tsx | 42 +++++ .../datepickerview/demos/h5/demo5.tsx | 38 ++++ .../datepickerview/demos/h5/demo6.tsx | 73 ++++++++ .../datepickerview/demos/h5/demo7.tsx | 39 ++++ .../datepickerview/demos/h5/demo8.tsx | 75 ++++++++ .../datepickerview/demos/taro/demo1.tsx | 78 +++++++- src/packages/datepickerview/types.ts | 3 +- 14 files changed, 707 insertions(+), 78 deletions(-) create mode 100644 src/packages/datepickerview/demos/h5/demo2.tsx create mode 100644 src/packages/datepickerview/demos/h5/demo3.tsx create mode 100644 src/packages/datepickerview/demos/h5/demo4.tsx create mode 100644 src/packages/datepickerview/demos/h5/demo5.tsx create mode 100644 src/packages/datepickerview/demos/h5/demo6.tsx create mode 100644 src/packages/datepickerview/demos/h5/demo7.tsx create mode 100644 src/packages/datepickerview/demos/h5/demo8.tsx diff --git a/src/packages/datepickerview/datepickerview.scss b/src/packages/datepickerview/datepickerview.scss index 972cefa123..cc903b87ef 100644 --- a/src/packages/datepickerview/datepickerview.scss +++ b/src/packages/datepickerview/datepickerview.scss @@ -1,2 +1,4 @@ .nut-datepickerview { + display: flex; + width: 100%; } diff --git a/src/packages/datepickerview/datepickerview.taro.tsx b/src/packages/datepickerview/datepickerview.taro.tsx index d6f56c529c..047296305d 100644 --- a/src/packages/datepickerview/datepickerview.taro.tsx +++ b/src/packages/datepickerview/datepickerview.taro.tsx @@ -1,26 +1,184 @@ -import React, { FunctionComponent } from 'react' +import React, { useState, useEffect, FunctionComponent } from 'react' import classNames from 'classnames' import { View } from '@tarojs/components' +import isEqual from 'react-fast-compare' +import { padZero } from '@/utils/pad-zero' +import PickerView, { + PickerOptions, + PickerValue, +} from '@/packages/pickerview/index.taro' +import { useConfig } from '@/packages/configprovider' +import { usePropsValue } from '@/hooks/use-props-value' import { ComponentDefaults } from '@/utils/typings' +import { isDate } from '@/utils/is-date' +import { + formatValue, + generateDatePickerRanges, + generatePickerColumnWithCallback, + getDatePartValue, + handlePickerValueChange, +} from '@/packages/datepicker/utils' import { DatePickerViewProps } from './types' -// import { useConfig } from '@/packages/configprovider/configprovider.taro' -// import { useRtl } from '@/packages/configprovider/index.taro' +const currentYear = new Date().getFullYear() const defaultProps = { ...ComponentDefaults, + type: 'date', + showChinese: false, + threeDimensional: true, + minuteStep: 1, + startDate: new Date(currentYear - 10, 0, 1), + endDate: new Date(currentYear + 10, 11, 31), } as DatePickerViewProps + export const DatePickerView: FunctionComponent< Partial & React.HTMLAttributes > = (props) => { - // const { locale } = useConfig() - // const rtl = useRtl() - const { className, style } = { ...defaultProps, ...props } + const { + startDate, + endDate, + type, + showChinese, + minuteStep, + defaultValue, + formatter, + filter, + onChange, + threeDimensional, + className, + style, + } = { + ...defaultProps, + ...props, + } + const classPrefix = 'nut-datepickerview' const cls = classNames(classPrefix, className) + + const { locale } = useConfig() + const lang = locale.datepicker + + const zhCNType: { [key: string]: string } = { + day: lang.day, + year: lang.year, + month: lang.month, + hour: lang.hour, + minute: lang.min, + seconds: lang.seconds, + } + + const [pickerValue, setPickerValue] = useState([]) + const [pickerOptions, setPickerOptions] = useState([]) + + const [selectedDate, setSelectedDate] = usePropsValue({ + value: props.value && formatValue(props.value, startDate, endDate), + defaultValue: defaultValue && formatValue(defaultValue, startDate, endDate), + finalValue: 0, + }) + + const handleDateComparison = ( + newDate: Date | null, + selectedOptions: PickerOptions, + index: number + ) => { + const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() + if (newDate && isDate(newDate)) { + if (!isEqual) { + setSelectedDate(formatValue(newDate, startDate, endDate)) + onChange?.( + selectedOptions, + [ + String(newDate.getFullYear()), + padZero(newDate.getMonth() + 1), + padZero(newDate.getDate()), + ], + index + ) + } + } + } + + const handleChange = ( + selectedOptions: PickerOptions, + selectedValue: PickerValue[], + index: number + ) => { + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) + } + + const generatePickerColumns = (): PickerOptions[] => { + const dateRanges = generateDatePickerRanges( + type, + selectedDate, + startDate, + endDate + ) + + const columns = dateRanges.map((rangeConfig, columnIndex) => { + const { type: columnType, range } = rangeConfig + const selectedValue = getDatePartValue(columnType, selectedDate) + + const pickerColumn = generatePickerColumnWithCallback( + range[0], + range[1], + selectedValue, + columnType, + minuteStep, + (selectedIndex, options) => { + pickerValue[columnIndex] = options[selectedIndex]?.value + setPickerValue([...pickerValue]) + }, + showChinese, + zhCNType, + formatter + ) + + if (filter?.(columnType, pickerColumn)) { + return filter(columnType, pickerColumn) + } + + return pickerColumn + }) + + return columns || [] + } + + useEffect(() => { + console.log(new Date(selectedDate).toLocaleDateString()) + if ( + !isEqual( + new Date(selectedDate)?.getTime(), + new Date(selectedDate)?.getTime() + ) + ) { + setSelectedDate(selectedDate) + } + }, [selectedDate]) + + useEffect(() => { + setPickerOptions(generatePickerColumns()) + }, [selectedDate, startDate, endDate]) + return ( - DatePickerView + {pickerOptions.length && ( + { + handleChange(selectedOptions, value, index) + }} + threeDimensional={threeDimensional} + /> + )} ) } diff --git a/src/packages/datepickerview/datepickerview.tsx b/src/packages/datepickerview/datepickerview.tsx index 4a3be2b474..c36db43827 100644 --- a/src/packages/datepickerview/datepickerview.tsx +++ b/src/packages/datepickerview/datepickerview.tsx @@ -1,5 +1,7 @@ import React, { useState, useEffect, FunctionComponent } from 'react' import classNames from 'classnames' +import isEqual from 'react-fast-compare' +import { padZero } from '@/utils/pad-zero' import PickerView, { PickerOptions, PickerValue, @@ -70,32 +72,29 @@ export const DatePickerView: FunctionComponent< const [selectedDate, setSelectedDate] = usePropsValue({ value: props.value && formatValue(props.value, startDate, endDate), - defaultValue: - props.defaultValue && formatValue(props.defaultValue, startDate, endDate), + defaultValue: defaultValue && formatValue(defaultValue, startDate, endDate), finalValue: 0, }) - const [innerDate, setInnerDate] = useState(selectedDate) - const handleDateComparison = ( newDate: Date | null, selectedOptions: PickerOptions, index: number ) => { - const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() + const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() if (newDate && isDate(newDate)) { if (!isEqual) { - setInnerDate(formatValue(newDate, startDate, endDate)) + setSelectedDate(formatValue(newDate, startDate, endDate)) + onChange?.( + selectedOptions, + [ + String(newDate.getFullYear()), + padZero(newDate.getMonth() + 1), + padZero(newDate.getDate()), + ], + index + ) } - onChange?.( - selectedOptions, - [ - String(newDate.getFullYear()), - String(newDate.getMonth() + 1), - String(newDate.getDate()), - ], - index - ) } } @@ -117,14 +116,14 @@ export const DatePickerView: FunctionComponent< const generatePickerColumns = (): PickerOptions[] => { const dateRanges = generateDatePickerRanges( type, - innerDate, + selectedDate, startDate, endDate ) const columns = dateRanges.map((rangeConfig, columnIndex) => { const { type: columnType, range } = rangeConfig - const selectedValue = getDatePartValue(columnType, innerDate) + const selectedValue = getDatePartValue(columnType, selectedDate) const pickerColumn = generatePickerColumnWithCallback( range[0], @@ -151,30 +150,35 @@ export const DatePickerView: FunctionComponent< return columns || [] } - // useEffect(() => { - // setInnerDate(selectedDate) - // }, [selectedDate]) + useEffect(() => { + console.log(new Date(selectedDate).toLocaleDateString()) + if ( + !isEqual( + new Date(selectedDate)?.getTime(), + new Date(selectedDate)?.getTime() + ) + ) { + setSelectedDate(selectedDate) + } + }, [selectedDate]) useEffect(() => { - console.log('ssss', innerDate) setPickerOptions(generatePickerColumns()) - }, [innerDate, startDate, endDate]) - console.log('pickerValue', pickerValue, pickerOptions) + }, [selectedDate, startDate, endDate]) + return ( - <> -
- {pickerOptions.length && ( - { - handleChange(selectedOptions, value, index) - }} - threeDimensional={threeDimensional} - /> - )} -
- +
+ {pickerOptions.length && ( + { + handleChange(selectedOptions, value, index) + }} + threeDimensional={threeDimensional} + /> + )} +
) } diff --git a/src/packages/datepickerview/demo.tsx b/src/packages/datepickerview/demo.tsx index bde58f472d..6b02bca451 100644 --- a/src/packages/datepickerview/demo.tsx +++ b/src/packages/datepickerview/demo.tsx @@ -1,25 +1,69 @@ 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' +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 DatePickerViewDemo = () => { const [translated] = useTranslate({ 'zh-CN': { - title: '基础用法', - }, - 'en-US': { - title: 'Basic Usage', + basic: '选择日期', + mmdd: '选择月日', + showAll: '选择年月日时分', + time: '选择时分秒', + hourMinutes: '选择时分', + format: '格式化选项', + stepMins: '分钟数递增步长设置', + filter: '过滤选项', }, 'zh-TW': { - title: '基礎用法', + basic: '選擇日期', + mmdd: '選擇月日', + showAll: '選擇年月日時分', + time: '選擇時分秒', + hourMinutes: '選擇時分', + format: '格式化选项', + stepMins: '分鐘數遞增步長設置', + filter: '過濾選項', + }, + 'en-US': { + basic: 'Choose Date', + mmdd: 'Choose Month-Day', + showAll: 'Choose DateTime', + time: 'Choose Time', + hourMinutes: 'Selective time', + format: 'Option Formatter', + stepMins: 'Option Steps', + filter: 'Option Filter', }, }) + return ( -
-

{translated.title}

- -
+ <> +
+

{translated.basic}

+ +

{translated.mmdd}

+ +

{translated.showAll}

+ +

{translated.time}

+ +

{translated.hourMinutes}

+ +

{translated.format}

+ +

{translated.stepMins}

+ +

{translated.filter}

+ +
+ ) } - export default DatePickerViewDemo diff --git a/src/packages/datepickerview/demos/h5/demo1.tsx b/src/packages/datepickerview/demos/h5/demo1.tsx index 87e2a24cab..fe44912754 100644 --- a/src/packages/datepickerview/demos/h5/demo1.tsx +++ b/src/packages/datepickerview/demos/h5/demo1.tsx @@ -4,6 +4,7 @@ import { Cell, PickerValue, PickerOptions, + CellGroup, } from '@nutui/nutui-react' import isEqual from 'react-fast-compare' @@ -34,33 +35,43 @@ const Demo1 = () => { const handleChange = (setDesc: (desc: string) => void, setValue?: (value: string) => void) => - (options: PickerOptions, values: PickerValue[]) => { + (selectedOptions: PickerOptions, value: PickerValue[]) => { + console.log('demo-onChange-value', value) if (setValue) { - if (isEqual(values, ['2026', '02', '21'])) { + if (isEqual(value, ['2026', '02', '26'])) { setValue('2026/03/22') setDesc('2026年03月22日') } else { - setValue(values.join('/')) - setDesc(options.map((option) => option.label).join('')) + setValue(value.join('/')) + setDesc(selectedOptions.map((option) => option.label).join('')) } } else { - setDesc(options.map((option) => option.label).join('')) + setDesc(selectedOptions.map((option) => option.label).join('')) } } return ( <> - - - - - - - + + + + + + + + + + + + ) } diff --git a/src/packages/datepickerview/demos/h5/demo2.tsx b/src/packages/datepickerview/demos/h5/demo2.tsx new file mode 100644 index 0000000000..25cb712127 --- /dev/null +++ b/src/packages/datepickerview/demos/h5/demo2.tsx @@ -0,0 +1,34 @@ +import React, { useState } from 'react' +import { + DatePickerView, + Cell, + PickerOptions, + CellGroup, +} from '@nutui/nutui-react' + +const Demo2 = () => { + const defaultValue = new Date() + const [desc, setDesc] = useState( + `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` + ) + + const onChange = (options: PickerOptions) => { + setDesc(options.map((option) => option.label).join('-')) + } + return ( + + + + onChange(selectedOptions)} + /> + + + ) +} +export default Demo2 diff --git a/src/packages/datepickerview/demos/h5/demo3.tsx b/src/packages/datepickerview/demos/h5/demo3.tsx new file mode 100644 index 0000000000..bf0cce9a2e --- /dev/null +++ b/src/packages/datepickerview/demos/h5/demo3.tsx @@ -0,0 +1,44 @@ +import React, { useState } from 'react' +import { + DatePicker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react' + +const Demo3 = () => { + const defaultValue = new Date() + const defaultDescription = `${defaultValue.getFullYear()}-${ + defaultValue.getMonth() + 1 + }-${defaultValue.getDate()}` + + const startDate = new Date(2020, 0, 1) + const endDate = new Date(2025, 10, 1) + const [show, setShow] = useState(false) + const [desc, setDesc] = useState(`${defaultDescription} 11:08`) + const confirm = (values: PickerValue[], options: PickerOptions) => { + const date = values.slice(0, 3).join('-') + const time = values.slice(3).join(':') + setDesc(`${date} ${time}`) + } + return ( + <> + setShow(true)} + /> + setShow(false)} + onConfirm={(options, values) => confirm(values, options)} + /> + + ) +} +export default Demo3 diff --git a/src/packages/datepickerview/demos/h5/demo4.tsx b/src/packages/datepickerview/demos/h5/demo4.tsx new file mode 100644 index 0000000000..7e01fa6996 --- /dev/null +++ b/src/packages/datepickerview/demos/h5/demo4.tsx @@ -0,0 +1,42 @@ +import React, { useState } from 'react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react' + +const Demo4 = () => { + const defaultValue = new Date() + const defaultDescription = `${defaultValue.getFullYear()}-${ + defaultValue.getMonth() + 1 + }-${defaultValue.getDate()}` + const startDate = new Date(2020, 0, 1) + const endDate = new Date(2025, 10, 1) + const [show, setShow] = useState(false) + const [desc, setDesc] = useState('10:10:00') + const confirm = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join(':')) + } + + return ( + <> + setShow(true)} + /> + setShow(false)} + onConfirm={(options, values) => confirm(values, options)} + /> + + ) +} +export default Demo4 diff --git a/src/packages/datepickerview/demos/h5/demo5.tsx b/src/packages/datepickerview/demos/h5/demo5.tsx new file mode 100644 index 0000000000..e396a55a9f --- /dev/null +++ b/src/packages/datepickerview/demos/h5/demo5.tsx @@ -0,0 +1,38 @@ +import React, { useState } from 'react' +import { + DatePicker, + Cell, + PickerOptions, + PickerValue, +} from '@nutui/nutui-react' + +const Demo5 = () => { + const defaultValue = new Date() + const defaultDescription = `${defaultValue.getFullYear()}-${ + defaultValue.getMonth() + 1 + }-${defaultValue.getDate()}` + const startDate = new Date(2020, 0, 1) + const endDate = new Date(2025, 10, 1) + const [show, setShow] = useState(false) + const [desc, setDesc] = useState('10:10') + const confirm8 = (options: PickerOptions, values: PickerValue[]) => { + setDesc(options.map((option) => option.label).join(':')) + } + + return ( + <> + setShow(true)} /> + setShow(false)} + onConfirm={(options, values) => confirm8(options, values)} + /> + + ) +} +export default Demo5 diff --git a/src/packages/datepickerview/demos/h5/demo6.tsx b/src/packages/datepickerview/demos/h5/demo6.tsx new file mode 100644 index 0000000000..21350012de --- /dev/null +++ b/src/packages/datepickerview/demos/h5/demo6.tsx @@ -0,0 +1,73 @@ +import React, { useState } from 'react' +import { + DatePicker, + Cell, + PickerValue, + PickerOption, + PickerOptions, +} from '@nutui/nutui-react' + +const Demo6 = () => { + const defaultValue = new Date() + const defaultDescription = `${defaultValue.getFullYear()}-${ + defaultValue.getMonth() + 1 + }-${defaultValue.getDate()}` + const [show, setShow] = useState(false) + const [desc, setDesc] = useState(`${defaultDescription} 10:10`) + + const confirm = (values: PickerValue[], options: PickerOptions) => { + const date = options + .slice(1, 3) + .map((op) => op.label) + .join('') + const time = options + .slice(3) + .map((op) => op.value) + .join(':') + setDesc(`${options[0].label}年${date} ${time}`) + } + const formatter = (type: string, option: PickerOption) => { + switch (type) { + case 'year': + option.label += '' + break + case 'month': + option.label += '月' + break + case 'day': + option.label += '日' + break + case 'hour': + option.label += '时' + break + case 'minute': + option.label += '分' + break + default: + option.label += '' + } + return option + } + + return ( + <> + setShow(true)} + /> + setShow(false)} + onConfirm={(options, values) => confirm(values, options)} + /> + + ) +} +export default Demo6 diff --git a/src/packages/datepickerview/demos/h5/demo7.tsx b/src/packages/datepickerview/demos/h5/demo7.tsx new file mode 100644 index 0000000000..ba3b8f9366 --- /dev/null +++ b/src/packages/datepickerview/demos/h5/demo7.tsx @@ -0,0 +1,39 @@ +import React, { useState } from 'react' +import { + DatePicker, + Cell, + PickerValue, + PickerOptions, +} from '@nutui/nutui-react' + +const Demo7 = () => { + const defaultValue = new Date() + const defaultDescription = `${defaultValue.getFullYear()}-${ + defaultValue.getMonth() + 1 + }-${defaultValue.getDate()}` + const startDate = new Date(2020, 0, 1) + const endDate = new Date(2025, 10, 1) + const [show, setShow] = useState(false) + const [desc, setDesc] = useState('10:10:00') + + const confirm6 = (values: PickerValue[], options: PickerOptions) => { + setDesc(options.map((option) => option.label).join(':')) + } + return ( + <> + setShow(true)} /> + setShow(false)} + onConfirm={(options, values) => confirm6(values, options)} + /> + + ) +} +export default Demo7 diff --git a/src/packages/datepickerview/demos/h5/demo8.tsx b/src/packages/datepickerview/demos/h5/demo8.tsx new file mode 100644 index 0000000000..d4e8d8f9ab --- /dev/null +++ b/src/packages/datepickerview/demos/h5/demo8.tsx @@ -0,0 +1,75 @@ +import React, { useState } from 'react' +import { + DatePicker, + Cell, + PickerValue, + PickerOption, + PickerOptions, +} from '@nutui/nutui-react' + +const Demo8 = () => { + const startDate = new Date(2020, 0, 1) + const endDate = new Date(2025, 10, 1) + const defaultValue = new Date() + const defaultDescription = `${defaultValue.getFullYear()}-${ + defaultValue.getMonth() + 1 + }-${defaultValue.getDate()} 06:00` + const [show, setShow] = useState(false) + 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(' ')) + } + 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: PickerOption) => { + switch (type) { + case 'year': + option.label += `年` + break + case 'month': + option.label += `月` + break + case 'day': + option.label += `日` + break + case 'hour': + option.label += `时` + break + default: + option.label += '' + } + return option + } + return ( + <> + setShow(true)} + /> + setShow(false)} + onConfirm={(options, values) => confirm(values, options)} + /> + + ) +} +export default Demo8 diff --git a/src/packages/datepickerview/demos/taro/demo1.tsx b/src/packages/datepickerview/demos/taro/demo1.tsx index 208ba8615a..dc102cec87 100644 --- a/src/packages/datepickerview/demos/taro/demo1.tsx +++ b/src/packages/datepickerview/demos/taro/demo1.tsx @@ -1,12 +1,78 @@ -import React from 'react' -import { Cell, DatePickerView } from '@nutui/nutui-react-taro' -// import { Dongdong } from '@nutui/icons-react-taro' +import React, { useState } from 'react' +import { + DatePickerView, + Cell, + PickerValue, + PickerOptions, + CellGroup, +} from '@nutui/nutui-react-taro' +import isEqual from 'react-fast-compare' + +const useDatePicker = (initialDate: Date) => { + const defaultDateObj = { + year: initialDate.getFullYear(), + month: initialDate.getMonth() + 1, + day: initialDate.getDate(), + } + + const defaultDesc = `${defaultDateObj.year}年${defaultDateObj.month}月${defaultDateObj.day}日` + const defaultValue = Object.values(defaultDateObj).join('-') + + return { defaultDesc, defaultValue } +} const Demo1 = () => { + const defaultDate = new Date() + const { defaultDesc: defaultDesc1, defaultValue: defaultValue1 } = + useDatePicker(defaultDate) + const { defaultDesc: defaultDesc2, defaultValue: defaultValue2 } = + useDatePicker(defaultDate) + + const [desc1, setDesc1] = useState(defaultDesc1) + + const [value, setValue] = useState(defaultValue2) + const [desc2, setDesc2] = useState(defaultDesc2) + + const handleChange = + (setDesc: (desc: string) => void, setValue?: (value: string) => void) => + (selectedOptions: PickerOptions, value: PickerValue[]) => { + console.log('demo-onChange-value', value) + if (setValue) { + if (isEqual(value, ['2026', '02', '26'])) { + setValue('2026/03/22') + setDesc('2026年03月22日') + } else { + setValue(value.join('/')) + setDesc(selectedOptions.map((option) => option.label).join('')) + } + } else { + setDesc(selectedOptions.map((option) => option.label).join('')) + } + } + return ( - - - + <> + + + + + + + + + + + + + ) } diff --git a/src/packages/datepickerview/types.ts b/src/packages/datepickerview/types.ts index aa12f3da49..3f6a8414e0 100644 --- a/src/packages/datepickerview/types.ts +++ b/src/packages/datepickerview/types.ts @@ -23,8 +23,7 @@ export interface DatePickerViewProps extends BasicComponent { threeDimensional: boolean formatter: (type: string, option: PickerOption) => PickerOption filter: (type: string, options: PickerOptions) => PickerOptions - - onChange?: ( + onChange: ( selectedOptions: PickerOptions, selectedValue: PickerValue[], columnIndex: number 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 61/72] =?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 639214134b5e93f44839c93f6c4603c63e95a136 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 14:04:21 +0800 Subject: [PATCH 62/72] =?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 63/72] =?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 64/72] =?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 65/72] =?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 6befb47b8fd4e38dd538ddc1e9cf6b301b44e86e Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 15:34:44 +0800 Subject: [PATCH 66/72] =?UTF-8?q?fix:=20demo=E4=B8=B0=E5=AF=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepickerview/demo.taro.tsx | 19 +++-- src/packages/datepickerview/demo.tsx | 17 ----- .../datepickerview/demos/h5/demo3.tsx | 44 ----------- .../datepickerview/demos/h5/demo4.tsx | 42 ----------- .../datepickerview/demos/h5/demo5.tsx | 38 ---------- .../datepickerview/demos/h5/demo6.tsx | 73 ------------------ .../datepickerview/demos/h5/demo7.tsx | 39 ---------- .../datepickerview/demos/h5/demo8.tsx | 75 ------------------- .../datepickerview/demos/taro/demo2.tsx | 34 +++++++++ 9 files changed, 47 insertions(+), 334 deletions(-) delete mode 100644 src/packages/datepickerview/demos/h5/demo3.tsx delete mode 100644 src/packages/datepickerview/demos/h5/demo4.tsx delete mode 100644 src/packages/datepickerview/demos/h5/demo5.tsx delete mode 100644 src/packages/datepickerview/demos/h5/demo6.tsx delete mode 100644 src/packages/datepickerview/demos/h5/demo7.tsx delete mode 100644 src/packages/datepickerview/demos/h5/demo8.tsx create mode 100644 src/packages/datepickerview/demos/taro/demo2.tsx diff --git a/src/packages/datepickerview/demo.taro.tsx b/src/packages/datepickerview/demo.taro.tsx index e03d997bfb..3e4da160ee 100644 --- a/src/packages/datepickerview/demo.taro.tsx +++ b/src/packages/datepickerview/demo.taro.tsx @@ -4,25 +4,32 @@ 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' const DatePickerViewDemo = () => { const [translated] = useTranslate({ 'zh-CN': { - title: '基础用法', - }, - 'en-US': { - title: 'Basic Usage', + basic: '选择日期', + mmdd: '选择月日', }, + 'zh-TW': { - title: '基礎用法', + basic: '選擇日期', + mmdd: '選擇月日', + }, + 'en-US': { + basic: 'Choose Date', + mmdd: 'Choose Month-Day', }, }) return ( <>
- {translated.title} + {translated.basic} +

{translated.mmdd}

+
) diff --git a/src/packages/datepickerview/demo.tsx b/src/packages/datepickerview/demo.tsx index 6b02bca451..97f0699a2c 100644 --- a/src/packages/datepickerview/demo.tsx +++ b/src/packages/datepickerview/demo.tsx @@ -2,12 +2,6 @@ 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' -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 DatePickerViewDemo = () => { const [translated] = useTranslate({ @@ -51,17 +45,6 @@ const DatePickerViewDemo = () => {

{translated.mmdd}

{translated.showAll}

- -

{translated.time}

- -

{translated.hourMinutes}

- -

{translated.format}

- -

{translated.stepMins}

- -

{translated.filter}

-
) diff --git a/src/packages/datepickerview/demos/h5/demo3.tsx b/src/packages/datepickerview/demos/h5/demo3.tsx deleted file mode 100644 index bf0cce9a2e..0000000000 --- a/src/packages/datepickerview/demos/h5/demo3.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { useState } from 'react' -import { - DatePicker, - Cell, - PickerOptions, - PickerValue, -} from '@nutui/nutui-react' - -const Demo3 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` - - const startDate = new Date(2020, 0, 1) - const endDate = new Date(2025, 10, 1) - const [show, setShow] = useState(false) - const [desc, setDesc] = useState(`${defaultDescription} 11:08`) - const confirm = (values: PickerValue[], options: PickerOptions) => { - const date = values.slice(0, 3).join('-') - const time = values.slice(3).join(':') - setDesc(`${date} ${time}`) - } - return ( - <> - setShow(true)} - /> - setShow(false)} - onConfirm={(options, values) => confirm(values, options)} - /> - - ) -} -export default Demo3 diff --git a/src/packages/datepickerview/demos/h5/demo4.tsx b/src/packages/datepickerview/demos/h5/demo4.tsx deleted file mode 100644 index 7e01fa6996..0000000000 --- a/src/packages/datepickerview/demos/h5/demo4.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useState } from 'react' -import { - DatePicker, - Cell, - PickerValue, - PickerOptions, -} from '@nutui/nutui-react' - -const Demo4 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` - const startDate = new Date(2020, 0, 1) - const endDate = new Date(2025, 10, 1) - const [show, setShow] = useState(false) - const [desc, setDesc] = useState('10:10:00') - const confirm = (values: PickerValue[], options: PickerOptions) => { - setDesc(options.map((option) => option.label).join(':')) - } - - return ( - <> - setShow(true)} - /> - setShow(false)} - onConfirm={(options, values) => confirm(values, options)} - /> - - ) -} -export default Demo4 diff --git a/src/packages/datepickerview/demos/h5/demo5.tsx b/src/packages/datepickerview/demos/h5/demo5.tsx deleted file mode 100644 index e396a55a9f..0000000000 --- a/src/packages/datepickerview/demos/h5/demo5.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useState } from 'react' -import { - DatePicker, - Cell, - PickerOptions, - PickerValue, -} from '@nutui/nutui-react' - -const Demo5 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` - const startDate = new Date(2020, 0, 1) - const endDate = new Date(2025, 10, 1) - const [show, setShow] = useState(false) - const [desc, setDesc] = useState('10:10') - const confirm8 = (options: PickerOptions, values: PickerValue[]) => { - setDesc(options.map((option) => option.label).join(':')) - } - - return ( - <> - setShow(true)} /> - setShow(false)} - onConfirm={(options, values) => confirm8(options, values)} - /> - - ) -} -export default Demo5 diff --git a/src/packages/datepickerview/demos/h5/demo6.tsx b/src/packages/datepickerview/demos/h5/demo6.tsx deleted file mode 100644 index 21350012de..0000000000 --- a/src/packages/datepickerview/demos/h5/demo6.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useState } from 'react' -import { - DatePicker, - Cell, - PickerValue, - PickerOption, - PickerOptions, -} from '@nutui/nutui-react' - -const Demo6 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` - const [show, setShow] = useState(false) - const [desc, setDesc] = useState(`${defaultDescription} 10:10`) - - const confirm = (values: PickerValue[], options: PickerOptions) => { - const date = options - .slice(1, 3) - .map((op) => op.label) - .join('') - const time = options - .slice(3) - .map((op) => op.value) - .join(':') - setDesc(`${options[0].label}年${date} ${time}`) - } - const formatter = (type: string, option: PickerOption) => { - switch (type) { - case 'year': - option.label += '' - break - case 'month': - option.label += '月' - break - case 'day': - option.label += '日' - break - case 'hour': - option.label += '时' - break - case 'minute': - option.label += '分' - break - default: - option.label += '' - } - return option - } - - return ( - <> - setShow(true)} - /> - setShow(false)} - onConfirm={(options, values) => confirm(values, options)} - /> - - ) -} -export default Demo6 diff --git a/src/packages/datepickerview/demos/h5/demo7.tsx b/src/packages/datepickerview/demos/h5/demo7.tsx deleted file mode 100644 index ba3b8f9366..0000000000 --- a/src/packages/datepickerview/demos/h5/demo7.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useState } from 'react' -import { - DatePicker, - Cell, - PickerValue, - PickerOptions, -} from '@nutui/nutui-react' - -const Demo7 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` - const startDate = new Date(2020, 0, 1) - const endDate = new Date(2025, 10, 1) - const [show, setShow] = useState(false) - const [desc, setDesc] = useState('10:10:00') - - const confirm6 = (values: PickerValue[], options: PickerOptions) => { - setDesc(options.map((option) => option.label).join(':')) - } - return ( - <> - setShow(true)} /> - setShow(false)} - onConfirm={(options, values) => confirm6(values, options)} - /> - - ) -} -export default Demo7 diff --git a/src/packages/datepickerview/demos/h5/demo8.tsx b/src/packages/datepickerview/demos/h5/demo8.tsx deleted file mode 100644 index d4e8d8f9ab..0000000000 --- a/src/packages/datepickerview/demos/h5/demo8.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useState } from 'react' -import { - DatePicker, - Cell, - PickerValue, - PickerOption, - PickerOptions, -} from '@nutui/nutui-react' - -const Demo8 = () => { - const startDate = new Date(2020, 0, 1) - const endDate = new Date(2025, 10, 1) - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()} 06:00` - const [show, setShow] = useState(false) - 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(' ')) - } - 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: PickerOption) => { - switch (type) { - case 'year': - option.label += `年` - break - case 'month': - option.label += `月` - break - case 'day': - option.label += `日` - break - case 'hour': - option.label += `时` - break - default: - option.label += '' - } - return option - } - return ( - <> - setShow(true)} - /> - setShow(false)} - onConfirm={(options, values) => confirm(values, options)} - /> - - ) -} -export default Demo8 diff --git a/src/packages/datepickerview/demos/taro/demo2.tsx b/src/packages/datepickerview/demos/taro/demo2.tsx new file mode 100644 index 0000000000..3ff341c5de --- /dev/null +++ b/src/packages/datepickerview/demos/taro/demo2.tsx @@ -0,0 +1,34 @@ +import React, { useState } from 'react' +import { + DatePickerView, + Cell, + PickerOptions, + CellGroup, +} from '@nutui/nutui-react-taro' + +const Demo2 = () => { + const defaultValue = new Date() + const [desc, setDesc] = useState( + `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` + ) + + const onChange = (options: PickerOptions) => { + setDesc(options.map((option) => option.label).join('-')) + } + return ( + + + + onChange(selectedOptions)} + /> + + + ) +} +export default Demo2 From 3c3e8651249201542b34e5b45513e807e28c89ce Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 15:54:57 +0800 Subject: [PATCH 67/72] feat: test --- .../datepickerview.spec.tsx.snap | 431 ++++++++++++++++++ .../__test__/datepickerview.spec.tsx | 150 +++++- 2 files changed, 578 insertions(+), 3 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/datepickerview/__test__/datepickerview.spec.tsx b/src/packages/datepickerview/__test__/datepickerview.spec.tsx index cd30db2062..1900e67242 100644 --- a/src/packages/datepickerview/__test__/datepickerview.spec.tsx +++ b/src/packages/datepickerview/__test__/datepickerview.spec.tsx @@ -3,7 +3,151 @@ import { render } from '@testing-library/react' import '@testing-library/jest-dom' import { DatePickerView } from '../datepickerview' -test('should match snapshot', () => { - const { container } = render() - expect(container).toMatchSnapshot() +const currentYear = new Date().getFullYear() +test('Show Chinese', async () => { + const { container } = render( + + ) + + expect( + container.querySelectorAll('.nut-pickerview-roller-item')[0] + ).toHaveTextContent('年') +}) + +test('Min date & Max date', async () => { + const { container, rerender } = render( + + ) + + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[0].querySelectorAll('.nut-pickerview-roller-item-tiled') + expect(lists.length).toBe(3) + rerender( + + ) + rerender( + + ) + rerender( + + ) + rerender( + + ) + const formatter = (type: string, option: any) => { + switch (type) { + case 'year': + option.label += '' + break + case 'month': + option.label += 'M' + break + case 'day': + option.label += 'D' + break + case 'hour': + option.label += 'H' + break + case 'minute': + option.label += 'M' + break + default: + option.label += '' + } + return option + } + rerender( + + ) +}) + +test('Increment step setting', async () => { + const { container } = render( + + ) + + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[1].querySelectorAll('.nut-pickerview-roller-item') + expect(lists.length).toBe(12) +}) + +test('Filter Time', async () => { + const filter = vi.fn((type, options) => { + if (type === 'hour') { + return options.filter((option: any) => Number(option.value) % 6 === 0) + } + return options + }) + + const { container } = render( + + ) + + const columns = container.querySelectorAll('.nut-pickerview-list') + const lists = columns[3].querySelectorAll('.nut-pickerview-roller-item') + expect(lists.length).toBe(4) }) From 29a6b47c3bb32d44d3c6d4fd0097171ddbe34064 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 16:24:40 +0800 Subject: [PATCH 68/72] fix: update doc --- src/config.json | 2 +- src/packages/datepickerview/doc.en-US.md | 43 ++++++++++++++++++++++ src/packages/datepickerview/doc.md | 41 +++++++++++++-------- src/packages/datepickerview/doc.taro.md | 47 ++++++++++++++++++++++++ src/packages/datepickerview/doc.zh-TW.md | 47 ++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 src/packages/datepickerview/doc.en-US.md create mode 100644 src/packages/datepickerview/doc.taro.md create mode 100644 src/packages/datepickerview/doc.zh-TW.md diff --git a/src/config.json b/src/config.json index cad2ede234..e4841b664d 100644 --- a/src/config.json +++ b/src/config.json @@ -645,7 +645,7 @@ "version": "3.0.0", "name": "DatePickerView", "type": "component", - "cName": "选择器视图", + "cName": "日期选择器视图", "desc": "DatePickerView 是 DatePicker 的内容区域", "sort": 16, "show": true, diff --git a/src/packages/datepickerview/doc.en-US.md b/src/packages/datepickerview/doc.en-US.md new file mode 100644 index 0000000000..03ab86f457 --- /dev/null +++ b/src/packages/datepickerview/doc.en-US.md @@ -0,0 +1,43 @@ +# DatePicker + +Used to select time, support date and time dimensions. + +## Import + +```tsx +import { DatePickerView } from '@nutui/nutui' +``` + +### Choose Date + +:::demo + + + +::: + +### Choose Month-Day + +:::demo + + + +::: + +## DatePicker + +### Props + +| Property | Description | Type | Default | +| --- | --- | --- | --- | +| defaultValue | Default date | `Date` | `null` | +| value | controlled date | `Date` | `null` | +| type | Can be set to date time year-month month-day datehour hour-minutes | `string` | `date` | +| minuteStep | Option minute step | `number` | `1` | +| showChinese | Show Chinese | `boolean` | `false` | +| startDate | Start date | `Date` | `Ten years ago on January 1` | +| endDate | End date | `Date` | `Ten years later on December 31` | +| formatter | Option text formatter | `(type: string, option: PickerOption) => PickerOption` | `-` | +| filter | Option filter | `(type: string, options: PickerOptions) => PickerOptions` | `-` | +| threeDimensional | Turn on 3D effects | `boolean` | `true` | +| onChange | Emitted when current option changed. | `(options, value, index) => void` | `-` | diff --git a/src/packages/datepickerview/doc.md b/src/packages/datepickerview/doc.md index 3ed0771ca6..fa1e52b734 100644 --- a/src/packages/datepickerview/doc.md +++ b/src/packages/datepickerview/doc.md @@ -1,16 +1,16 @@ -# DatePickerView 选择器视图 +# DatePickerView 日期选择器视图 -DatePickerView 是 DatePicker 的内容区域 +时间选择器,支持日期、年月、时分等维度。 ## 引入 ```tsx -import { name } from '@nutui/nutui-react' +import { DatePickerView } from '@nutui/nutui' ``` ## 示例代码 -### 基础用法 +### 选择日期 :::demo @@ -18,19 +18,30 @@ import { name } from '@nutui/nutui-react' ::: -## DatePickerView +### 选择月日 -### Props +通过 type 属性来定义需要选择的时间类型。将 type 设置为 year-month 即可选择年份和月份,设置为 month-day 即可选择月份和日期。 -| 属性 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| name | 图标名 | String | - | +:::demo + + -## 主题定制 +::: + +## DatePicker -### 样式变量 +### Props -组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。 -| 名称 | 说明 | 默认值 | -| --- | --- | --- | -| --nutui-datepickerview-height | badge 的高度 | `14px` | +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| defaultValue | 初始值 | `Date` | `null` | +| value | 受控 | `Date` | `null` | +| type | 类时间类型,可选值 date time year-month month-day datehour datetime hour-minutes | `string` | `date` | +| minuteStep | 分钟步进值 | `number` | `1` | +| showChinese | 每列是否展示中文 | `boolean` | `false` | +| startDate | 开始日期 | `Date` | `十年前` | +| endDate | 结束日期 | `Date` | `十年后` | +| formatter | 选项格式化函数 | `(type: string, option: PickerOption) => PickerOption` | `-` | +| filter | 选项过滤函数 | `(type: string, options: PickerOptions) => PickerOptions` | `-` | +| threeDimensional | 是否开启3D效果 | `boolean` | `true` | +| onChange | 选项改变时触发 | `(options, value, index) => void` | `-` | diff --git a/src/packages/datepickerview/doc.taro.md b/src/packages/datepickerview/doc.taro.md new file mode 100644 index 0000000000..e68ed5b298 --- /dev/null +++ b/src/packages/datepickerview/doc.taro.md @@ -0,0 +1,47 @@ +# DatePickerView 日期选择器视图 + +时间选择器,支持日期、年月、时分等维度。 + +## 引入 + +```tsx +import { DatePickerView } from '@nutui/nutui-taro' +``` + +## 示例代码 + +### 选择日期 + +:::demo + + + +::: + +### 选择月日 + +通过 type 属性来定义需要选择的时间类型。将 type 设置为 year-month 即可选择年份和月份,设置为 month-day 即可选择月份和日期。 + +:::demo + + + +::: + +## DatePicker + +### Props + +| 属性 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| defaultValue | 初始值 | `Date` | `null` | +| value | 受控 | `Date` | `null` | +| type | 类时间类型,可选值 date time year-month month-day datehour datetime hour-minutes | `string` | `date` | +| minuteStep | 分钟步进值 | `number` | `1` | +| showChinese | 每列是否展示中文 | `boolean` | `false` | +| startDate | 开始日期 | `Date` | `十年前` | +| endDate | 结束日期 | `Date` | `十年后` | +| formatter | 选项格式化函数 | `(type: string, option: PickerOption) => PickerOption` | `-` | +| filter | 选项过滤函数 | `(type: string, options: PickerOptions) => PickerOptions` | `-` | +| threeDimensional | 是否开启3D效果 | `boolean` | `true` | +| onChange | 选项改变时触发 | `(options, value, index) => void` | `-` | diff --git a/src/packages/datepickerview/doc.zh-TW.md b/src/packages/datepickerview/doc.zh-TW.md new file mode 100644 index 0000000000..e24427b108 --- /dev/null +++ b/src/packages/datepickerview/doc.zh-TW.md @@ -0,0 +1,47 @@ +# DatePickerView 日期选择器視圖 + +時間選擇器,支持日期、年月、時分等維度。 + +## 引入 + +```tsx +import { DatePickerView } from '@nutui/nutui' +``` + +## 示例代碼 + +### 選擇日期 + +:::demo + + + +::: + +### 選擇月日 + +通過 type 屬性來定義需要選擇的時間類型。將 type 設置為 year-month 即可選擇年份和月份,設置為 month-day 即可選擇月份和日期。 + +:::demo + + + +::: + +## DatePicker + +### Props + +| 屬性 | 說明 | 類型 | 默認值 | +| --- | --- | --- | --- | +| defaultValue | 初始值 | `Date` | `null` | +| value | 受控 | `Date` | `null` | +| type | 類時間類型,可選值 date time year-month month-day datehour datetime hour-minutes | `string` | `date` | +| minuteStep | 分鐘步進值 | `number` | `1` | +| showChinese | 每列是否展示中文 | `boolean` | `false` | +| startDate | 開始日期 | `Date` | `十年前` | +| endDate | 結束日期 | `Date` | `十年後` | +| formatter | 選項格式化函數 | `(type: string, option: PickerOption) => PickerOption` | `-` | +| filter | 選項過濾函數 | `(type: string, options: PickerOptions) => PickerOptions` | `-` | +| threeDimensional | 是否開啟3D效果 | `boolean` | `true` | +| onChange | 選項改變時觸發 | `(options, value, index) => void` | `-` | From 381f1134217168c707aa61b547dade0472a19faa Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 27 Feb 2025 16:36:02 +0800 Subject: [PATCH 69/72] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=AF=94?= =?UTF-8?q?=E8=BE=83=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.taro.tsx | 11 +++++++---- src/packages/datepicker/datepicker.tsx | 13 ++++++++----- src/packages/datepickerview/datepickerview.taro.tsx | 4 +--- src/packages/datepickerview/datepickerview.tsx | 4 +--- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 4cf3c985eb..b2930962b0 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -4,6 +4,8 @@ import React, { ForwardRefRenderFunction, useImperativeHandle, } from 'react' +import isEqual from 'react-fast-compare' +import classNames from 'classnames' import { View } from '@tarojs/components' import Picker from '@/packages/picker/index.taro' import { useConfig } from '@/packages/configprovider/index.taro' @@ -77,6 +79,9 @@ const InternalPicker: ForwardRefRenderFunction< seconds: lang.seconds, } + const classPrefix = 'nut-datepicker' + const cls = classNames(classPrefix, className) + const [pickerValue, setPickerValue] = useState([]) const [pickerOptions, setPickerOptions] = useState([]) @@ -111,9 +116,8 @@ const InternalPicker: ForwardRefRenderFunction< selectedOptions: PickerOptions, index: number ) => { - const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() if (newDate && isDate(newDate)) { - if (!isEqual) { + if (!isEqual(new Date(selectedDate)?.getTime(), newDate?.getTime())) { setInnerDate(formatValue(newDate, startDate, endDate)) } onChange?.( @@ -130,8 +134,7 @@ const InternalPicker: ForwardRefRenderFunction< const handleConfirmDateComparison = (newDate: Date | null) => { if (newDate && isDate(newDate)) { - const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() - if (!isEqual) { + if (!isEqual(new Date(selectedDate)?.getTime(), newDate?.getTime())) { setSelectedDate(formatValue(newDate, startDate, endDate)) } } diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 3881dc8122..5aaf58fcee 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -4,6 +4,8 @@ import React, { ForwardRefRenderFunction, useImperativeHandle, } from 'react' +import isEqual from 'react-fast-compare' +import classNames from 'classnames' import Picker from '@/packages/picker' import { useConfig } from '@/packages/configprovider' import { usePropsValue } from '@/hooks/use-props-value' @@ -75,6 +77,9 @@ const InternalPicker: ForwardRefRenderFunction< seconds: lang.seconds, } + const classPrefix = 'nut-datepicker' + const cls = classNames(classPrefix, className) + const [pickerValue, setPickerValue] = useState([]) const [pickerOptions, setPickerOptions] = useState([]) @@ -109,9 +114,8 @@ const InternalPicker: ForwardRefRenderFunction< selectedOptions: PickerOptions, index: number ) => { - const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() if (newDate && isDate(newDate)) { - if (!isEqual) { + if (!isEqual(new Date(selectedDate)?.getTime(), newDate?.getTime())) { setInnerDate(formatValue(newDate, startDate, endDate)) } onChange?.( @@ -128,8 +132,7 @@ const InternalPicker: ForwardRefRenderFunction< const handleConfirmDateComparison = (newDate: Date | null) => { if (newDate && isDate(newDate)) { - const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() - if (!isEqual) { + if (!isEqual(new Date(selectedDate)?.getTime(), newDate?.getTime())) { setSelectedDate(formatValue(newDate, startDate, endDate)) } } @@ -221,7 +224,7 @@ const InternalPicker: ForwardRefRenderFunction< return ( <> {typeof children === 'function' && children(selectedDate)} -
+
{pickerOptions.length && ( { - const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() if (newDate && isDate(newDate)) { - if (!isEqual) { + if (!isEqual(new Date(selectedDate)?.getTime(), newDate?.getTime())) { setSelectedDate(formatValue(newDate, startDate, endDate)) onChange?.( selectedOptions, @@ -152,7 +151,6 @@ export const DatePickerView: FunctionComponent< } useEffect(() => { - console.log(new Date(selectedDate).toLocaleDateString()) if ( !isEqual( new Date(selectedDate)?.getTime(), diff --git a/src/packages/datepickerview/datepickerview.tsx b/src/packages/datepickerview/datepickerview.tsx index c36db43827..1f7ce7960d 100644 --- a/src/packages/datepickerview/datepickerview.tsx +++ b/src/packages/datepickerview/datepickerview.tsx @@ -81,9 +81,8 @@ export const DatePickerView: FunctionComponent< selectedOptions: PickerOptions, index: number ) => { - const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() if (newDate && isDate(newDate)) { - if (!isEqual) { + if (!isEqual(new Date(selectedDate)?.getTime(), newDate?.getTime())) { setSelectedDate(formatValue(newDate, startDate, endDate)) onChange?.( selectedOptions, @@ -151,7 +150,6 @@ export const DatePickerView: FunctionComponent< } useEffect(() => { - console.log(new Date(selectedDate).toLocaleDateString()) if ( !isEqual( new Date(selectedDate)?.getTime(), 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 70/72] =?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 From 12da7982cd5f7fa4e9608db784678cfec45c300c Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 4 Mar 2025 20:10:51 +0800 Subject: [PATCH 71/72] =?UTF-8?q?fix:=20pr=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.json | 26 ++++++++++----------- src/packages/datepicker/datepicker.taro.tsx | 2 +- src/packages/datepickerview/demo.tsx | 19 --------------- 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/src/config.json b/src/config.json index e4841b664d..f03f604839 100644 --- a/src/config.json +++ b/src/config.json @@ -584,6 +584,19 @@ "author": "dsj", "dd": false }, + { + "version": "3.0.0", + "name": "DatePickerView", + "type": "component", + "cName": "日期选择器视图", + "desc": "DatePickerView 是 DatePicker 的内容区域", + "sort": 16, + "show": true, + "taro": true, + "v15": false, + "dd": false, + "author": "songsong" + }, { "version": "2.0.0", "name": "Form", @@ -641,19 +654,6 @@ "name": "数据录入", "enName": "dataentry", "packages": [ - { - "version": "3.0.0", - "name": "DatePickerView", - "type": "component", - "cName": "日期选择器视图", - "desc": "DatePickerView 是 DatePicker 的内容区域", - "sort": 16, - "show": true, - "taro": true, - "v15": false, - "dd": false, - "author": "songsong" - }, { "version": "2.0.0", "name": "Menu", diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index b2930962b0..0824dd06ee 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -226,7 +226,7 @@ const InternalPicker: ForwardRefRenderFunction< return ( <> {typeof children === 'function' && children(selectedDate)} - + {pickerOptions.length && ( { 'zh-CN': { basic: '选择日期', mmdd: '选择月日', - showAll: '选择年月日时分', - time: '选择时分秒', - hourMinutes: '选择时分', - format: '格式化选项', - stepMins: '分钟数递增步长设置', - filter: '过滤选项', }, 'zh-TW': { basic: '選擇日期', mmdd: '選擇月日', - showAll: '選擇年月日時分', - time: '選擇時分秒', - hourMinutes: '選擇時分', - format: '格式化选项', - stepMins: '分鐘數遞增步長設置', - filter: '過濾選項', }, 'en-US': { basic: 'Choose Date', mmdd: 'Choose Month-Day', - showAll: 'Choose DateTime', - time: 'Choose Time', - hourMinutes: 'Selective time', - format: 'Option Formatter', - stepMins: 'Option Steps', - filter: 'Option Filter', }, }) @@ -44,7 +26,6 @@ const DatePickerViewDemo = () => {

{translated.mmdd}

-

{translated.showAll}

) From e14d2238683ce1c79c4d4616bbc07a403a0f90bc Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Wed, 5 Mar 2025 10:23:35 +0800 Subject: [PATCH 72/72] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9ts=E6=8A=A5?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepickerview/datepickerview.taro.tsx | 6 +++--- src/packages/datepickerview/datepickerview.tsx | 6 +++--- src/packages/datepickerview/demos/h5/demo1.tsx | 1 - src/packages/datepickerview/demos/h5/demo2.tsx | 1 - src/packages/datepickerview/demos/taro/demo1.tsx | 1 - src/packages/datepickerview/demos/taro/demo2.tsx | 1 - 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/packages/datepickerview/datepickerview.taro.tsx b/src/packages/datepickerview/datepickerview.taro.tsx index cac26fd384..5fd29ae0c9 100644 --- a/src/packages/datepickerview/datepickerview.taro.tsx +++ b/src/packages/datepickerview/datepickerview.taro.tsx @@ -32,9 +32,9 @@ const defaultProps = { endDate: new Date(currentYear + 10, 11, 31), } as DatePickerViewProps -export const DatePickerView: FunctionComponent< - Partial & React.HTMLAttributes -> = (props) => { +export const DatePickerView: FunctionComponent> = ( + props +) => { const { startDate, endDate, diff --git a/src/packages/datepickerview/datepickerview.tsx b/src/packages/datepickerview/datepickerview.tsx index 1f7ce7960d..fb3742820e 100644 --- a/src/packages/datepickerview/datepickerview.tsx +++ b/src/packages/datepickerview/datepickerview.tsx @@ -31,9 +31,9 @@ const defaultProps = { endDate: new Date(currentYear + 10, 11, 31), } as DatePickerViewProps -export const DatePickerView: FunctionComponent< - Partial & React.HTMLAttributes -> = (props) => { +export const DatePickerView: FunctionComponent> = ( + props +) => { const { startDate, endDate, diff --git a/src/packages/datepickerview/demos/h5/demo1.tsx b/src/packages/datepickerview/demos/h5/demo1.tsx index fe44912754..0d023d57b5 100644 --- a/src/packages/datepickerview/demos/h5/demo1.tsx +++ b/src/packages/datepickerview/demos/h5/demo1.tsx @@ -36,7 +36,6 @@ const Demo1 = () => { const handleChange = (setDesc: (desc: string) => void, setValue?: (value: string) => void) => (selectedOptions: PickerOptions, value: PickerValue[]) => { - console.log('demo-onChange-value', value) if (setValue) { if (isEqual(value, ['2026', '02', '26'])) { setValue('2026/03/22') diff --git a/src/packages/datepickerview/demos/h5/demo2.tsx b/src/packages/datepickerview/demos/h5/demo2.tsx index 25cb712127..2d627dc6b4 100644 --- a/src/packages/datepickerview/demos/h5/demo2.tsx +++ b/src/packages/datepickerview/demos/h5/demo2.tsx @@ -20,7 +20,6 @@ const Demo2 = () => { { const handleChange = (setDesc: (desc: string) => void, setValue?: (value: string) => void) => (selectedOptions: PickerOptions, value: PickerValue[]) => { - console.log('demo-onChange-value', value) if (setValue) { if (isEqual(value, ['2026', '02', '26'])) { setValue('2026/03/22') diff --git a/src/packages/datepickerview/demos/taro/demo2.tsx b/src/packages/datepickerview/demos/taro/demo2.tsx index 3ff341c5de..68c59725a0 100644 --- a/src/packages/datepickerview/demos/taro/demo2.tsx +++ b/src/packages/datepickerview/demos/taro/demo2.tsx @@ -20,7 +20,6 @@ const Demo2 = () => {