From f17ab7bfcb796302eb9640412d5d53f97a2bda4c Mon Sep 17 00:00:00 2001 From: epoll-j Date: Fri, 13 Sep 2024 16:32:24 +0800 Subject: [PATCH 1/6] feat(tree-select): add tree-select component feat #395 --- src/tree-select/defaultProps.ts | 12 ++ src/tree-select/style/css.js | 1 + src/tree-select/style/index.js | 1 + src/tree-select/tree-select.en-US.md | 19 +++ src/tree-select/tree-select.md | 19 +++ src/tree-select/tree-select.tsx | 192 +++++++++++++++++++++++++++ src/tree-select/type.ts | 50 +++++++ 7 files changed, 294 insertions(+) create mode 100644 src/tree-select/defaultProps.ts create mode 100644 src/tree-select/style/css.js create mode 100644 src/tree-select/style/index.js create mode 100644 src/tree-select/tree-select.en-US.md create mode 100644 src/tree-select/tree-select.md create mode 100644 src/tree-select/tree-select.tsx create mode 100644 src/tree-select/type.ts diff --git a/src/tree-select/defaultProps.ts b/src/tree-select/defaultProps.ts new file mode 100644 index 00000000..7900daa0 --- /dev/null +++ b/src/tree-select/defaultProps.ts @@ -0,0 +1,12 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdTreeSelectProps } from './type'; + +export const treeSelectDefaultProps: TdTreeSelectProps = { + filterable: false, + height: 336, + multiple: false, + options: [], +}; diff --git a/src/tree-select/style/css.js b/src/tree-select/style/css.js new file mode 100644 index 00000000..6a9a4b13 --- /dev/null +++ b/src/tree-select/style/css.js @@ -0,0 +1 @@ +import './index.css'; diff --git a/src/tree-select/style/index.js b/src/tree-select/style/index.js new file mode 100644 index 00000000..d6cbf010 --- /dev/null +++ b/src/tree-select/style/index.js @@ -0,0 +1 @@ +import '../../_common/style/mobile/components/tree-select/_index.less'; diff --git a/src/tree-select/tree-select.en-US.md b/src/tree-select/tree-select.en-US.md new file mode 100644 index 00000000..07b699f8 --- /dev/null +++ b/src/tree-select/tree-select.en-US.md @@ -0,0 +1,19 @@ +:: BASE_DOC :: + +## API + + +### TreeSelect Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +filterable | Boolean | false | \- | N +height | String / Number | 336 | \- | N +keys | Object | - | alias filed name in data。Typescript:`TreeKeysType`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +multiple | Boolean | false | \- | N +options | Array | [] | Typescript:`Array` | N +value | String / Number / Array | - | Typescript:`TreeSelectValue` `type TreeSelectValue = string \| number \| Array`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tree-select/type.ts) | N +defaultValue | String / Number / Array | - | uncontrolled property。Typescript:`TreeSelectValue` `type TreeSelectValue = string \| number \| Array`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tree-select/type.ts) | N +onChange | Function | | Typescript:`(value: TreeSelectValue, level: TreeLevel) => void`
[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tree-select/type.ts)。
`type TreeLevel = 0 \| 1 \| 2`
| N diff --git a/src/tree-select/tree-select.md b/src/tree-select/tree-select.md new file mode 100644 index 00000000..7b05d151 --- /dev/null +++ b/src/tree-select/tree-select.md @@ -0,0 +1,19 @@ +:: BASE_DOC :: + +## API + + +### TreeSelect Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +filterable | Boolean | false | 是否可搜索 | N +height | String / Number | 336 | 高度,默认单位为 px | N +keys | Object | - | 用来定义 `value / label / disabled / children` 在 `data` 数据中对应的字段别名,示例:`{ value: 'key', label: 'name', children: 'list' }`。TS 类型:`TreeKeysType`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +multiple | Boolean | false | 是否允许多选 | N +options | Array | [] | 选项。TS 类型:`Array` | N +value | String / Number / Array | - | 选中值。TS 类型:`TreeSelectValue` `type TreeSelectValue = string \| number \| Array`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tree-select/type.ts) | N +defaultValue | String / Number / Array | - | 选中值。非受控属性。TS 类型:`TreeSelectValue` `type TreeSelectValue = string \| number \| Array`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tree-select/type.ts) | N +onChange | Function | | TS 类型:`(value: TreeSelectValue, level: TreeLevel) => void`
点击任何节点均会触发;level 代表当前点击的层级,0 代表最左侧,依次递进。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/tree-select/type.ts)。
`type TreeLevel = 0 \| 1 \| 2`
| N diff --git a/src/tree-select/tree-select.tsx b/src/tree-select/tree-select.tsx new file mode 100644 index 00000000..8f07b1e3 --- /dev/null +++ b/src/tree-select/tree-select.tsx @@ -0,0 +1,192 @@ +import React, { FC, useEffect, useState } from 'react'; +import classNames from 'classnames'; +import useDefault from '../_util/useDefault'; +import useDefaultProps from '../hooks/useDefaultProps'; +import { TdTreeSelectProps, TreeSelectValue } from './type'; +import { treeSelectDefaultProps } from './defaultProps'; +import { usePrefixClass } from '../hooks/useClass'; +import { convertUnit } from '../_util/convertUnit'; +import { TreeOptionData } from '../common'; +import { SideBar, SideBarItem } from '../side-bar'; +import parseTNode from '../_util/parseTNode'; +import { Checkbox } from '../checkbox'; +import { Radio, RadioGroup } from '../radio'; +import { NativeProps } from '../_util/withNativeProps'; + +export interface TreeSelectProps extends TdTreeSelectProps, NativeProps {} + +const TreeSelect: FC = (props) => { + const treeSelectClass = usePrefixClass('tree-select'); + const { value, onChange, height, options, keys, multiple } = useDefaultProps(props, treeSelectDefaultProps); + const [innerValue, setInnerValue] = useDefault(value, null, onChange); + const [leafLevel, setLeafLevel] = useState(0); + const [treeOptions, setTreeOptions] = useState([]); + // const rootStyle = useMemo(() => [`height: ${convertUnit(height)}`, style].join(';'), [height, style]); + + const buildTreeOptions = () => { + const { options, multiple, keys } = props; + + let level = -1; + const tmpTreeOptions: TreeOptionData[][] = []; + let node: TreeOptionData = { children: options }; + if (options.length === 0 || (Array.isArray(value) && value.length === 0)) return; + while (node && node.children) { + level += 1; + const list = (node.children as TreeOptionData[]).map((item: TreeOptionData) => ({ + label: item[keys?.label || 'label'], + value: item[keys?.value || 'value'], + disabled: item[keys?.disabled || 'disabled'], + children: item.children, + })); + const thisValue = innerValue?.[level]; + + tmpTreeOptions.push([...list]); + if (thisValue == null) { + const [firstChild] = list; + node = firstChild; + } else { + const child = list.find((child) => child.value === thisValue); + node = child ?? list[0]; + } + } + setLeafLevel(Math.max(0, level)); + setTreeOptions(tmpTreeOptions); + + if (multiple) { + const finalValue = innerValue; + if (finalValue[leafLevel] != null && !Array.isArray(finalValue[leafLevel])) { + throw TypeError('应传入数组类型的 value'); + } + } + }; + + const getTreeClass = (level: number, total: number) => { + if (level === 0) return 'right'; + if (level === 1 && level !== total - 1) return 'middle'; + return 'left'; + }; + + const onRootChange = (level: number) => { + setInnerValue(innerValue, level); + }; + + const handleTreeClick = (itemValue: TreeSelectValue, level: number, isDisabled: boolean) => { + if (isDisabled) return; + // innerValue.value[level] = itemValue; + setInnerValue(innerValue, level); + }; + + useEffect(() => { + buildTreeOptions(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [innerValue, options, keys, multiple]); + + const renderSideBar = (treeOption: TreeOptionData[]) => ( + onRootChange(0)}> + {treeOption.map((item, index) => ( + + ))} + + ); + + const renderMiddleLevel = (treeOption: TreeOptionData[], level: number) => + treeOption.map((item) => ( +
handleTreeClick(item.value, level, item.disabled)} + > + {parseTNode(item.label)} +
+ )); + + const renderLeafLevel = (treeOption: TreeOptionData[], level: number) => { + if (multiple) { + return ( + onRootChange(level)} + > + {treeOption.map((item) => ( + + {item.label} + + ))} + + ); + } + return ( + onRootChange(level)}> + {treeOption.map((item) => ( + + {item.label} + + ))} + + ); + }; + + return ( +
+ {treeOptions.map((item, level) => { + let levelContent; + if (level === 0) { + levelContent = renderSideBar(item); + } else if (level === leafLevel) { + levelContent = renderLeafLevel(item, level); + } else { + levelContent = renderMiddleLevel(item, level); + } + + return ( +
+ {levelContent} +
+ ); + })} +
+ ); +}; + +TreeSelect.displayName = 'TreeSelect'; + +export default TreeSelect; diff --git a/src/tree-select/type.ts b/src/tree-select/type.ts new file mode 100644 index 00000000..ae085bff --- /dev/null +++ b/src/tree-select/type.ts @@ -0,0 +1,50 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TreeOptionData, TreeKeysType } from '../common'; + +export interface TdTreeSelectProps { + /** + * 是否可搜索 + * @default false + */ + filterable?: boolean; + /** + * 高度,默认单位为 px + * @default 336 + */ + height?: string | number; + /** + * 用来定义 `value / label / disabled / children` 在 `data` 数据中对应的字段别名,示例:`{ value: 'key', label: 'name', children: 'list' }` + */ + keys?: TreeKeysType; + /** + * 是否允许多选 + * @default false + */ + multiple?: boolean; + /** + * 选项 + * @default [] + */ + options?: Array; + /** + * 选中值 + */ + value?: TreeSelectValue; + /** + * 选中值,非受控属性 + */ + defaultValue?: TreeSelectValue; + /** + * 点击任何节点均会触发;level 代表当前点击的层级,0 代表最左侧,依次递进 + */ + onChange?: (value: TreeSelectValue, level: TreeLevel) => void; +} + +export type TreeSelectValue = string | number | Array; + +export type TreeLevel = 0 | 1 | 2; From 4c9d3be77ea7e3198f169fbc51f1ee1f8c1097d2 Mon Sep 17 00:00:00 2001 From: epoll-j Date: Sat, 14 Sep 2024 14:17:15 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(tree-select):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/mobile/mobile.config.js | 5 ++ site/web/site.config.js | 6 ++ src/index.ts | 2 +- src/tree-select/index.ts | 10 ++++ src/tree-select/tree-select.en-US.md | 1 - src/tree-select/tree-select.md | 1 - src/tree-select/tree-select.tsx | 82 ++++++++++++++++------------ 7 files changed, 70 insertions(+), 37 deletions(-) create mode 100644 src/tree-select/index.ts diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index 167d0fb3..bd485409 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -262,5 +262,10 @@ export default { name: 'table', component: () => import('tdesign-mobile-react/table/_example/index.tsx'), }, + { + title: 'TreeSelect 树形选择', + name: 'tree-select', + component: () => import('tdesign-mobile-react/tree-select/_example/index.tsx'), + }, ], }; diff --git a/site/web/site.config.js b/site/web/site.config.js index 6138e9cc..c422fd3c 100644 --- a/site/web/site.config.js +++ b/site/web/site.config.js @@ -220,6 +220,12 @@ export default { path: '/mobile-react/components/upload', component: () => import('tdesign-mobile-react/upload/upload.md'), }, + { + title: 'TreeSelect 树形选择', + name: 'tree-select', + path: '/mobile-react/components/tree-select', + component: () => import('tdesign-mobile-react/tree-select/tree-select.md'), + }, ], }, { diff --git a/src/index.ts b/src/index.ts index 91b4e09e..3411eb95 100644 --- a/src/index.ts +++ b/src/index.ts @@ -65,7 +65,7 @@ export * from './drawer'; * 二期组件 */ export * from './collapse'; - +export * from './tree-select'; export * from './notice-bar'; /** diff --git a/src/tree-select/index.ts b/src/tree-select/index.ts new file mode 100644 index 00000000..b355da1c --- /dev/null +++ b/src/tree-select/index.ts @@ -0,0 +1,10 @@ +import _TreeSelect from './tree-select'; +import { TdTreeSelectProps } from './type'; + +import './style'; + +export * from './type'; +export type TreeSelectProps = TdTreeSelectProps; +const TreeSelect = _TreeSelect; +// export const TreeSelect: WithInstallType = withInstall(_TreeSelect); +export default TreeSelect; diff --git a/src/tree-select/tree-select.en-US.md b/src/tree-select/tree-select.en-US.md index 07b699f8..7340ddee 100644 --- a/src/tree-select/tree-select.en-US.md +++ b/src/tree-select/tree-select.en-US.md @@ -2,7 +2,6 @@ ## API - ### TreeSelect Props name | type | default | description | required diff --git a/src/tree-select/tree-select.md b/src/tree-select/tree-select.md index 7b05d151..c57b16e9 100644 --- a/src/tree-select/tree-select.md +++ b/src/tree-select/tree-select.md @@ -2,7 +2,6 @@ ## API - ### TreeSelect Props 名称 | 类型 | 默认值 | 描述 | 必传 diff --git a/src/tree-select/tree-select.tsx b/src/tree-select/tree-select.tsx index 8f07b1e3..ccbe74be 100644 --- a/src/tree-select/tree-select.tsx +++ b/src/tree-select/tree-select.tsx @@ -2,7 +2,7 @@ import React, { FC, useEffect, useState } from 'react'; import classNames from 'classnames'; import useDefault from '../_util/useDefault'; import useDefaultProps from '../hooks/useDefaultProps'; -import { TdTreeSelectProps, TreeSelectValue } from './type'; +import { TdTreeSelectProps, TreeLevel, TreeSelectValue } from './type'; import { treeSelectDefaultProps } from './defaultProps'; import { usePrefixClass } from '../hooks/useClass'; import { convertUnit } from '../_util/convertUnit'; @@ -17,15 +17,15 @@ export interface TreeSelectProps extends TdTreeSelectProps, NativeProps {} const TreeSelect: FC = (props) => { const treeSelectClass = usePrefixClass('tree-select'); - const { value, onChange, height, options, keys, multiple } = useDefaultProps(props, treeSelectDefaultProps); - const [innerValue, setInnerValue] = useDefault(value, null, onChange); + const { value, onChange, height, options, keys, multiple, defaultValue } = useDefaultProps( + props, + treeSelectDefaultProps, + ); + const [innerValue, setInnerValue] = useDefault(value, defaultValue, onChange); const [leafLevel, setLeafLevel] = useState(0); const [treeOptions, setTreeOptions] = useState([]); - // const rootStyle = useMemo(() => [`height: ${convertUnit(height)}`, style].join(';'), [height, style]); const buildTreeOptions = () => { - const { options, multiple, keys } = props; - let level = -1; const tmpTreeOptions: TreeOptionData[][] = []; let node: TreeOptionData = { children: options }; @@ -39,7 +39,6 @@ const TreeSelect: FC = (props) => { children: item.children, })); const thisValue = innerValue?.[level]; - tmpTreeOptions.push([...list]); if (thisValue == null) { const [firstChild] = list; @@ -51,7 +50,6 @@ const TreeSelect: FC = (props) => { } setLeafLevel(Math.max(0, level)); setTreeOptions(tmpTreeOptions); - if (multiple) { const finalValue = innerValue; if (finalValue[leafLevel] != null && !Array.isArray(finalValue[leafLevel])) { @@ -66,23 +64,33 @@ const TreeSelect: FC = (props) => { return 'left'; }; - const onRootChange = (level: number) => { - setInnerValue(innerValue, level); + const onRootChange = (level: TreeLevel, itemValue: any) => { + const newVal = Array.isArray(innerValue) ? [...innerValue] : innerValue; + newVal[level] = itemValue; + setInnerValue(newVal, level); }; - const handleTreeClick = (itemValue: TreeSelectValue, level: number, isDisabled: boolean) => { + const handleTreeClick = (itemValue: TreeSelectValue, level: TreeLevel, isDisabled: boolean) => { if (isDisabled) return; - // innerValue.value[level] = itemValue; - setInnerValue(innerValue, level); + + const newVal = Array.isArray(innerValue) ? [...innerValue] : innerValue; + newVal[level] = itemValue; + setInnerValue(newVal, level); }; useEffect(() => { buildTreeOptions(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [innerValue, options, keys, multiple]); + }, [innerValue, options, keys, multiple, value]); const renderSideBar = (treeOption: TreeOptionData[]) => ( - onRootChange(0)}> + { + onRootChange(0, val); + }} + > {treeOption.map((item, index) => ( = (props) => { [`${treeSelectClass}__item--active`]: item.value === innerValue[level], [`${treeSelectClass}__item--disabled`]: item.disabled, })} - onClick={() => handleTreeClick(item.value, level, item.disabled)} + onClick={() => handleTreeClick(item.value, level as TreeLevel, item.disabled)} > {parseTNode(item.label)} @@ -113,9 +121,9 @@ const TreeSelect: FC = (props) => { if (multiple) { return ( onRootChange(level)} + onChange={(val) => onRootChange(level as TreeLevel, val)} > {treeOption.map((item) => ( = (props) => { ); } return ( - onRootChange(level)}> - {treeOption.map((item) => ( - - {item.label} - - ))} - + <> + onRootChange(level as TreeLevel, val)} + > + {treeOption.map((item) => ( + + {item.label} + + ))} + + ); }; From f6ac2895f8a56d4a32de1654c92706a956b7c3f1 Mon Sep 17 00:00:00 2001 From: epoll-j Date: Sat, 14 Sep 2024 14:44:30 +0800 Subject: [PATCH 3/6] feat(tree-select): add example feat #395 --- src/tree-select/_example/base.tsx | 39 +++++++ src/tree-select/_example/index.tsx | 24 +++++ src/tree-select/_example/multiple.tsx | 40 ++++++++ src/tree-select/_example/normal.tsx | 142 ++++++++++++++++++++++++++ src/tree-select/index.ts | 4 +- src/tree-select/tree-select.tsx | 2 +- 6 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 src/tree-select/_example/base.tsx create mode 100644 src/tree-select/_example/index.tsx create mode 100644 src/tree-select/_example/multiple.tsx create mode 100644 src/tree-select/_example/normal.tsx diff --git a/src/tree-select/_example/base.tsx b/src/tree-select/_example/base.tsx new file mode 100644 index 00000000..e172af3e --- /dev/null +++ b/src/tree-select/_example/base.tsx @@ -0,0 +1,39 @@ +import React, { useState } from 'react'; +import { TreeSelect } from 'tdesign-mobile-react'; +import { TdTreeSelectProps, TreeSelectValue } from '../type'; + +export default function BaseBadge() { + const chineseNumber = '一二三四五六七八九十'.split(''); + + const generateTree = (deep = 0, count = 10, prefix = ''): TdTreeSelectProps['options'] => { + const ans: TdTreeSelectProps['options'] = []; + + for (let i = 0; i < count; i += 1) { + const value = prefix ? `${prefix}-${i}` : `${i}`; + const rect = { + label: `选项${chineseNumber[i]}`, + value, + }; + + if (deep > 0) { + // @ts-ignore + rect.children = generateTree(deep - 1, 10, value); + } + ans.push(rect); + } + + return ans; + }; + const options = generateTree(1); + const [value, setValue] = useState(['1', '1-0']); + const onChange: TdTreeSelectProps['onChange'] = (itemValue, level) => { + console.log(itemValue, level); + setValue(itemValue); + }; + + return ( + <> + + + ); +} diff --git a/src/tree-select/_example/index.tsx b/src/tree-select/_example/index.tsx new file mode 100644 index 00000000..9d96e8f3 --- /dev/null +++ b/src/tree-select/_example/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import TDemoBlock from '../../../site/mobile/components/DemoBlock'; +import TDemoHeader from '../../../site/mobile/components/DemoHeader'; + +import BaseDemo from './base'; +import MultipleDemo from './multiple'; +import NormalDemo from './normal'; + +export default function BadgeDemo() { + return ( +
+ + + + + + + + + + +
+ ); +} diff --git a/src/tree-select/_example/multiple.tsx b/src/tree-select/_example/multiple.tsx new file mode 100644 index 00000000..1757e2cf --- /dev/null +++ b/src/tree-select/_example/multiple.tsx @@ -0,0 +1,40 @@ +import React, { useState } from 'react'; +import { TreeSelect } from 'tdesign-mobile-react'; +import { TdTreeSelectProps, TreeSelectValue } from '../type'; + +export default function BaseBadge() { + const chineseNumber = '一二三四五六七八九十'.split(''); + + const generateTree = (deep = 0, count = 10, prefix = ''): TdTreeSelectProps['options'] => { + const ans: TdTreeSelectProps['options'] = []; + + for (let i = 0; i < count; i += 1) { + const value = prefix ? `${prefix}-${i}` : `${i}`; + const rect = { + label: `选项${chineseNumber[i]}`, + value, + }; + + if (deep > 0) { + // @ts-ignore + rect.children = generateTree(deep - 1, 10, value); + } + ans.push(rect); + } + + return ans; + }; + const options = generateTree(1); + const [value, setValue] = useState(['0', ['0-0', '0-1']]); + + const onChange: TdTreeSelectProps['onChange'] = (itemValue, level) => { + console.log(itemValue, level); + setValue(itemValue); + }; + + return ( + <> + + + ); +} diff --git a/src/tree-select/_example/normal.tsx b/src/tree-select/_example/normal.tsx new file mode 100644 index 00000000..7b43353e --- /dev/null +++ b/src/tree-select/_example/normal.tsx @@ -0,0 +1,142 @@ +import React, { useState } from 'react'; +import { TreeSelect } from 'tdesign-mobile-react'; +import { TdTreeSelectProps, TreeSelectValue } from '../type'; + +export default function BaseBadge() { + const areaList = { + provinces: { + 110000: '北京市', + 440000: '广东省', + }, + cities: { + 110100: '北京市', + 440100: '广州市', + 440200: '韶关市', + 440300: '深圳市', + 440400: '珠海市', + 440500: '汕头市', + 440600: '佛山市', + }, + counties: { + 110101: '东城区', + 110102: '西城区', + 110105: '朝阳区', + 110106: '丰台区', + 110107: '石景山区', + 110108: '海淀区', + 110109: '门头沟区', + 110111: '房山区', + 110112: '通州区', + 110113: '顺义区', + 110114: '昌平区', + 110115: '大兴区', + 110116: '怀柔区', + 110117: '平谷区', + 110118: '密云区', + 110119: '延庆区', + 440103: '荔湾区', + 440104: '越秀区', + 440105: '海珠区', + 440106: '天河区', + 440111: '白云区', + 440112: '黄埔区', + 440113: '番禺区', + 440114: '花都区', + 440115: '南沙区', + 440117: '从化区', + 440118: '增城区', + 440203: '武江区', + 440204: '浈江区', + 440205: '曲江区', + 440222: '始兴县', + 440224: '仁化县', + 440229: '翁源县', + 440232: '乳源瑶族自治县', + 440233: '新丰县', + 440281: '乐昌市', + 440282: '南雄市', + 440303: '罗湖区', + 440304: '福田区', + 440305: '南山区', + 440306: '宝安区', + 440307: '龙岗区', + 440308: '盐田区', + 440309: '龙华区', + 440310: '坪山区', + 440311: '光明区', + 440402: '香洲区', + 440403: '斗门区', + 440404: '金湾区', + 440507: '龙湖区', + 440511: '金平区', + 440512: '濠江区', + 440513: '潮阳区', + 440514: '潮南区', + 440515: '澄海区', + 440523: '南澳县', + 440604: '禅城区', + 440605: '南海区', + 440606: '顺德区', + 440607: '三水区', + 440608: '高明区', + }, + }; + + const generateTree = () => { + const { provinces, cities, counties } = areaList; + const options: TdTreeSelectProps['options'] = []; + const eachObj = (obj: Record, cb: (item: string) => void) => Object.keys(obj).forEach(cb); + // @ts-ignore + const match = (v1, v2, base) => parseInt(v1 / base, 10) === parseInt(v2 / base, 10); + + eachObj(provinces, (prov) => { + const cityList: TdTreeSelectProps['options'] = []; + + eachObj(cities, (city) => { + const countyList: TdTreeSelectProps['options'] = []; + + if (match(city, prov, 10000)) { + eachObj(counties, (county) => { + if (match(county, city, 100)) { + countyList.push({ + label: counties[county], + value: county, + disabled: ['110102', '440104'].includes(county), + }); + } + }); + cityList.push({ + label: cities[city], + value: city, + children: countyList, + disabled: ['110100', '440200'].includes(city), + }); + } + }); + + const item = { + label: provinces[prov], + value: prov, + children: cityList, + }; + + options.push(item); + }); + + return options; + }; + + const options = generateTree(); + const [value, setValue] = useState(['110000', '110100', '110101']); + + const onChange: TdTreeSelectProps['onChange'] = (itemValue, level) => { + console.log(itemValue, level); + setValue(itemValue); + }; + + return ( + <> + + + ); +} diff --git a/src/tree-select/index.ts b/src/tree-select/index.ts index b355da1c..3880645d 100644 --- a/src/tree-select/index.ts +++ b/src/tree-select/index.ts @@ -5,6 +5,4 @@ import './style'; export * from './type'; export type TreeSelectProps = TdTreeSelectProps; -const TreeSelect = _TreeSelect; -// export const TreeSelect: WithInstallType = withInstall(_TreeSelect); -export default TreeSelect; +export const TreeSelect = _TreeSelect; diff --git a/src/tree-select/tree-select.tsx b/src/tree-select/tree-select.tsx index ccbe74be..76c5faa8 100644 --- a/src/tree-select/tree-select.tsx +++ b/src/tree-select/tree-select.tsx @@ -52,7 +52,7 @@ const TreeSelect: FC = (props) => { setTreeOptions(tmpTreeOptions); if (multiple) { const finalValue = innerValue; - if (finalValue[leafLevel] != null && !Array.isArray(finalValue[leafLevel])) { + if (finalValue[level] != null && !Array.isArray(finalValue[level])) { throw TypeError('应传入数组类型的 value'); } } From b77c4bd5855036472cd7a52b982c33062ee3536b Mon Sep 17 00:00:00 2001 From: epoll-j Date: Wed, 18 Sep 2024 17:23:17 +0800 Subject: [PATCH 4/6] docs(tree-select): update api docs --- src/tree-select/defaultProps.ts | 7 +------ src/tree-select/tree-select.en-US.md | 1 - src/tree-select/tree-select.md | 1 - src/tree-select/tree-select.tsx | 7 +++---- src/tree-select/type.ts | 5 ----- 5 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/tree-select/defaultProps.ts b/src/tree-select/defaultProps.ts index 7900daa0..89436caf 100644 --- a/src/tree-select/defaultProps.ts +++ b/src/tree-select/defaultProps.ts @@ -4,9 +4,4 @@ import { TdTreeSelectProps } from './type'; -export const treeSelectDefaultProps: TdTreeSelectProps = { - filterable: false, - height: 336, - multiple: false, - options: [], -}; +export const treeSelectDefaultProps: TdTreeSelectProps = { height: 336, multiple: false, options: [] }; diff --git a/src/tree-select/tree-select.en-US.md b/src/tree-select/tree-select.en-US.md index 7340ddee..519ba710 100644 --- a/src/tree-select/tree-select.en-US.md +++ b/src/tree-select/tree-select.en-US.md @@ -8,7 +8,6 @@ name | type | default | description | required -- | -- | -- | -- | -- className | String | - | className of component | N style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N -filterable | Boolean | false | \- | N height | String / Number | 336 | \- | N keys | Object | - | alias filed name in data。Typescript:`TreeKeysType`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N multiple | Boolean | false | \- | N diff --git a/src/tree-select/tree-select.md b/src/tree-select/tree-select.md index c57b16e9..0b7edfa2 100644 --- a/src/tree-select/tree-select.md +++ b/src/tree-select/tree-select.md @@ -8,7 +8,6 @@ -- | -- | -- | -- | -- className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N -filterable | Boolean | false | 是否可搜索 | N height | String / Number | 336 | 高度,默认单位为 px | N keys | Object | - | 用来定义 `value / label / disabled / children` 在 `data` 数据中对应的字段别名,示例:`{ value: 'key', label: 'name', children: 'list' }`。TS 类型:`TreeKeysType`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N multiple | Boolean | false | 是否允许多选 | N diff --git a/src/tree-select/tree-select.tsx b/src/tree-select/tree-select.tsx index 76c5faa8..f5da72e5 100644 --- a/src/tree-select/tree-select.tsx +++ b/src/tree-select/tree-select.tsx @@ -128,16 +128,15 @@ const TreeSelect: FC = (props) => { {treeOption.map((item) => ( - {item.label} - + label={item.label} + /> ))}
); diff --git a/src/tree-select/type.ts b/src/tree-select/type.ts index ae085bff..0a556da7 100644 --- a/src/tree-select/type.ts +++ b/src/tree-select/type.ts @@ -7,11 +7,6 @@ import { TreeOptionData, TreeKeysType } from '../common'; export interface TdTreeSelectProps { - /** - * 是否可搜索 - * @default false - */ - filterable?: boolean; /** * 高度,默认单位为 px * @default 336 From 340e61651f6f5764fcb5f6bbe39960055ace1147 Mon Sep 17 00:00:00 2001 From: epoll-j Date: Wed, 18 Sep 2024 17:27:09 +0800 Subject: [PATCH 5/6] test(tree-select): update snap --- test/snap/__snapshots__/csr.test.jsx.snap | 9175 +++++++++++++-------- test/snap/__snapshots__/ssr.test.jsx.snap | 24 +- 2 files changed, 5924 insertions(+), 3275 deletions(-) diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index e84cba47..b1765166 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -25316,38 +25316,118 @@ exports[`csr snapshot test > csr test src/overlay/_example/index.tsx 1`] = ` `; -exports[`csr snapshot test > csr test src/popover/_example/base.tsx 1`] = ` +exports[`csr snapshot test > csr test src/popup/_example/base.tsx 1`] = `
- - 跳转链接 + + 顶部弹出 - - + + + + +
`; -exports[`csr snapshot test > csr test src/popover/_example/index.tsx 1`] = ` +exports[`csr snapshot test > csr test src/popup/_example/custom-close.tsx 1`] = `
+ +
+`; + +exports[`csr snapshot test > csr test src/popup/_example/index.tsx 1`] = ` +
+
csr test src/popover/_example/index.tsx 1`] = `

- Link 链接 + Popup 弹窗层

- 当功能使用图标即可表意清楚时,可使用纯图标悬浮按钮,例如:添加、发布。 + 由其他控件触发,屏幕滑出或弹出一块自定义内容区域

csr test src/popover/_example/index.tsx 1`] = `

- 基础文字链接 + 基础弹出层

-
-
-

+ +

-
+ + + 中间弹出 + +
+

+ 01 组件示例 +

- 前置图标文字链接 + 应用示例

-
- - - - - - - - 跳转链接 - - - + + + +
+
+
+`; + +exports[`csr snapshot test > csr test src/popup/_example/with-title.tsx 1`] = ` +
+ + +
+`; + +exports[`csr snapshot test > csr test src/progress/_example/base.tsx 1`] = ` + +
+ +
+
+`; + +exports[`csr snapshot test > csr test src/progress/_example/circle.tsx 1`] = ` +
+
-
-

- 03 组件样式 -

-

- 链接尺寸 -

-
-
@@ -25951,848 +25979,842 @@ exports[`csr snapshot test > csr test src/popover/_example/index.tsx 1`] = `
`; -exports[`csr snapshot test > csr test src/popover/_example/linkSize.tsx 1`] = ` +exports[`csr snapshot test > csr test src/progress/_example/custom.tsx 1`] = ` -`; - -exports[`csr snapshot test > csr test src/popover/_example/prefix.tsx 1`] = ` - +
+ +
`; -exports[`csr snapshot test > csr test src/popover/_example/status.tsx 1`] = ` +exports[`csr snapshot test > csr test src/progress/_example/index.tsx 1`] = ` -`; - -exports[`csr snapshot test > csr test src/popover/_example/suffix.tsx 1`] = ` - + + + - -`; - -exports[`csr snapshot test > csr test src/popover/_example/theme.tsx 1`] = ` - + + + + - + - -`; - -exports[`csr snapshot test > csr test src/popover/_example/underline.tsx 1`] = ` - -`; - -exports[`csr snapshot test > csr test src/popup/_example/base.tsx 1`] = ` -
-
- - - - - - -
-`; - -exports[`csr snapshot test > csr test src/popup/_example/custom-close.tsx 1`] = ` -
- - -
-`; - -exports[`csr snapshot test > csr test src/popup/_example/index.tsx 1`] = ` -
-
-
-

- Popup 弹窗层 -

-

- 由其他控件触发,屏幕滑出或弹出一块自定义内容区域 -

+
+
+
+
+
+
+
+ 80% +
+
+
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
-

- 01 组件类型 -

- 基础弹出层 + 百分比内显进度条

- -`; - -exports[`csr snapshot test > csr test src/popup/_example/with-title.tsx 1`] = ` -
- - -
-`; - -exports[`csr snapshot test > csr test src/progress/_example/base.tsx 1`] = ` -
-
-
-
+
+
+
+ 80% +
+ + + + +
+
- 80% +
+
+
+ + + +
+ + + + +
+
+
+
+
+
+
+ + + +
+ + + + +
+
+
+
+
+
+
+ + + +
+ + + + +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/progress/_example/line.tsx 1`] = ` +
+
@@ -26800,17 +26822,20 @@ exports[`csr snapshot test > csr test src/progress/_example/base.tsx 1`] = ` class="t-progress" >
- 80% -
+ class="t-progress__inner" + style="width: 80%;" + /> +
+
+ 80%
@@ -26818,252 +26843,108 @@ exports[`csr snapshot test > csr test src/progress/_example/base.tsx 1`] = `
-
+
- 30% +
- - - - -
-
-
-
-
-`; - -exports[`csr snapshot test > csr test src/progress/_example/circle.tsx 1`] = ` -
-
-
-
-
-
- 80% -
- - - - -
-
-
-
-
-
-
-
- - - -
-
+
+
+
+
- - - -
-
+
+
+
+
- - - -
@@ -27071,7 +26952,7 @@ exports[`csr snapshot test > csr test src/progress/_example/circle.tsx 1`] = `
`; -exports[`csr snapshot test > csr test src/progress/_example/custom.tsx 1`] = ` +exports[`csr snapshot test > csr test src/progress/_example/plump.tsx 1`] = `
csr test src/progress/_example/custom.tsx 1`] = ` class="t-progress" >
-
-
- 88% + class="t-progress__info" + > + 80% +
@@ -27106,308 +26983,207 @@ exports[`csr snapshot test > csr test src/progress/_example/custom.tsx 1`] = ` class="example-progress__item" >
-
-
- 88% + class="t-progress__info" + > + 88% +
-
-
-`; - -exports[`csr snapshot test > csr test src/progress/_example/index.tsx 1`] = ` -
-
-

- Progress 进度条 -

-

- 展示操作的当前进度 -

-
-
-
-

- 01 类型 -

-

- 基础进度条 -

-
-
-
-
-
-
-
-
- 80% -
-
-
-
-
-
-
-
- 80% -
-
-
-
-
-
-
-
-
- 30% -
- - - - -
+ 88%
-

- 过渡样式 -

-
-
-
-
-
-
- 88% -
+ 88%
-
- -
- -
+
+
+`; + +exports[`csr snapshot test > csr test src/progress/_example/transition.tsx 1`] = ` +
+
-

- 自定义颜色/圆角 -

+
+
+
+ 88% +
+
+
+
+ +
+
+
+`; + +exports[`csr snapshot test > csr test src/result/_example/custom.tsx 1`] = ` +
+
+
+
-
-
-
-
- 88% -
-
-
-
-
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0s;" + />
-
-
-
-
- 88% -
-
+ class="t-loading__dot" + style="animation-duration: 0.8s; animation-delay: 0.26666666666666666s;" + /> +
+
+
+ 自定义结果 +
+
+ 描述文字 +
+
+
+`; + +exports[`csr snapshot test > csr test src/result/_example/index.tsx 1`] = ` +
+
+
+

+ Result 结果 +

+

+ 结果反馈 +

+
@@ -27417,151 +27193,136 @@ exports[`csr snapshot test > csr test src/progress/_example/index.tsx 1`] = `

- 02 组件状态 + 01类型

- 线性进度条 + 不同结果反馈

-
+
-
-
-
-
-
- 80% -
-
+ + +
+
+ 成功状态 +
+
+ 描述文字
-
-
-
-
-
- - - -
-
+ + +
+
+ 失败状态 +
+
+ 描述文字
-
-
-
-
-
- - - -
-
+ + +
+
+ 警示状态 +
+
+ 描述文字
-
-
-
-
-
- - - -
-
+ + +
+
+ 默认状态 +
+
+ 描述文字
@@ -27573,105 +27334,66 @@ exports[`csr snapshot test > csr test src/progress/_example/index.tsx 1`] = `
+

- 百分比内显进度条 + 自定义结果

-
-
-
-
- 80% -
-
-
-
-
-
- 88% +
+
+
+
-
-
-
-
- 88% -
-
-
-
+ 自定义结果
-
-
-
-
- 88% -
-
-
-
+ 描述文字
@@ -27682,219 +27404,30 @@ exports[`csr snapshot test > csr test src/progress/_example/index.tsx 1`] = `
+

- 环形进度条 + 页面位置展示

-
-
-
-
- 80% -
- - - - -
-
-
-
-
-
-
- - - -
- - - - -
-
-
-
-
-
-
- - - -
- - - - -
-
-
-
-
-
-
- - - -
- - - - -
-
-
+ + + 页面位置展示 + +
@@ -27902,380 +27435,378 @@ exports[`csr snapshot test > csr test src/progress/_example/index.tsx 1`] = `
`; -exports[`csr snapshot test > csr test src/progress/_example/line.tsx 1`] = ` +exports[`csr snapshot test > csr test src/result/_example/theme.tsx 1`] = `
-
+
-
-
-
-
-
- 80% -
-
+ + +
+
+ 成功状态 +
+
+ 描述文字
-
-
-
-
-
- - - -
-
+ + +
+
+ 失败状态 +
+
+ 描述文字
-
-
-
-
-
- - - -
-
+ + +
+
+ 警示状态 +
+
+ 描述文字
-
-
-
-
-
- - - -
-
+ + +
+
+ 默认状态 +
+
+ 描述文字
`; -exports[`csr snapshot test > csr test src/progress/_example/plump.tsx 1`] = ` +exports[`csr snapshot test > csr test src/search/_example/action.tsx 1`] = `
-
-
-
- 80% -
-
-
+ + +
+
+
+`; + +exports[`csr snapshot test > csr test src/search/_example/base.tsx 1`] = ` +
+
-
-
-
- 88% -
-
-
+ + +
+
+
+`; + +exports[`csr snapshot test > csr test src/search/_example/index.tsx 1`] = ` +
+
+

+ Search 搜索框 +

+

+ 用于用户输入搜索信息,并进行页面内容搜索。 +

+
+
+

+ 01 组件类型 +

+

+ 基础搜索框 +

+
+
- 88% + + + +
+

+ 输入值后显示取消按钮 +

+
+
- 88% + + + +
-
-
-`; - -exports[`csr snapshot test > csr test src/progress/_example/transition.tsx 1`] = ` -
-
-
-
-
-
+

- 88% -

+ 搜索框形状 +

-
-
- -
- -
-
-
-`; - -exports[`csr snapshot test > csr test src/result/_example/custom.tsx 1`] = ` -
-
-
-
-
-
+ + +
-
-
- 自定义结果 -
-
- 描述文字 -
-
-
-`; - -exports[`csr snapshot test > csr test src/result/_example/index.tsx 1`] = ` -
-
-
-

- Result 结果 -

-

- 结果反馈 -

-
@@ -28285,376 +27816,55 @@ exports[`csr snapshot test > csr test src/result/_example/index.tsx 1`] = `

- 01类型 + 03 组件状态

- 不同结果反馈 + 默认状态其他对齐方式

-
-
-
- - - -
-
- 成功状态 -
-
- 描述文字 -
-
-
-
- - - -
-
- 失败状态 -
-
- 描述文字 -
-
-
-
- - - -
-
- 警示状态 -
-
- 描述文字 -
-
+
-
-
- 默认状态 -
-
- 描述文字 +
-
-
- -

- 自定义结果 -

-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- 自定义结果 -
-
- 描述文字 -
-
-
-
-
-
- -

- 页面位置展示 -

-
-
-
- -
-
-
-
-
-`; - -exports[`csr snapshot test > csr test src/result/_example/theme.tsx 1`] = ` -
-
-
-
- - - -
-
- 成功状态 -
-
- 描述文字 -
-
-
-
- - - -
-
- 失败状态 -
-
- 描述文字 -
-
-
-
- - - -
-
- 警示状态 -
-
- 描述文字 -
-
-
-
- - - -
-
- 默认状态 -
-
- 描述文字 -
-
`; -exports[`csr snapshot test > csr test src/search/_example/action.tsx 1`] = ` +exports[`csr snapshot test > csr test src/search/_example/other.tsx 1`] = `
csr test src/search/_example/action.tsx 1`] = ` class="t-search" >
csr test src/search/_example/action.tsx 1`] = ` /> csr test src/search/_example/action.tsx 1`] = `
`; -exports[`csr snapshot test > csr test src/search/_example/base.tsx 1`] = ` +exports[`csr snapshot test > csr test src/search/_example/shape.tsx 1`] = `
csr test src/search/_example/base.tsx 1`] = ` class="t-search" >
csr test src/search/_example/base.tsx 1`] = `
`; -exports[`csr snapshot test > csr test src/search/_example/index.tsx 1`] = ` +exports[`csr snapshot test > csr test src/side-bar/_example/base.tsx 1`] = `
-

- Search 搜索框 -

-

- 用于用户输入搜索信息,并进行页面内容搜索。 -

-
-
-
-

- 01 组件类型 -

-

- 基础搜索框 -

+
+ 选项一 +
+
+
+
+
+
+
-
-
-

- 输入值后显示取消按钮 -

+
+ 选项三 +
+
+ 6
+
+
+ 选项五 +
+
+
-

- 02 组件样式 -

-

- 搜索框形状 -

-
-
+ 标题一 +
-
-
-
-

- 03 组件状态 -

-

- 默认状态其他对齐方式 -

-
-
-
-
-
-
-
-`; - -exports[`csr snapshot test > csr test src/search/_example/other.tsx 1`] = ` -
-
- -
-
-`; - -exports[`csr snapshot test > csr test src/search/_example/shape.tsx 1`] = ` -
-
- -
-
-`; - -exports[`csr snapshot test > csr test src/side-bar/_example/base.tsx 1`] = ` -
-