Skip to content

Commit b193cf0

Browse files
committed
feat(ui:dropdown): add dropdown-group
1 parent 2c9135f commit b193cf0

14 files changed

Lines changed: 207 additions & 42 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import type { DDropdownItemProps } from './DropdownItem';
2+
3+
import React, { useCallback, useMemo } from 'react';
4+
5+
import { usePrefixConfig, useComponentConfig, useCustomContext, useTranslation } from '../../hooks';
6+
import { getClassName, mergeStyle, toId } from '../../utils';
7+
import { DDropdownContext } from './Dropdown';
8+
9+
export interface DDropdownGroupProps extends React.LiHTMLAttributes<HTMLLIElement> {
10+
dId: string;
11+
dTitle: React.ReactNode;
12+
__level?: number;
13+
}
14+
15+
export function DDropdownGroup(props: DDropdownGroupProps) {
16+
const {
17+
dId,
18+
dTitle,
19+
__level = 0,
20+
id,
21+
className,
22+
style,
23+
tabIndex = -1,
24+
children,
25+
onClick,
26+
onFocus,
27+
onBlur,
28+
...restProps
29+
} = useComponentConfig(DDropdownGroup.name, props);
30+
31+
//#region Context
32+
const dPrefix = usePrefixConfig();
33+
const [{ onFocus: _onFocus, onBlur: _onBlur }] = useCustomContext(DDropdownContext);
34+
//#endregion
35+
36+
const [t] = useTranslation('Common');
37+
38+
const _id = id ?? `${dPrefix}dropdown-group-${toId(dId)}`;
39+
40+
const handleFocus = useCallback(
41+
(e) => {
42+
onFocus?.(e);
43+
_onFocus?.(dId, _id);
44+
},
45+
[_id, _onFocus, dId, onFocus]
46+
);
47+
48+
const handleBlur = useCallback(
49+
(e) => {
50+
onBlur?.(e);
51+
_onBlur?.();
52+
},
53+
[_onBlur, onBlur]
54+
);
55+
56+
const childs = useMemo(() => {
57+
return React.Children.map(children as Array<React.ReactElement<DDropdownItemProps>>, (child) =>
58+
React.cloneElement(child, {
59+
...child.props,
60+
__level: __level + 1,
61+
})
62+
);
63+
}, [children, __level]);
64+
65+
return (
66+
<>
67+
<li
68+
{...restProps}
69+
id={_id}
70+
style={mergeStyle(style, {
71+
paddingLeft: 12 + __level * 16,
72+
})}
73+
className={getClassName(className, `${dPrefix}dropdown-group`)}
74+
tabIndex={tabIndex}
75+
role="separator"
76+
aria-orientation="horizontal"
77+
onFocus={handleFocus}
78+
onBlur={handleBlur}
79+
>
80+
{dTitle}
81+
</li>
82+
{React.Children.count(childs) === 0 ? (
83+
<span
84+
className={`${dPrefix}dropdown-group__empty`}
85+
style={{
86+
paddingLeft: style?.paddingLeft,
87+
}}
88+
>
89+
{t('No Data')}
90+
</span>
91+
) : (
92+
childs
93+
)}
94+
</>
95+
);
96+
}

packages/ui/src/components/dropdown/DropdownItem.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@ import { isUndefined } from 'lodash';
22
import { useCallback } from 'react';
33

44
import { usePrefixConfig, useComponentConfig, useCustomContext } from '../../hooks';
5-
import { getClassName, toId } from '../../utils';
5+
import { getClassName, mergeStyle, toId } from '../../utils';
66
import { DDropdownContext } from './Dropdown';
77

88
export interface DDropdownItemProps extends React.LiHTMLAttributes<HTMLLIElement> {
99
dId: string;
1010
dIcon?: React.ReactNode;
1111
dDisabled?: boolean;
12+
__level?: number;
1213
}
1314

