-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from ming680/feature/menu
Feature/menu
- Loading branch information
Showing
13 changed files
with
394 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,8 @@ | |
"actived", | ||
"borderless", | ||
"Cascader", | ||
"classname", | ||
"clsx", | ||
"Popconfirm", | ||
"Swiper", | ||
"tdesign" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { ComponentChildren } from 'omi'; | ||
|
||
/** | ||
* 将Component的children转换为数组 | ||
* @param children ComponentChildren | undefined | ||
* @returns ComponentChildren[] | ||
*/ | ||
export function getChildrenArray(children?: ComponentChildren) { | ||
if (!children) { | ||
return []; | ||
} | ||
if (Array.isArray(children)) { | ||
return children; | ||
} | ||
return [children]; | ||
} | ||
|
||
/** | ||
* 判断是否某个name的slot | ||
* @param name string | ||
* @param children ComponentChildren | undefined | ||
* @returns boolean | ||
*/ | ||
export function hasSlot(name: string, children?: ComponentChildren) { | ||
const childrenArray = getChildrenArray(children); | ||
|
||
return childrenArray.some((child) => { | ||
if (typeof child === 'object' && 'attributes' in child) { | ||
return child.attributes?.slot === name; | ||
} | ||
return false; | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { bind, Component, OmiDOMAttributes, signal, tag } from 'omi'; | ||
|
||
import classname, { getClassPrefix } from '../_util/classname'; | ||
import { getChildrenArray, hasSlot } from '../_util/component'; | ||
import { convertToLightDomNode } from '../_util/lightDom'; | ||
import { StyledProps } from '../common'; | ||
import { DEFAULT_MENU_WIDTH } from './_util/constant'; | ||
import { MenuValue, TdMenuProps } from './type'; | ||
|
||
export interface MenuProps extends TdMenuProps, StyledProps, OmiDOMAttributes {} | ||
|
||
@tag('t-menu') | ||
export default class Menu extends Component<MenuProps> { | ||
static css = []; | ||
|
||
static defaultProps: TdMenuProps = { | ||
collapsed: false, | ||
width: '232px', | ||
}; | ||
|
||
static propTypes = { | ||
collapsed: Boolean, | ||
value: [String, Number], | ||
width: [String, Number, Array], | ||
onChange: Function, | ||
}; | ||
|
||
private active = signal<MenuValue>(''); | ||
|
||
// 这里不能声明 collapsed 会被外部覆盖 | ||
private menuCollapsed = signal(true); | ||
|
||
provide = { | ||
collapsed: this.menuCollapsed, | ||
active: this.active, | ||
onChange: this.handleChange, | ||
}; | ||
|
||
@bind | ||
handleChange(value: MenuValue) { | ||
this.fire('change', value); | ||
} | ||
|
||
render() { | ||
const { className, style, width, value, collapsed } = this.props; | ||
|
||
this.active.value = value; | ||
this.menuCollapsed.value = collapsed; | ||
|
||
const classPrefix = getClassPrefix(); | ||
const menuWidthArr = Array.isArray(width) ? width : [width, DEFAULT_MENU_WIDTH[1]]; | ||
|
||
const hasLogo = hasSlot('logo', this.props.children); | ||
const hasOperations = hasSlot('operations', this.props.children); | ||
|
||
const children = getChildrenArray(this.props.children) | ||
.filter((item) => item.nodeName === 't-menu-item') | ||
.map(convertToLightDomNode); | ||
|
||
return ( | ||
<div | ||
className={classname(`${classPrefix}-default-menu`, className, { | ||
[`${classPrefix}-is-collapsed`]: collapsed, | ||
})} | ||
style={{ width: collapsed ? menuWidthArr[1] : menuWidthArr[0], ...style }} | ||
> | ||
<div className={`${classPrefix}-default-menu__inner`}> | ||
{hasLogo && ( | ||
<div className={`${classPrefix}-menu__logo`}> | ||
<span> | ||
<slot name="logo" /> | ||
</span> | ||
</div> | ||
)} | ||
|
||
<ul className={classname(`${classPrefix}-menu`, `${classPrefix}-menu--scroll`)}>{children}</ul> | ||
|
||
{hasOperations && ( | ||
<div className={`${classPrefix}-menu__operations`}> | ||
<slot name="operations" /> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import 'tdesign-web-components/tooltip'; | ||
|
||
import { bind, Component, tag } from 'omi'; | ||
|
||
import classname, { getClassPrefix } from '../_util/classname'; | ||
import { convertToLightDomNode } from '../_util/lightDom'; | ||
import { StyledProps } from '../common'; | ||
import { TdMenuItemProps } from './type'; | ||
|
||
export interface MenuItemProps extends TdMenuItemProps, StyledProps {} | ||
|
||
@tag('t-menu-item') | ||
export default class MenuItem extends Component<MenuItemProps> { | ||
static css = ` | ||
.${getClassPrefix()}-menu__item--has-icon .${getClassPrefix()}-menu__content, | ||
.${getClassPrefix()}-menu__item--has-icon .${getClassPrefix()}-menu__item-link { | ||
margin-left: var(--td-comp-margin-s); | ||
} | ||
.${getClassPrefix()}-menu__item > ${getClassPrefix()}-tooltip { | ||
position: absolute; | ||
inset: 0; | ||
} | ||
.${getClassPrefix()}-menu__item .${getClassPrefix()}-menu__item-tooltip-inner { | ||
width: 100%; | ||
height: 100%; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
`; | ||
|
||
static propTypes = { | ||
label: Object, | ||
disabled: Boolean, | ||
href: String, | ||
icon: Object, | ||
target: String, | ||
value: [String, Number], | ||
onClick: Function, | ||
}; | ||
|
||
inject = ['active', 'onChange', 'collapsed']; | ||
|
||
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, 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 === this.injection.active.value, | ||
[`${classPrefix}-menu__item--plain`]: !icon, | ||
[`${classPrefix}-menu__item--has-icon`]: !!lightIcon, | ||
}); | ||
|
||
const content = ( | ||
<> | ||
{lightIcon} | ||
{href ? ( | ||
<a href={href} target={target} className={classname(`${classPrefix}-menu__item-link`)}> | ||
<span className={`${classPrefix}-menu__content`}>{label}</span> | ||
</a> | ||
) : ( | ||
<span className={`${classPrefix}-menu__content`}>{label}</span> | ||
)} | ||
</> | ||
); | ||
|
||
if (this.injection.collapsed.value && !this.props.disabled) { | ||
return ( | ||
<t-tooltip content={label} placement="right"> | ||
<div className={`${classPrefix}-menu__item-tooltip-inner`}>{content}</div> | ||
</t-tooltip> | ||
); | ||
} | ||
|
||
return content; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
--- | ||
title: Menu 导航菜单 | ||
description: 用于承载网站的架构,并提供跳转的菜单列表。 | ||
isComponent: true | ||
usage: { title: '', description: '' } | ||
spline: base | ||
--- | ||
|
||
### 可收起的侧边导航 | ||
|
||
在侧边导航上提供收起按钮,点击后可以将侧边栏最小化,常见于带有图标的侧边导航。 | ||
|
||
{{ closable-side }} | ||
|
||
## API | ||
|
||
### Menu Props | ||
|
||
名称 | 类型 | 默认值 | 说明 | 必传 | ||
-- | -- | -- | -- | -- | ||
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<string \| number>` | N | ||
onChange | Function | | TS 类型:`(evt: CustomEvent<MenuValue>) => void`<br/>激活菜单项发生变化时触发 | 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`<br/>点击时触发 | N |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<t-menu | ||
value={this.active.value} | ||
collapsed={this.collapsed.value} | ||
onChange={(evt) => { | ||
this.active.value = evt.detail; | ||
}} | ||
> | ||
<span slot="logo">LOGO</span> | ||
<t-menu-item label="仪表盘" value="0" icon={<t-icon name="app" />} /> | ||
<t-menu-item label="资源列表" value="1" icon={<t-icon name="code" />} /> | ||
<t-menu-item label="调度平台" value="2" icon={<t-icon name="file" />} /> | ||
<t-menu-item label="精准监控" value="3" icon={<t-icon name="user" />} /> | ||
<t-menu-item label="根目录" value="4" icon={<t-icon name="rollback" />} /> | ||
<t-menu-item label="消息区" value="5" icon={<t-icon name="mail" />} /> | ||
<t-button | ||
slot="operations" | ||
variant="text" | ||
shape="square" | ||
icon={<t-icon name="view-list" />} | ||
onClick={() => { | ||
this.collapsed.value = !this.collapsed.value; | ||
}} | ||
/> | ||
</t-menu> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const DEFAULT_MENU_WIDTH = [232, 64]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import './style/index.js'; | ||
|
||
import _Menu from './Menu'; | ||
import _MenuItem from './MenuItem'; | ||
|
||
export type { MenuProps } from './Menu'; | ||
export type { MenuItemProps } from './MenuItem'; | ||
export * from './type'; | ||
|
||
export const Menu = _Menu; | ||
export const MenuItem = _MenuItem; | ||
|
||
export default Menu; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { css, globalCSS } from 'omi'; | ||
|
||
// 为了做主题切换 | ||
import styles from '../../_common/style/web/components/menu/_index.less'; | ||
|
||
export const styleSheet = css` | ||
${styles} | ||
`; | ||
|
||
globalCSS(styleSheet); |
Oops, something went wrong.