Skip to content

Commit

Permalink
feat(dropdown): dropdown complete 🎉 (#106)
Browse files Browse the repository at this point in the history
* feat(dropdown): dropdown complete 🎉

dropdown complete 🎉

re #65

* feat(dropdown): dropdown complete 🎉

dropdown complete 🎉

re #65
  • Loading branch information
duenyang authored Jul 19, 2024
1 parent 4316734 commit 346782f
Show file tree
Hide file tree
Showing 30 changed files with 1,386 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,6 @@ module.exports = {
'no-script-url': 'warn',
'no-param-reassign': ['error', { props: true, ignorePropertyModificationsForRegex: ['^html'] }],
'no-underscore-dangle': ['error', { allow: ['__filename', '__dirname', '__TDESIGN_THEME_PREFIX__'] }],
'import/no-duplicates': 'off',
},
};
7 changes: 7 additions & 0 deletions site/sidebar.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ export default [
path: '/components/affix',
component: () => import('tdesign-web-components/affix/README.md'),
},
{
title: 'Dropdown 下拉菜单',
titleEn: 'Dropdown',
name: 'dropdown',
path: '/components/dropdown',
component: () => import('tdesign-web-components/dropdown/README.md'),
},
{
title: 'Menu 导航菜单',
name: 'menu',
Expand Down
1 change: 1 addition & 0 deletions src/_util/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export function hasClass(el: Element, cls: string) {

// 判断一个元素是否包含另一个元素
export function domContains(parent: HTMLElement, child: HTMLElement) {
if (!parent || !child) return false;
if (parent.contains(child)) {
return true;
}
Expand Down
74 changes: 74 additions & 0 deletions src/_util/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import camelCase from 'lodash/camelCase';

export function omit(obj: object, fields: string[]): object {
const shallowCopy = {
...obj,
};
for (let i = 0; i < fields.length; i++) {
const key = fields[i];
delete shallowCopy[key];
}
return shallowCopy;
}

export function removeEmptyAttrs<T>(obj: T): Partial<T> {
const newObj = {};

Object.keys(obj).forEach((key) => {
if (typeof obj[key] !== 'undefined' || obj[key] === null) {
newObj[key] = obj[key];
}
});

return newObj;
}

// eslint-disable-next-line default-param-last
export function getTabElementByValue(tabs = [], value: string): object {
const [result] = tabs.filter((item) => {
const { id } = item as any;
return id === value;
});
return result || null;
}

export function firstUpperCase(str: string): string {
return str.toLowerCase().replace(/( |^)[a-z]/g, (char: string) => char.toUpperCase());
}

export type Gradients = { [percent: string]: string };
export type FromTo = { from: string; to: string };
export type LinearGradient = { direction?: string } & (Gradients | FromTo);
export function getBackgroundColor(color: string | string[] | LinearGradient): string {
if (typeof color === 'string') {
return color;
}
if (Array.isArray(color)) {
if (color[0] && color[0][0] === '#') {
color.unshift('90deg');
}
return `linear-gradient( ${color.join(',')} )`;
}
const { from, to, direction = 'to right', ...rest } = color;
let keys = Object.keys(rest);
if (keys.length) {
keys = keys.sort((a, b) => parseFloat(a.substr(0, a.length - 1)) - parseFloat(b.substr(0, b.length - 1)));
const tempArr = keys.map((key: any) => `${rest[key]} ${key}`);
return `linear-gradient(${direction}, ${tempArr.join(',')})`;
}
return `linear-gradient(${direction}, ${from}, ${to})`;
}

// keyboard-event => onKeyboardEvent
export function getPropsApiByEvent(eventName: string) {
return camelCase(`on-${eventName}`);
}

/**
* 兼容样式中支持 number/string 类型的传值 得出最后的结果。
* @param param number 或 string 类型的可用于样式上的值
* @returns 可使用的样式值。
*/
export function pxCompat(param: string | number) {
return typeof param === 'number' ? `${param}px` : param;
}
3 changes: 3 additions & 0 deletions src/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Component, tag } from 'omi';
import classname, { getClassPrefix } from '../_util/classname';
import eventDispose from '../_util/eventDispose';
import { convertToLightDomNode } from '../_util/lightDom';
import parseTNode from '../_util/parseTNode';
import { StyledProps } from '../common';
import { TdButtonProps } from './type';

Expand Down Expand Up @@ -81,6 +82,7 @@ export default class Button extends Component<ButtonProps> {
shape,
ignoreAttributes,
children,
suffix,
...rest
} = props;

Expand Down Expand Up @@ -122,6 +124,7 @@ export default class Button extends Component<ButtonProps> {
>
{iconNode ? iconNode : null}
<span className={`${classPrefix}-button__text`}>{children}</span>
{suffix && <span className={`${classPrefix}-button__suffix`}>{parseTNode(suffix)}</span>}
</Tag>
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/button/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export interface TdButtonProps {
* 按钮内部图标,可完全自定义
*/
icon?: TNode;
/**
* 按钮后置图标,可完全自定义
*/
suffix?: TNode;
/**
* 是否显示为加载状态
* @default false
Expand Down
109 changes: 109 additions & 0 deletions src/dropdown/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
title: Dropdown 下拉菜单
description: 用于承载过多的操作集合,通过下拉拓展的形式,收纳更多的操作。
isComponent: true
usage: { title: "", description: "" }
spline: navigation
---

### 文字下拉菜单

文字按钮触发下拉菜单。常用于空间极度受限的收纳操作场景,一般用于表格内详情操作的收纳。

{{ base }}

### 按钮下拉菜单

普通按钮触发下拉菜单。常用于操作收纳场景。

{{ button }}

### 带分割线下拉菜单

通过分割线区分不同的下拉菜单操作。常用于需要区分不同操作结果的场景。

{{ split }}

### 多层下拉菜单

带逻辑层级关系的操作下拉菜单。常用于需要收纳多层逻辑的操作场景。

{{ multiple }}

### 带禁用操作项的下拉菜单

下拉菜单的操作项可以设置禁用。常用于禁用部分操作项的场景。

{{ disabled }}

### 自定义其他属性的下拉菜单

下拉菜单依赖 `Popup` 组件,可以透传自定义 `Popup` 组件的属性。

{{ custom }}

### 定义最大高度的下拉菜单

下拉菜单支持定义最大高度。

{{ long }}

### 自定义主题的下拉菜单

下拉菜单的菜单项都支持自定义主题,根据具体场景使用。

{{ theme }}

### 向左展开的下拉菜单

下拉菜单支持向左侧展开。

{{ left }}

### 带图标的下拉菜单

下拉菜单支持配置菜单项的前置图标。

{{ icon }}

### 通过传入Children方式使用下拉菜单

下拉菜单操作项支持通过传入Children的方式传递操作项。

{{ child }}

## API
### Dropdown Props

名称 | 类型 | 默认值 | 说明 | 必传
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
direction | String | right | 多层级操作时,子层级展开方向。可选项:left/right | N
disabled | Boolean | false | 是否禁用组件 | N
hideAfterItemClick | Boolean | true | 点击选项后是否自动隐藏弹窗 | N
maxColumnWidth | String / Number | 100 | 选项最大宽度,内容超出时,显示为省略号。值为字符串时,值就是最大宽度;值为数字时,单位:px | N
maxHeight | Number | 300 | 弹窗最大高度,单位:px 。统一控制每一列的高度 | N
minColumnWidth | String / Number | 10 | 选项最小宽度。值为字符串时,值就是最小宽度;值为数字时,单位:px | N
options | Array | [] | 下拉操作项。TS 类型:`Array<DropdownOption>` `type DropdownOption = { children?: Array<TdDropdownItemProps> } & TdDropdownItemProps & Record<string, any>`[详细类型定义](https://github.com/TDesignOteam/tdesign-web-components/tree/main/src/dropdown/type.ts) | N
panelBottomContent | TNode | - | 面板内的底部内容。TS 类型:`string \| TNode`[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/tree/main/src/common.ts) | N
panelTopContent | TNode | - | 面板内的顶部内容。TS 类型:`string \| TNode`[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/tree/main/src/common.ts) | N
placement | String | bottom-left | 弹窗定位方式,可选值同 Popup 组件。可选项:top/left/right/bottom/top-left/top-right/bottom-left/bottom-right/left-top/left-bottom/right-top/right-bottom | N
popupProps | Object | - | 透传 Popup 组件属性,方便更加自由地控制。比如使用 popupProps.overlayStyle 设置浮层样式。TS 类型:`PopupProps`[Popup API Documents](./popup?tab=api)[详细类型定义](https://github.com/TDesignOteam/tdesign-web-components/tree/main/src/dropdown/type.ts) | N
trigger | String | hover | 触发下拉显示的方式。可选项:hover/click/focus/context-menu | N
onClick | Function | | TS 类型:`(dropdownItem: DropdownOption, context: { e: MouseEvent }) => void`<br/>下拉操作项点击时触发 | N

### DropdownItem Props

名称 | 类型 | 默认值 | 说明 | 必传
-- | -- | -- | -- | --
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
active | Boolean | false | 是否高亮当前操作项 | N
content | TNode | '' | 下拉操作项内容。TS 类型:`string \| TNode`[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/tree/main/src/common.ts) | N
disabled | Boolean | false | 是否禁用操作项 | N
divider | Boolean | false | 是否显示操作项之间的分隔线(分隔线默认在下方) | N
prefixIcon | TElement | - | 组件前置图标。TS 类型:`TNode`[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/tree/main/src/common.ts) | N
theme | String | default | 下拉菜单选项主题。可选项:default/success/warning/error。TS 类型:`DropdownItemTheme` `type DropdownItemTheme = 'default' \| 'success' \| 'warning' \| 'error'`[详细类型定义](https://github.com/TDesignOteam/tdesign-web-components/tree/main/src/dropdown-menu/type.ts) | N
value | String / Number / Object | - | 下拉操作项唯一标识。TS 类型:`string \| number \| { [key: string]: any }` | N
onClick | Function | | TS 类型:`(dropdownItem: DropdownOption, context: { e: MouseEvent }) => void`<br/>点击时触发 | N
37 changes: 37 additions & 0 deletions src/dropdown/_example/base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'tdesign-web-components/button';
import 'tdesign-icons-web-components';
import 'tdesign-web-components/dropdown';

import { DropdownProps } from 'tdesign-web-components/dropdown';

export default function BasicDropdown() {
const getContent = () => <div>操作四</div>;
const options = [
{
content: '操作一',
value: 1,
},
{
content: '操作二',
value: 2,
},
{
content: '操作三',
value: 3,
},
{
content: getContent(),
value: 4,
},
];
const clickHandler: DropdownProps['onClick'] = (data) => {
console.log(`选中【${data.value}】`);
};
return (
<t-dropdown options={options} onClick={clickHandler}>
<t-button variant="text" suffix={<t-icon name="chevron-down" size="16" />}>
更多
</t-button>
</t-dropdown>
);
}
35 changes: 35 additions & 0 deletions src/dropdown/_example/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'tdesign-icons-web-components';
import 'tdesign-web-components/dropdown';
import 'tdesign-web-components/button';

export default function BasicDropdown() {
const getContent = () => <div>操作四</div>;
const options = [
{
content: '操作一',
value: 1,
},
{
content: '操作二',
value: 2,
},
{
content: '操作三',
value: 3,
},
{
content: getContent(),
value: 4,
},
];
const clickHandler = (data) => {
console.log(`选中【${data.value}】`);
};
return (
<t-dropdown options={options} onClick={clickHandler}>
<t-button theme="default" variant="outline" shape="square">
<t-icon name="ellipsis" size="16" />
</t-button>
</t-dropdown>
);
}
51 changes: 51 additions & 0 deletions src/dropdown/_example/child.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'tdesign-web-components/button';
import 'tdesign-web-components/dropdown';
import 'tdesign-icons-web-components';

import { DropdownProps } from 'tdesign-web-components';

import { MessagePlugin } from '../../message';

export default function BasicDropdown() {
const clickHandler: DropdownProps['onClick'] = (data) => {
MessagePlugin.success(`选中【${data.value}】`);
};
return (
<t-dropdown minColumnWidth={100} trigger="click">
<t-button variant="text" suffix={<t-icon name="chevron-down" size="16" />}>
更多
</t-button>
<t-dropdown-menu>
<t-dropdown-item value={1}>
操作一
<t-dropdown-menu>
<t-dropdown-item value={11}>操作1-1</t-dropdown-item>
<t-dropdown-item value={12}>
操作1-2
<t-dropdown-menu>
<t-dropdown-item value={111}>操作1-1-1</t-dropdown-item>
<t-dropdown-item value={112}>操作1-1-2</t-dropdown-item>
</t-dropdown-menu>
</t-dropdown-item>
</t-dropdown-menu>
</t-dropdown-item>
<t-dropdown-item value={2}>
操作二
<t-dropdown-menu>
<t-dropdown-item value={21}>
操作2-1
<t-dropdown-menu>
<t-dropdown-item value={211}>操作2-1-1</t-dropdown-item>
<t-dropdown-item value={211}>操作2-1-2</t-dropdown-item>
</t-dropdown-menu>
</t-dropdown-item>
<t-dropdown-item value={22} onClick={clickHandler}>
操作2-2
</t-dropdown-item>
</t-dropdown-menu>
</t-dropdown-item>
<t-dropdown-item value={3}>操作三</t-dropdown-item>
</t-dropdown-menu>
</t-dropdown>
);
}
Loading

0 comments on commit 346782f

Please sign in to comment.