1415
export function DDropdownItem(props: DDropdownItemProps) {
1516
const {
1617
dId,
1718
dIcon,
1819
dDisabled = false,
20+
__level = 0,
1921
id,
2022
className,
23+
style,
2124
tabIndex,
2225
children,
2326
onClick,
@@ -66,6 +69,9 @@ export function DDropdownItem(props: DDropdownItemProps) {
6669
className={getClassName(className, `${dPrefix}dropdown-item`, {
6770
'is-disabled': dDisabled,
6871
})}
72+
style={mergeStyle(style, {
73+
paddingLeft: 12 + __level * 16,
74+
})}
6975
role="menuitem"
7076
tabIndex={isUndefined(tabIndex) ? -1 : tabIndex}
7177
aria-disabled={dDisabled}

packages/ui/src/components/dropdown/DropdownSub.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { isUndefined } from 'lodash';
22
import React, { useCallback, useEffect, useMemo } from 'react';
33

44
import { usePrefixConfig, useComponentConfig, useCustomContext, useImmer, useRefCallback, useTranslation } from '../../hooks';
5-
import { getClassName, getHorizontalSideStyle, toId } from '../../utils';
5+
import { getClassName, getHorizontalSideStyle, mergeStyle, toId } from '../../utils';
66
import { DPopup } from '../_popup';
77
import { DIcon } from '../icon';
88
import { DDropdownContext } from './Dropdown';
@@ -17,6 +17,7 @@ export interface DDropdownSubProps extends React.LiHTMLAttributes<HTMLLIElement>
1717
dIcon?: React.ReactNode;
1818
dTitle: React.ReactNode;
1919
dDisabled?: boolean;
20+
__level?: number;
2021
}
2122

2223
export function DDropdownSub(props: DDropdownSubProps) {
@@ -25,6 +26,7 @@ export function DDropdownSub(props: DDropdownSubProps) {
2526
dIcon,
2627
dTitle,
2728
dDisabled = false,
29+
__level = 0,
2830
id,
2931
className,
3032
style,
@@ -137,6 +139,9 @@ export function DDropdownSub(props: DDropdownSubProps) {
137139
className={getClassName(className, `${dPrefix}dropdown-sub`, {
138140
'is-disabled': dDisabled,
139141
})}
142+
style={mergeStyle(style, {
143+
paddingLeft: 12 + __level * 16,
144+
})}
140145
role="menuitem"
141146
tabIndex={isUndefined(tabIndex) ? -1 : tabIndex}
142147
aria-haspopup={true}
@@ -165,7 +170,18 @@ export function DDropdownSub(props: DDropdownSubProps) {
165170
aria-orientation="vertical"
166171
aria-activedescendant={activedescendant}
167172
>
168-
{React.Children.count(children) === 0 ? <span className={`${dPrefix}dropdown-sub__empty`}>{t('No Data')}</span> : children}
173+
{React.Children.count(children) === 0 ? (
174+
<span
175+
className={`${dPrefix}dropdown-sub__empty`}
176+
style={{
177+
paddingLeft: 12 + __level * 16,
178+
}}
179+
>
180+
{t('No Data')}
181+
</span>
182+
) : (
183+
children
184+
)}
169185
</ul>
170186
}
171187
dTrigger={dropdownPopupTrigger}

packages/ui/src/components/dropdown/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ Extend `React.HTMLAttributes<HTMLElement>`.
3030
| onItemClick | Click the callback of the menu item | `(id: string) => void` | - |
3131
<!-- prettier-ignore-end -->
3232

33+
### DDropdownGroupProps
34+
35+
Extend `React.LiHTMLAttributes<HTMLLIElement>`.
36+
37+
<!-- prettier-ignore-start -->
38+
| Property | Description | Type | Default |
39+
| --- | --- | --- | --- |
40+
| dId | Uniquely identifies | string | - |
41+
| dTitle | The title of the menu group | React.ReactNode | - |
42+
<!-- prettier-ignore-end -->
43+
3344
### DDropdownSubProps
3445

3546
Extend `React.LiHTMLAttributes<HTMLLIElement>`.

packages/ui/src/components/dropdown/README.zh-Hant.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ title: 下拉菜单
2929
| onItemClick | 点击菜单项的回调 | `(id: string) => void` | - |
3030
<!-- prettier-ignore-end -->
3131

32+
### DDropdownGroupProps
33+
34+
继承 `React.LiHTMLAttributes<HTMLLIElement>`
35+
36+
<!-- prettier-ignore-start -->
37+
| 参数 | 说明 | 类型 | 默认值 |
38+
| --- | --- | --- | --- |
39+
| dId | 唯一标识 | string | - |
40+
| dTitle | 菜单分组的标题 | React.ReactNode | - |
41+
<!-- prettier-ignore-end -->
42+
3243
### DDropdownSubProps
3344

3445
继承 `React.LiHTMLAttributes<HTMLLIElement>`

packages/ui/src/components/dropdown/demos/1.Basic.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Automatically adjust the position beyond the `window` view.
1717
超出 `window` 视图自动调整位置。
1818

1919
```tsx
20-
import { DDropdown, DDropdownItem, DButton, DIcon } from '@react-devui/ui';
20+
import { DDropdown, DDropdownItem, DDropdownGroup, DButton, DIcon } from '@react-devui/ui';
2121

2222
export default function Demo() {
2323
const icon1 = <DIcon dName="arrow-down"></DIcon>;
@@ -50,10 +50,12 @@ export default function Demo() {
5050
dTrigger="click"
5151
>
5252
<DDropdownItem dId="Item1">Item 1</DDropdownItem>
53-
<DDropdownItem dId="Item2" dDisabled>
54-
Item 2
55-
</DDropdownItem>
56-
<DDropdownItem dId="Item3">Item 3</DDropdownItem>
53+
<DDropdownItem dId="Item2">Item 2</DDropdownItem>
54+
<DDropdownGroup dId="Group3" dTitle="Group 3">
55+
<DDropdownItem dId="Item31">Item 31</DDropdownItem>
56+
<DDropdownItem dId="Item32">Item 32</DDropdownItem>
57+
</DDropdownGroup>
58+
<DDropdownItem dId="Item4">Item 4</DDropdownItem>
5759
</DDropdown>
5860
</>
5961
);

packages/ui/src/components/dropdown/demos/4.MultiLevel.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default function Demo() {
3535
</DDropdownItem>
3636
</DDropdownSub>
3737
<DDropdownSub dId="Sub4" dTitle="Sub 3" dDisabled>
38-
<DDropdownItem dId="Item31">Item 41</DDropdownItem>
38+
<DDropdownItem dId="Item41">Item 41</DDropdownItem>
3939
</DDropdownSub>
4040
</DDropdown>
4141
);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './Dropdown';
22
export * from './DropdownItem';
33
export * from './DropdownSub';
4+
export * from './DropdownCroup';

packages/ui/src/components/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ export { DDrag, DDrop, DDragPlaceholder } from './drag-drop';
1313
export type { DDrawerProps, DDrawerHeaderProps, DDrawerFooterProps } from './drawer';
1414
export { DDrawer, DDrawerHeader, DDrawerFooter } from './drawer';
1515

16-
export type { DDropdownProps, DDropdownItemProps, DDropdownSubProps } from './dropdown';
17-
export { DDropdown, DDropdownItem, DDropdownSub } from './dropdown';
16+
export type { DDropdownProps, DDropdownItemProps, DDropdownSubProps, DDropdownGroupProps } from './dropdown';
17+
export { DDropdown, DDropdownItem, DDropdownSub, DDropdownGroup } from './dropdown';
1818

1919
export type { DRowProps, DColProps } from './grid';
2020
export { DRow, DCol } from './grid';

packages/ui/src/components/menu/MenuGroup.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { usePrefixConfig, useComponentConfig, useCustomContext, useTranslation }
77
import { getClassName, toId, mergeStyle } from '../../utils';
88
import { DMenuContext } from './Menu';
99

10-
export interface DMenuGroupProps extends React.HTMLAttributes<HTMLDivElement> {
10+
export interface DMenuGroupProps extends React.LiHTMLAttributes<HTMLLIElement> {
1111
dId: string;
1212
dTitle: React.ReactNode;
1313
__level?: number;
@@ -70,7 +70,7 @@ export function DMenuGroup(props: DMenuGroupProps) {
7070

7171
return (
7272
<>
73-
<div
73+
<li
7474
{...restProps}
7575
id={_id}
7676
className={getClassName(className, `${dPrefix}menu-group`)}
@@ -84,7 +84,7 @@ export function DMenuGroup(props: DMenuGroupProps) {
8484
onBlur={handleBlur}
8585
>
8686
{dTitle}
87-
</div>
87+
</li>
8888
{React.Children.count(childs) === 0 ? (
8989
<span className={`${dPrefix}menu-group__empty`} style={{ paddingLeft: 16 + (__level + 1) * 20 }}>
9090
{t('No Data')}

0 commit comments

Comments
 (0)