From f03c24812bfcdb4242a39a1ae926010c84d58410 Mon Sep 17 00:00:00 2001 From: ming680 <827668037@qq.com> Date: Wed, 28 Aug 2024 15:52:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(dropdownmenu):=20=E6=96=B0=E5=A2=9EDropdow?= =?UTF-8?q?nMenu=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #392 --- site/mobile/mobile.config.js | 5 + site/web/site.config.js | 6 + src/dropdown-menu/DropdownItem.tsx | 273 ++++++++++++++++++ src/dropdown-menu/DropdownMenu.tsx | 66 +++++ src/dropdown-menu/DropdownMenuContext.ts | 11 + src/dropdown-menu/_example/customized.tsx | 106 +++++++ src/dropdown-menu/_example/direction.tsx | 55 ++++ src/dropdown-menu/_example/disabled.tsx | 9 + src/dropdown-menu/_example/index.tsx | 38 +++ src/dropdown-menu/_example/multiple.tsx | 88 ++++++ src/dropdown-menu/_example/single.tsx | 55 ++++ .../_example/style/customized.less | 16 + src/dropdown-menu/defaultProps.ts | 21 ++ src/dropdown-menu/dropdown-menu.en-US.md | 64 ++++ src/dropdown-menu/dropdown-menu.md | 64 ++++ src/dropdown-menu/index.ts | 10 + src/dropdown-menu/style/css.js | 1 + src/dropdown-menu/style/index.js | 2 + src/dropdown-menu/type.ts | 99 +++++++ src/index.ts | 1 + 20 files changed, 990 insertions(+) create mode 100644 src/dropdown-menu/DropdownItem.tsx create mode 100644 src/dropdown-menu/DropdownMenu.tsx create mode 100644 src/dropdown-menu/DropdownMenuContext.ts create mode 100644 src/dropdown-menu/_example/customized.tsx create mode 100644 src/dropdown-menu/_example/direction.tsx create mode 100644 src/dropdown-menu/_example/disabled.tsx create mode 100644 src/dropdown-menu/_example/index.tsx create mode 100644 src/dropdown-menu/_example/multiple.tsx create mode 100644 src/dropdown-menu/_example/single.tsx create mode 100644 src/dropdown-menu/_example/style/customized.less create mode 100644 src/dropdown-menu/defaultProps.ts create mode 100644 src/dropdown-menu/dropdown-menu.en-US.md create mode 100644 src/dropdown-menu/dropdown-menu.md create mode 100644 src/dropdown-menu/index.ts create mode 100644 src/dropdown-menu/style/css.js create mode 100644 src/dropdown-menu/style/index.js create mode 100644 src/dropdown-menu/type.ts diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index d5967f20..d52135ee 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -132,6 +132,11 @@ export default { name: 'dialog', component: () => import('tdesign-mobile-react/dialog/_example/index.jsx'), }, + { + title: 'DropdownMenu 下拉菜单', + name: 'dropdown-menu', + component: () => import('tdesign-mobile-react/dropdown-menu/_example/index.tsx'), + }, { title: 'Loading 加载中', name: 'loading', diff --git a/site/web/site.config.js b/site/web/site.config.js index f709bd4a..b00c2367 100644 --- a/site/web/site.config.js +++ b/site/web/site.config.js @@ -323,6 +323,12 @@ export default { path: '/mobile-react/components/dialog', component: () => import('tdesign-mobile-react/dialog/dialog.md'), }, + { + title: 'DropdownMenu 下拉菜单', + name: 'dropdown-menu', + path: '/mobile-react/components/dropdown-menu', + component: () => import('tdesign-mobile-react/dropdown-menu/dropdown-menu.md'), + }, { title: 'Drawer 抽屉', name: 'drawer', diff --git a/src/dropdown-menu/DropdownItem.tsx b/src/dropdown-menu/DropdownItem.tsx new file mode 100644 index 00000000..a3109326 --- /dev/null +++ b/src/dropdown-menu/DropdownItem.tsx @@ -0,0 +1,273 @@ +import { useClickAway } from 'ahooks'; +import cx from 'classnames'; +import uniqueId from 'lodash/uniqueId'; +import React, { FC, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import { CaretDownSmallIcon, CaretUpSmallIcon } from 'tdesign-icons-react'; +import { Button, Checkbox, Popup, Radio, RadioGroup } from 'tdesign-mobile-react'; +import useDefault from 'tdesign-mobile-react/_util/useDefault'; +import CheckboxGroup from 'tdesign-mobile-react/checkbox/CheckboxGroup'; +import { StyledProps } from 'tdesign-mobile-react/common'; +import useDefaultProps from 'tdesign-mobile-react/hooks/useDefaultProps'; +import useConfig from '../_util/useConfig'; +import { dropdownItemDefaultProps } from './defaultProps'; +import DropdownMenuContext from './DropdownMenuContext'; +import type { TdDropdownItemProps } from './type'; + +export interface DropdownItemProps extends TdDropdownItemProps, StyledProps {} + +const DropdownItem: FC = (props) => { + const { + className, + style, + disabled, + options: inputOptions, + optionsColumns, + label, + value, + defaultValue, + onChange, + multiple, + onConfirm, + onReset, + footer, + keys, + } = useDefaultProps(props, dropdownItemDefaultProps); + const { classPrefix } = useConfig(); + const itemClass = `${classPrefix}-dropdown-item`; + const [innerValue, setInnerValue] = useDefault(value, defaultValue, onChange); + const [modalValue, setModalValue] = useState(innerValue); + + const options = useMemo( + () => + inputOptions.map((item) => ({ + value: item[keys?.value ?? 'value'], + label: item[keys?.label ?? 'label'], + disabled: item[keys?.disabled ?? 'disabled'], + })), + [keys, inputOptions], + ); + + const [id] = useState(uniqueId()); + + const { direction, activedId, onChangeActivedId, showOverlay, zIndex, closeOnClickOverlay } = + useContext(DropdownMenuContext); + + const labelText = useMemo( + () => label || options.find((item) => item.value === innerValue)?.label || '', + [options, label, innerValue], + ); + + const isActived = id === activedId; + + const menuItemRef = useRef(); + const itemRef = useRef(); + + const getDropdownItemStyle = () => { + const ele = menuItemRef.current; + if (!ele) { + return {}; + } + + const { top, bottom } = ele.getBoundingClientRect(); + + if (direction === 'up') { + return { + transform: 'rotateX(180deg) rotateY(180deg)', + zIndex, + bottom: `calc(100vh - ${top}px)`, + }; + } + + return { + zIndex, + top: `${bottom}px`, + }; + }; + + useClickAway(() => { + if (!isActived || !closeOnClickOverlay) { + return; + } + onChangeActivedId(''); + }, itemRef); + + useEffect(() => { + if (isActived) { + setModalValue(innerValue); + } + }, [isActived, innerValue]); + + const attach = useCallback(() => itemRef.current || document.body, []); + + return ( + <> +
{ + if (disabled) { + return; + } + onChangeActivedId(isActived ? '' : id); + if (!isActived) { + e.stopPropagation(); + } + }} + > +
{labelText}
+ {direction === 'down' ? ( + + ) : ( + + )} +
+ {isActived ? ( +
+ {/* TODO Popup 暂不支持 duration */} + { + if (!visible) { + onChangeActivedId(''); + } + }} + closeOnOverlayClick={closeOnClickOverlay} + showOverlay={showOverlay} + zIndex={zIndex} + style={{ + position: 'absolute', + borderRadius: 0, + }} + overlayProps={{ + style: { + position: 'absolute', + }, + }} + className={`${itemClass}__popup-host`} + > +
+
+ {props.children ? ( + props.children + ) : ( + <> + {/* TODO checkbox 组件未升级 样式不对 */} + {multiple ? ( + { + setModalValue(value); + }} + // className={`itemClass__checkbox-group`} + style={{ + gridTemplateColumns: `repeat(${optionsColumns}, 1fr)`, + }} + > + {options.map((item, index) => ( + + ))} + + ) : ( + { + setModalValue(value); + setInnerValue(value); + onChangeActivedId(''); + }} + > + {/* TODO radio 暂不支持 icon line */} + {options.map((item, index) => ( + + ))} + + )} + + )} +
+ + {footer ? footer : null} + + {multiple && !footer ? ( +
+ + +
+ ) : null} +
+
+
+ ) : null} + + ); +}; + +DropdownItem.displayName = 'DropdownItem'; + +export default DropdownItem; diff --git a/src/dropdown-menu/DropdownMenu.tsx b/src/dropdown-menu/DropdownMenu.tsx new file mode 100644 index 00000000..d833c725 --- /dev/null +++ b/src/dropdown-menu/DropdownMenu.tsx @@ -0,0 +1,66 @@ +import cx from 'classnames'; +import React, { ComponentProps, FC, forwardRef, useImperativeHandle, useState } from 'react'; +import { StyledProps } from 'tdesign-mobile-react/common'; +import useDefaultProps from 'tdesign-mobile-react/hooks/useDefaultProps'; +import useConfig from '../_util/useConfig'; +import { dropdownMenuDefaultProps } from './defaultProps'; +import DropdownItem from './DropdownItem'; +import DropdownMenuContext from './DropdownMenuContext'; +import type { TdDropdownMenuProps } from './type'; + +export interface DropdownMenuProps extends TdDropdownMenuProps, StyledProps {} + +type DropdownMenuRef = { + collapseMenu: () => void; +}; + +const DropdownMenu: FC }> = forwardRef< + DropdownMenuRef, + DropdownMenuProps +>((props, ref) => { + const { className, style, direction, zIndex, closeOnClickOverlay, showOverlay, duration } = + useDefaultProps(props, dropdownMenuDefaultProps); + + const { classPrefix } = useConfig(); + const name = `${classPrefix}-dropdown-menu`; + + const items = []; + React.Children.forEach(props.children, (child: typeof DropdownItem) => { + if ( + React.isValidElement>(child) && + (child.type as any)?.displayName === DropdownItem.displayName + ) { + items.push(child); + } + }); + + const [activedId, setActivedId] = useState(''); + + useImperativeHandle(ref, () => ({ + collapseMenu: () => { + setActivedId(''); + }, + })); + + return ( + +
+ {items} +
+
+ ); +}); + +DropdownMenu.displayName = 'DropdownMenu'; + +export default DropdownMenu; diff --git a/src/dropdown-menu/DropdownMenuContext.ts b/src/dropdown-menu/DropdownMenuContext.ts new file mode 100644 index 00000000..e237cc01 --- /dev/null +++ b/src/dropdown-menu/DropdownMenuContext.ts @@ -0,0 +1,11 @@ +import noop from 'lodash/noop'; +import React from 'react'; +import { dropdownMenuDefaultProps } from './defaultProps'; + +const DropdownMenuContext = React.createContext({ + ...dropdownMenuDefaultProps, + activedId: '', + onChangeActivedId: noop, +}); + +export default DropdownMenuContext; diff --git a/src/dropdown-menu/_example/customized.tsx b/src/dropdown-menu/_example/customized.tsx new file mode 100644 index 00000000..039d9694 --- /dev/null +++ b/src/dropdown-menu/_example/customized.tsx @@ -0,0 +1,106 @@ +import cloneDeep from 'lodash/cloneDeep'; +import noop from 'lodash/noop'; +import React, { useRef, useState } from 'react'; +import { Button, Checkbox, DropdownItem, DropdownMenu } from 'tdesign-mobile-react'; +import CheckboxGroup from 'tdesign-mobile-react/checkbox/CheckboxGroup'; +import './style/customized.less'; + +const chineseNumber = '一二三四五六七八九十'.split(''); + +const options1 = new Array(8).fill(null).map((_, i) => ({ + label: `类型${chineseNumber[i]}`, + value: `type_${i + 1}`, + disabled: false, +})); + +const options2 = new Array(8).fill(null).map((_, i) => ({ + label: `角色${chineseNumber[i]}`, + value: `role_${i + 1}`, + disabled: false, +})); + +const groups = [ + { + label: '类型', + options: [ + ...options1, + { + label: '禁用选项', + value: 'disabled', + disabled: true, + }, + ], + }, + { + label: '角色', + options: [ + ...options2, + { + label: '禁用选项', + value: 'disabled', + disabled: true, + }, + ], + }, +]; + +export const CustomizedDemo = () => { + const [checkSelectGroup, setCheckSelectGroup] = useState([['type_1', 'type_2'], ['role_2']]); + + const ref = useRef({ collapseMenu: noop }); + return ( + + + + + + } + label="三列多选" + multiple + options-columns="1" + > + {groups.map((group, index) => ( + <> +
+ {group.label} +
+ {/* TODO checkbox 组件未升级 */} + { + const newValue = cloneDeep(checkSelectGroup); + newValue[index] = value; + setCheckSelectGroup(newValue); + }} + > + {group.options.map((item, index) => ( + + ))} + + + ))} +
+
+ ); +}; diff --git a/src/dropdown-menu/_example/direction.tsx b/src/dropdown-menu/_example/direction.tsx new file mode 100644 index 00000000..74ab3db4 --- /dev/null +++ b/src/dropdown-menu/_example/direction.tsx @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import { DropdownItem, DropdownMenu } from 'tdesign-mobile-react'; + +const product = { + value: 'all', + options: [ + { + value: 'all', + label: '全部产品', + disabled: false, + }, + { + value: 'new', + label: '最新产品', + disabled: false, + }, + { + value: 'hot', + label: '最火产品', + disabled: false, + }, + ], +}; + +const sorter = { + value: 'default', + options: [ + { + value: 'default', + label: '默认排序', + disabled: false, + }, + { + value: 'price', + label: '价格从高到低', + disabled: false, + }, + ], +}; + +export const DirectionDemo = () => { + const [productValue, setProductValue] = useState(product.value); + const [sorterValue, setSorterValue] = useState(sorter.value); + + return ( + + setProductValue(value)} + /> + setSorterValue(value)} /> + + ); +}; diff --git a/src/dropdown-menu/_example/disabled.tsx b/src/dropdown-menu/_example/disabled.tsx new file mode 100644 index 00000000..a305a606 --- /dev/null +++ b/src/dropdown-menu/_example/disabled.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { DropdownItem, DropdownMenu } from 'tdesign-mobile-react'; + +export const DisabledDemo = () => ( + + + + +); diff --git a/src/dropdown-menu/_example/index.tsx b/src/dropdown-menu/_example/index.tsx new file mode 100644 index 00000000..adeb8a02 --- /dev/null +++ b/src/dropdown-menu/_example/index.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import TDemoBlock from '../../../site/mobile/components/DemoBlock'; +import TDemoHeader from '../../../site/mobile/components/DemoHeader'; + +import { CustomizedDemo } from './customized'; +import { DirectionDemo } from './direction'; +import { DisabledDemo } from './disabled'; +import { MultipleDemo } from './multiple'; +import { SingleDemo } from './single'; + +export default function RadioDemo() { + return ( +
+ + + + + + + + + + + + + + + + + + +
+
+ ); +} diff --git a/src/dropdown-menu/_example/multiple.tsx b/src/dropdown-menu/_example/multiple.tsx new file mode 100644 index 00000000..15afa326 --- /dev/null +++ b/src/dropdown-menu/_example/multiple.tsx @@ -0,0 +1,88 @@ +import React, { useState } from 'react'; +import { DropdownItem, DropdownMenu } from 'tdesign-mobile-react'; + +const chineseNumber = '一二三四五六七八九十'.split(''); + +const singleColumnOptions = new Array(8).fill(null).map((_, i) => ({ + label: `选项${chineseNumber[i]}`, + value: `option_${i + 1}`, + disabled: false, +})); + +singleColumnOptions.push({ + label: '禁用选项', + value: 'disabled', + disabled: true, +}); + +const doubleColumnsOptions = [ + ...singleColumnOptions, + { + label: '禁用选项', + value: 'disabled', + disabled: true, + }, +]; + +const tripleColumnsOptions = [ + ...doubleColumnsOptions, + { + label: '禁用选项', + value: 'disabled', + disabled: true, + }, +]; + +tripleColumnsOptions.splice(8, 0, { + label: `选项${chineseNumber[8]}`, + value: `option_${9}`, + disabled: false, +}); + +const emptyArr = new Array(10).fill(null); +const options = emptyArr.map((_, i) => ({ + label: '选项名称', + value: `option_${i + 1}`, + disabled: false, +})); + +options.push({ + label: '禁用选项', + value: 'disabled-item', + disabled: true, +}); + +export const MultipleDemo = () => { + const [singleValue, setSingleValue] = useState(['option_1']); + const [doubleValue, setDoubleValue] = useState(['option_1', 'option_2']); + const [tripleValue, setTripleValue] = useState(['option_1', 'option_2', 'option_3']); + + return ( + + setSingleValue(value)} + /> + setDoubleValue(value)} + /> + setTripleValue(value)} + /> + + ); +}; diff --git a/src/dropdown-menu/_example/single.tsx b/src/dropdown-menu/_example/single.tsx new file mode 100644 index 00000000..4c52dd47 --- /dev/null +++ b/src/dropdown-menu/_example/single.tsx @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import { DropdownItem, DropdownMenu } from 'tdesign-mobile-react'; + +const product = { + value: 'all', + options: [ + { + value: 'all', + label: '全部产品', + disabled: false, + }, + { + value: 'new', + label: '最新产品', + disabled: false, + }, + { + value: 'hot', + label: '最火产品', + disabled: false, + }, + ], +}; + +const sorter = { + value: 'default', + options: [ + { + value: 'default', + label: '默认排序', + disabled: false, + }, + { + value: 'price', + label: '价格从高到低', + disabled: false, + }, + ], +}; + +export const SingleDemo = () => { + const [productValue, setProductValue] = useState(product.value); + const [sorterValue, setSorterValue] = useState(sorter.value); + + return ( + + setProductValue(value)} + /> + setSorterValue(value)} /> + + ); +}; diff --git a/src/dropdown-menu/_example/style/customized.less b/src/dropdown-menu/_example/style/customized.less new file mode 100644 index 00000000..7759cc45 --- /dev/null +++ b/src/dropdown-menu/_example/style/customized.less @@ -0,0 +1,16 @@ +.demo-dropdown-item { + &_label { + padding: 16px 0 0 16px; + font-size: 14px; + color: var(--td-text-color-primary, rgba(0, 0, 0, 0.9)); + } + &_footer { + display: flex; + width: 100%; + --td-button-border-radius: 0; + + .t-button { + flex: 1; + } + } +} diff --git a/src/dropdown-menu/defaultProps.ts b/src/dropdown-menu/defaultProps.ts new file mode 100644 index 00000000..e0966adc --- /dev/null +++ b/src/dropdown-menu/defaultProps.ts @@ -0,0 +1,21 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdDropdownItemProps, TdDropdownMenuProps } from './type'; + +export const dropdownMenuDefaultProps: TdDropdownMenuProps = { + closeOnClickOverlay: true, + direction: 'down', + duration: 200, + showOverlay: true, + zIndex: 11600, +}; + +export const dropdownItemDefaultProps: TdDropdownItemProps = { + disabled: false, + multiple: false, + options: [], + optionsColumns: 1, + defaultValue: undefined, +}; diff --git a/src/dropdown-menu/dropdown-menu.en-US.md b/src/dropdown-menu/dropdown-menu.en-US.md new file mode 100644 index 00000000..85ea4385 --- /dev/null +++ b/src/dropdown-menu/dropdown-menu.en-US.md @@ -0,0 +1,64 @@ +--- +title: DropdownMenu +description: The menu displays several juxtaposed option categories that are used to filter the content of the entire page, consisting of a menu panel and menu options. +spline: base +isComponent: true +toc: false +--- + +### Radio DropdownMenu + +This parameter is used to select scenarios where an option is required + +{{ single }} + +### Multi-Select DropdownMenu + +This parameter is used when multiple options can be selected +The label does not change the name after selection, and the title displays a maximum of 4 characters beyond "..." handle +The multiple selection dropdownMenu can have the following styles depending on the content of the selection + +{{ multiple }} + +### Disable Menu/Options + +{{ disabled }} + +### Slot Style + +{{ customized }} + + +## API + +### DropdownMenu Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +closeOnClickOverlay | Boolean | true | \- | N +direction | String | down | options: down/up | N +duration | String / Number | 200 | \- | N +showOverlay | Boolean | true | \- | N +zIndex | Number | 11600 | \- | N + + +### DropdownItem Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +disabled | Boolean | false | \- | N +footer | TElement | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +keys | Object | - | Typescript:`KeysType`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +label | String | - | \- | N +multiple | Boolean | false | \- | N +options | Array | [] | Typescript:`Array` `interface DropdownOption { label: string; disabled: boolean; value: DropdownValue; }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/dropdown-menu/type.ts) | N +optionsColumns | String / Number | 1 | \- | N +value | String / Number / Array | undefined | Typescript:`DropdownValue ` `type DropdownValue = string \| number \| Array;`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/dropdown-menu/type.ts) | N +defaultValue | String / Number / Array | undefined | uncontrolled property。Typescript:`DropdownValue ` `type DropdownValue = string \| number \| Array;`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/dropdown-menu/type.ts) | N +onChange | Function | | Typescript:`(value: DropdownValue) => void`
| N +onConfirm | Function | | Typescript:`(value: DropdownValue) => void`
| N +onReset | Function | | Typescript:`(value: DropdownValue) => void`
| N diff --git a/src/dropdown-menu/dropdown-menu.md b/src/dropdown-menu/dropdown-menu.md new file mode 100644 index 00000000..72744bd2 --- /dev/null +++ b/src/dropdown-menu/dropdown-menu.md @@ -0,0 +1,64 @@ +--- +title: DropdownMenu 下拉菜单 +description: 菜单呈现数个并列的选项类目,用于整个页面的内容筛选,由菜单面板和菜单选项组成。 +spline: base +isComponent: true +toc: false +--- + +### 单选下拉菜单 + +用于选择项需要单选的场景 + +{{ single }} + +### 多选下拉菜单 + +用于选择项可以进行多选的场景 +选择后标签不改变名称,标题最多展示 4 个字符超出“…”处理 +多选下拉菜单根据选择项内容的差异,可以有以下不同的样式 + +{{ multiple }} + +### 禁用菜单/选项 + +{{ disabled }} + +### 插槽样式 + +{{ customized }} + + +## API + +### DropdownMenu Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +closeOnClickOverlay | Boolean | true | 是否在点击遮罩层后关闭菜单 | N +direction | String | down | 菜单展开方向。可选项:down/up | N +duration | String / Number | 200 | 动画时长 | N +showOverlay | Boolean | true | 是否显示遮罩层 | N +zIndex | Number | 11600 | 菜单栏 z-index 层级 | N + + +### DropdownItem Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +className | String | - | 类名 | N +style | Object | - | 样式,TS 类型:`React.CSSProperties` | N +disabled | Boolean | false | 是否禁用 | N +footer | TElement | - | 底部。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +keys | Object | - | 用来定义 value / label 在 `options` 中对应的字段别名。TS 类型:`KeysType`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +label | String | - | 标题 | N +multiple | Boolean | false | 是否多选 | N +options | Array | [] | 选项数据。TS 类型:`Array` `interface DropdownOption { label: string; disabled: boolean; value: DropdownValue; }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/dropdown-menu/type.ts) | N +optionsColumns | String / Number | 1 | 选项分栏(1-3) | N +value | String / Number / Array | undefined | 选中值。TS 类型:`DropdownValue ` `type DropdownValue = string \| number \| Array;`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/dropdown-menu/type.ts) | N +defaultValue | String / Number / Array | undefined | 选中值。非受控属性。TS 类型:`DropdownValue ` `type DropdownValue = string \| number \| Array;`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/dropdown-menu/type.ts) | N +onChange | Function | | TS 类型:`(value: DropdownValue) => void`
值改变时触发 | N +onConfirm | Function | | TS 类型:`(value: DropdownValue) => void`
点击确认时触发 | N +onReset | Function | | TS 类型:`(value: DropdownValue) => void`
点击重置时触发 | N diff --git a/src/dropdown-menu/index.ts b/src/dropdown-menu/index.ts new file mode 100644 index 00000000..29eabf19 --- /dev/null +++ b/src/dropdown-menu/index.ts @@ -0,0 +1,10 @@ +import _DropdownItem from './DropdownItem'; +import _DropdownMenu from './DropdownMenu'; + +import './style'; + +export * from './type'; + +export const DropdownMenu = _DropdownMenu; +export const DropdownItem = _DropdownItem; +export default DropdownMenu; diff --git a/src/dropdown-menu/style/css.js b/src/dropdown-menu/style/css.js new file mode 100644 index 00000000..6a9a4b13 --- /dev/null +++ b/src/dropdown-menu/style/css.js @@ -0,0 +1 @@ +import './index.css'; diff --git a/src/dropdown-menu/style/index.js b/src/dropdown-menu/style/index.js new file mode 100644 index 00000000..2c8d0cd2 --- /dev/null +++ b/src/dropdown-menu/style/index.js @@ -0,0 +1,2 @@ +import '../../_common/style/mobile/components/dropdown-item/v2/_index.less'; +import '../../_common/style/mobile/components/dropdown-menu/v2/_index.less'; diff --git a/src/dropdown-menu/type.ts b/src/dropdown-menu/type.ts new file mode 100644 index 00000000..7f341ec6 --- /dev/null +++ b/src/dropdown-menu/type.ts @@ -0,0 +1,99 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { KeysType, TElement } from '../common'; + +export interface TdDropdownMenuProps { + /** + * 是否在点击遮罩层后关闭菜单 + * @default true + */ + closeOnClickOverlay?: boolean; + /** + * 菜单展开方向 + * @default down + */ + direction?: 'down' | 'up'; + /** + * 动画时长 + * @default 200 + */ + duration?: string | number; + /** + * 是否显示遮罩层 + * @default true + */ + showOverlay?: boolean; + /** + * 菜单栏 z-index 层级 + * @default 11600 + */ + zIndex?: number; +} + +export interface TdDropdownItemProps { + /** + * 是否禁用 + * @default false + */ + disabled?: boolean; + /** + * 底部 + */ + footer?: TElement; + /** + * 用来定义 value / label 在 `options` 中对应的字段别名 + */ + keys?: KeysType; + /** + * 标题 + * @default '' + */ + label?: string; + /** + * 是否多选 + * @default false + */ + multiple?: boolean; + /** + * 选项数据 + * @default [] + */ + options?: Array; + /** + * 选项分栏(1-3) + * @default 1 + */ + optionsColumns?: string | number; + /** + * 选中值 + */ + value?: DropdownValue; + /** + * 选中值,非受控属性 + */ + defaultValue?: DropdownValue; + /** + * 值改变时触发 + */ + onChange?: (value: DropdownValue) => void; + /** + * 点击确认时触发 + */ + onConfirm?: (value: DropdownValue) => void; + /** + * 点击重置时触发 + */ + onReset?: (value: DropdownValue) => void; +} + +export interface DropdownOption { + label: string; + disabled: boolean; + value: DropdownValue; +} + +export type DropdownValue = string | number | Array; diff --git a/src/index.ts b/src/index.ts index 91b4e09e..277c95b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,6 +54,7 @@ export * from './table'; */ export * from './back-top'; export * from './dialog'; +export * from './dropdown-menu'; export * from './loading'; export * from './message'; export * from './popup';