From f5972d708cceb8ea42eeaf26fbb4708a212f3987 Mon Sep 17 00:00:00 2001 From: ming680 <827668037@qq.com> Date: Mon, 8 Jul 2024 01:02:22 +0800 Subject: [PATCH] feat(menu): menu --- src/menu/Menu.tsx | 20 +++++++++++--- src/menu/MenuItem.tsx | 42 ++++++++++++++++++++++++----- src/menu/README.md | 20 ++++++++++++-- src/menu/_example/base.tsx | 13 --------- src/menu/_example/closable-side.tsx | 40 +++++++++++++++++++++++++++ src/menu/type.ts | 13 ++------- 6 files changed, 112 insertions(+), 36 deletions(-) delete mode 100644 src/menu/_example/base.tsx create mode 100644 src/menu/_example/closable-side.tsx diff --git a/src/menu/Menu.tsx b/src/menu/Menu.tsx index 62c9344..50d44f6 100644 --- a/src/menu/Menu.tsx +++ b/src/menu/Menu.tsx @@ -1,11 +1,11 @@ -import { Component, OmiDOMAttributes, tag } from 'omi'; +import { bind, Component, OmiDOMAttributes, signal, tag } from 'omi'; import classname, { getClassPrefix } from '../_util/classname'; import { getChildrenArray, hasSlot } from '../_util/component'; import { StyledProps } from '../common'; import { DEFAULT_MENU_WIDTH } from './_util/constant'; import { menuDefaultProps } from './defaultProps'; -import { TdMenuProps } from './type'; +import { MenuValue, TdMenuProps } from './type'; export interface MenuProps extends TdMenuProps, StyledProps, OmiDOMAttributes {} @@ -15,12 +15,26 @@ export default class Menu extends Component { static defaultProps = {}; + active = signal(''); + + provide = { + active: this.active, + onChange: this.handleChange, + }; + + @bind + handleChange(value: MenuValue) { + this.fire('change', value); + } + render() { - const { className, style, width, collapsed } = { + const { className, style, width, collapsed, value } = { ...menuDefaultProps, ...this.props, }; + this.active.value = value; + const classPrefix = getClassPrefix(); const menuWidthArr = Array.isArray(width) ? width : [width, DEFAULT_MENU_WIDTH[1]]; diff --git a/src/menu/MenuItem.tsx b/src/menu/MenuItem.tsx index e2d972f..21b109f 100644 --- a/src/menu/MenuItem.tsx +++ b/src/menu/MenuItem.tsx @@ -1,4 +1,4 @@ -import { Component, tag } from 'omi'; +import { bind, Component, tag } from 'omi'; import classname, { getClassPrefix } from '../_util/classname'; import { convertToLightDomNode } from '../_util/lightDom'; @@ -8,25 +8,53 @@ import { TdMenuItemProps } from './type'; export interface MenuItemProps extends TdMenuItemProps, StyledProps {} @tag('t-menu-item') -export default class Menu extends Component { +export default class MenuItem extends Component { static isLightDOM = true; + inject = ['active', 'onChange']; + + constructor() { + super(); + this.addEventListener('click', this.handleClick); + } + + @bind + handleClick(evt: MouseEvent) { + if (!(evt instanceof MouseEvent)) { + // 防止死循环 下面还会 fire('click') 又触发了当前函数的执行 + return; + } + // 阻止自定义dom上绑定的onClick原生事件 + evt.stopImmediatePropagation(); + if (this.props.disabled) { + return; + } + this.fire('click', { + context: this, + value: this.props.value, + }); + this.injection.onChange?.(this.props.value); + } + + uninstalled() { + this.removeEventListener('click', this.handleClick); + } + render() { - const { label, icon, className, disabled, href, target } = this.props; + const { label, icon, className, disabled, href, target, value } = this.props; const classPrefix = getClassPrefix(); + const lightIcon = convertToLightDomNode(icon); + this.className = classname(`${classPrefix}-menu__item`, className, { [`${classPrefix}-is-disabled`]: disabled, - // [`${classPrefix}-is-active`]: value === active, + [`${classPrefix}-is-active`]: value === this.injection.active.value, [`${classPrefix}-menu__item--plain`]: !icon, }); - const lightIcon = convertToLightDomNode(icon); - return ( <> - {icon} {lightIcon} {href ? ( diff --git a/src/menu/README.md b/src/menu/README.md index 608a873..c669eed 100644 --- a/src/menu/README.md +++ b/src/menu/README.md @@ -6,10 +6,11 @@ usage: { title: '', description: '' } spline: base --- -### 基础使用 +### 可收起的侧边导航 -{{ base }} +在侧边导航上提供收起按钮,点击后可以将侧边栏最小化,常见于带有图标的侧边导航。 +{{ closable-side }} ## API @@ -17,8 +18,23 @@ spline: base 名称 | 类型 | 默认值 | 说明 | 必传 -- | -- | -- | -- | -- +className | String | - | 类名 | N +collapsed | Boolean | false | 是否收起菜单 | N +logo | TElement | - | 站点 LOGO。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N +operations | TElement | - | 导航操作区域。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N +value | String / Number | - | 激活菜单项。TS 类型:`MenuValue` `type MenuValue = string \| number`。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/menu/type.ts) | N +width | String / Number / Array | '232px' | 菜单宽度。值类型为数组时,分别表示菜单展开和折叠的宽度。[ 展开时的宽度, 折叠时的宽度 ],示例:['200px', '80px']。TS 类型:`string \| number \| Array` | N +onChange | Function | | TS 类型:`(evt: CustomEvent) => void`
激活菜单项发生变化时触发 | N + ### MenuItem Props + 名称 | 类型 | 默认值 | 说明 | 必传 -- | -- | -- | -- | -- +disabled | Boolean | - | 是否禁用菜单项展开/收起/跳转等功能 | N +href | String | - | 跳转链接 | N +icon | TElement | - | 图标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N +target | String | - | 链接或路由跳转方式。可选项:_blank/_self/_parent/_top | N +value | String / Number | - | 菜单项唯一标识。TS 类型:`MenuValue` | N +onClick | Function | | TS 类型:`(evt: CustomEvent<{ e: MouseEvent, value: MenuValue }>) => void`
点击时触发 | N diff --git a/src/menu/_example/base.tsx b/src/menu/_example/base.tsx deleted file mode 100644 index ae47d70..0000000 --- a/src/menu/_example/base.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import 'tdesign-web-components/icon'; -import 'tdesign-web-components/menu'; - -export default function Menu() { - return ( -
- - LOGO - }> - -
- ); -} diff --git a/src/menu/_example/closable-side.tsx b/src/menu/_example/closable-side.tsx new file mode 100644 index 0000000..7060ba2 --- /dev/null +++ b/src/menu/_example/closable-side.tsx @@ -0,0 +1,40 @@ +import 'tdesign-web-components/button'; +import 'tdesign-web-components/icon'; +import 'tdesign-web-components/menu'; + +import { Component, signal } from 'omi'; + +export default class CloseableSide extends Component { + collapsed = signal(false); + + active = signal('0'); + + render() { + return ( + { + this.active.value = evt.detail; + }} + > + LOGO + } /> + } /> + } /> + } /> + } /> + } /> + } + onClick={() => { + this.collapsed.value = !this.collapsed.value; + }} + /> + + ); + } +} diff --git a/src/menu/type.ts b/src/menu/type.ts index 2427663..d84574b 100644 --- a/src/menu/type.ts +++ b/src/menu/type.ts @@ -6,11 +6,6 @@ export interface TdMenuProps { * @default false */ collapsed?: boolean; - /** - * 同级别互斥展开 - * @default false - */ - expandMutex?: boolean; /** * 激活菜单项 */ @@ -23,11 +18,7 @@ export interface TdMenuProps { /** * 激活菜单项发生变化时触发 */ - onChange?: (value: MenuValue) => void; - /** - * 展开的菜单项发生变化时触发 - */ - onExpand?: (value: Array) => void; + onChange?: (value: CustomEvent) => void; } export interface TdMenuItemProps { @@ -59,7 +50,7 @@ export interface TdMenuItemProps { /** * 点击时触发 */ - onClick?: (context: { e: HTMLElement; value: MenuValue }) => void; + onClick?: (event: CustomEvent<{ e: HTMLElement; value: MenuValue }>) => void; } export type MenuValue = string | number;