Skip to content

Commit cd57b86

Browse files
feat(ActionSheet): item attribute enhancement (#845)
* feat(ActionSheet): item attribute enhancement * feat(ActionSheet): update demo * chore: remove unused code * fix: fix cr * chore: update demo icon --------- Co-authored-by: lwj <[email protected]>
1 parent c021fab commit cd57b86

File tree

14 files changed

+1611
-1399
lines changed

14 files changed

+1611
-1399
lines changed

src/action-sheet/ActionSheet.tsx

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const ActionSheet: React.FC<ActionSheetProps> = (props) => {
4141
onCancel?.(ev);
4242
};
4343

44-
const handleSelected = (idx) => {
44+
const handleSelected = (idx: number) => {
4545
const found = items?.[idx];
4646

4747
onSelected?.(found, idx);
@@ -53,29 +53,22 @@ export const ActionSheet: React.FC<ActionSheetProps> = (props) => {
5353
<Popup
5454
{...popupProps}
5555
visible={visible}
56-
className={actionSheetClass}
56+
className={cx({
57+
[`${actionSheetClass}`]: true,
58+
[`${actionSheetClass}--${theme}`]: true,
59+
[`${actionSheetClass}--${align}`]: true,
60+
[`${actionSheetClass}--no-description`]: !description,
61+
})}
5762
placement="bottom"
5863
onVisibleChange={(value) => {
5964
setVisible(value, { trigger: 'overlay' });
6065
}}
6166
showOverlay={showOverlay}
6267
>
63-
<div className={cx(`${actionSheetClass}__content`)}>
64-
{description ? (
65-
<p
66-
className={cx({
67-
[`${actionSheetClass}__description`]: true,
68-
[`${actionSheetClass}__description--left`]: align === 'left',
69-
[`${actionSheetClass}__description--grid`]: theme === 'grid',
70-
})}
71-
>
72-
{description}
73-
</p>
74-
) : null}
75-
{theme === 'list' ? <ActionSheetList items={items} align={align} onSelected={handleSelected} /> : null}
76-
{theme === 'grid' ? (
77-
<ActionSheetGrid items={items} align={align} onSelected={handleSelected} count={count} />
78-
) : null}
68+
<div className={`${actionSheetClass}__content`}>
69+
{description ? <p className={`${actionSheetClass}__description`}>{description}</p> : null}
70+
{theme === 'list' ? <ActionSheetList items={items} onSelected={handleSelected} /> : null}
71+
{theme === 'grid' ? <ActionSheetGrid items={items} onSelected={handleSelected} count={count} /> : null}
7972
{showCancel ? (
8073
<div className={`${actionSheetClass}__footer`}>
8174
<div className={`${actionSheetClass}__gap-${theme}`}></div>

src/action-sheet/ActionSheetGrid.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Grid, GridItem } from '../grid';
88
import { Swiper, SwiperProps } from '../swiper';
99
import { usePrefixClass } from '../hooks/useClass';
1010

11-
type ActionSheetGridProps = Pick<ActionSheetProps, 'items' | 'align'> & {
11+
type ActionSheetGridProps = Pick<ActionSheetProps, 'items'> & {
1212
onSelected?: (idx: number) => void;
1313
count?: number;
1414
};
@@ -55,7 +55,7 @@ export function ActionSheetGrid(props: ActionSheetGridProps) {
5555
{actionItems.map((item, idx1) => (
5656
<Swiper.SwiperItem key={idx1}>
5757
<Grid gutter={0} column={gridColumn} style={{ width: '100%' }}>
58-
{item.map((it, idx2) => {
58+
{item.map((it: ActionSheetItem, idx2: number) => {
5959
let label: string;
6060
let image: TNode;
6161
let badge: ActionSheetItem['badge'];
@@ -71,6 +71,7 @@ export function ActionSheetGrid(props: ActionSheetGridProps) {
7171
key={`${idx1}-${idx2}`}
7272
image={image}
7373
text={label}
74+
description={it.description}
7475
badge={badge}
7576
// @ts-ignore
7677
onClick={() => {

src/action-sheet/ActionSheetList.tsx

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,36 @@
11
import React from 'react';
22
import cx from 'classnames';
33

4-
import type { TElement } from '../common';
4+
import type { TNode } from '../common';
55
import type { ActionSheetProps } from './ActionSheet';
66
import type { ActionSheetItem } from './type';
77

88
import { Button } from '../button';
99
import { Badge } from '../badge';
1010
import { usePrefixClass } from '../hooks/useClass';
1111

12-
type ActionSheetListProps = Pick<ActionSheetProps, 'items' | 'align'> & {
12+
type ActionSheetListProps = Pick<ActionSheetProps, 'items'> & {
1313
onSelected?: (idx: number) => void;
1414
};
1515

1616
export function ActionSheetList(props: ActionSheetListProps) {
17-
const { items = [], align, onSelected } = props;
17+
const { items = [], onSelected } = props;
1818

1919
const actionSheetClass = usePrefixClass('action-sheet');
2020

21+
const renderTNode = (node: TNode) => {
22+
if (!node) return null;
23+
if (typeof node === 'function') {
24+
return node();
25+
}
26+
return node;
27+
};
28+
2129
return (
2230
<div className={cx(`${actionSheetClass}__list`)}>
23-
{items?.map((item, idx) => {
31+
{items?.map((item: ActionSheetItem, idx: number) => {
2432
let label: React.ReactNode;
2533
let disabled: ActionSheetItem['disabled'];
26-
let icon: ActionSheetItem['icon'];
2734
let color: ActionSheetItem['color'];
2835

2936
if (typeof item === 'string') {
@@ -37,7 +44,7 @@ export function ActionSheetList(props: ActionSheetListProps) {
3744
dot={item?.badge?.dot}
3845
content={item?.badge?.content}
3946
size={item?.badge?.size}
40-
offset={item?.badge?.offset || [-16, 20]}
47+
offset={item?.badge?.offset}
4148
>
4249
<span className={cx([`${actionSheetClass}__list-item-text`])}>{item?.label}</span>
4350
</Badge>
@@ -46,30 +53,46 @@ export function ActionSheetList(props: ActionSheetListProps) {
4653
label = <span className={cx([`${actionSheetClass}__list-item-text`])}>{item?.label}</span>;
4754
}
4855
disabled = item?.disabled;
49-
icon = item?.icon;
5056
color = item?.color;
5157
}
5258

59+
const prefixIcon = renderTNode(item?.icon);
60+
const suffixIcon = renderTNode(item?.suffixIcon);
61+
const desc = item?.description;
62+
const content = (
63+
<>
64+
<div className={`${actionSheetClass}__list-item-content`}>
65+
{prefixIcon && <div className={`${actionSheetClass}__list-item-icon`}>{prefixIcon}</div>}
66+
{label}
67+
{suffixIcon && (
68+
<div className={`${actionSheetClass}__list-item-icon ${actionSheetClass}__list-item-suffix-icon`}>
69+
{suffixIcon}
70+
</div>
71+
)}
72+
</div>
73+
{desc && <div className={`${actionSheetClass}__list-item-desc`}>{desc}</div>}
74+
</>
75+
);
76+
5377
return (
5478
<Button
5579
key={idx}
5680
variant="text"
5781
block
5882
className={cx({
5983
[`${actionSheetClass}__list-item`]: true,
60-
[`${actionSheetClass}__list-item--left`]: align === 'left',
84+
[`${actionSheetClass}__list-item--disabled`]: disabled,
6185
})}
6286
onClick={() => {
6387
onSelected?.(idx);
6488
}}
6589
disabled={disabled}
66-
icon={icon as TElement}
6790
style={{
6891
color,
6992
}}
7093
shape="rectangle"
7194
>
72-
{label}
95+
{content}
7396
</Button>
7497
);
7598
})}

src/action-sheet/__tests__/ActionSheetList.test.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,6 @@ describe('ActionSheetList', () => {
5656
expect(queryByTestId('mock-icon')).toBeInTheDocument();
5757
});
5858

59-
it(':align - should apply left alignment', () => {
60-
const items = ['选项一'];
61-
const { container } = render(<ActionSheetList items={items} align="left" onSelected={mockOnSelected} />);
62-
63-
expect(container.querySelector('.t-action-sheet__list-item--left')).toBeInTheDocument();
64-
});
65-
6659
it('should handle empty items array', () => {
6760
const { container } = render(<ActionSheetList items={[]} onSelected={mockOnSelected} />);
6861

src/action-sheet/__tests__/index.test.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,12 @@ describe('ActionSheet', () => {
126126
expect(container.textContent).toContain('Test description');
127127
});
128128

129-
it(':description - should apply left alignment class', () => {
130-
const { container } = render(<ActionSheet {...defaultProps} description="Test" align="left" />);
131-
const description = container.querySelector('.t-action-sheet__description--left');
132-
expect(description).toBeInTheDocument();
133-
});
134-
135129
it(':description - should apply grid theme class', () => {
136130
const { container } = render(<ActionSheet {...defaultProps} description="Test" theme="grid" />);
137-
const description = container.querySelector('.t-action-sheet__description--grid');
131+
const description = container.querySelector('.t-action-sheet__description');
132+
const grid = container.querySelector('.t-action-sheet--grid');
138133
expect(description).toBeInTheDocument();
134+
expect(grid).toBeInTheDocument();
139135
});
140136

141137
it(':align - should handle align prop', () => {

src/action-sheet/_example/align.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
11
import React, { useState } from 'react';
22
import { Button, ActionSheet } from 'tdesign-mobile-react';
3+
import { EnterIcon, BookmarkIcon, PinIcon, CloudUploadIcon } from 'tdesign-icons-react';
34

45
export default function ListExample() {
56
const [alignCenterVisible, setAlignCenterVisible] = useState(false);
67
const [alignLeftVisible, setAlignLeftVisible] = useState(false);
7-
8+
const items = [
9+
{
10+
label: 'Move',
11+
icon: () => <EnterIcon />,
12+
},
13+
{
14+
label: 'Mark as important',
15+
icon: <BookmarkIcon />,
16+
},
17+
{
18+
label: 'Unsubscribe',
19+
icon: <PinIcon />,
20+
},
21+
{
22+
label: 'Add to Tasks',
23+
icon: <CloudUploadIcon />,
24+
},
25+
];
826
return (
927
<div className="action-sheet-demo">
1028
<div className="action-sheet-demo-btns">
@@ -18,8 +36,9 @@ export default function ListExample() {
1836
<ActionSheet
1937
align="center"
2038
visible={alignCenterVisible}
21-
description="动作面板描述文字"
22-
items={['选项一', '选项二', '选项三', '选项四']}
39+
description="Email Settings"
40+
cancelText="cancel"
41+
items={items}
2342
onClose={() => {
2443
setAlignCenterVisible(false);
2544
}}
@@ -30,8 +49,9 @@ export default function ListExample() {
3049
<ActionSheet
3150
align="left"
3251
visible={alignLeftVisible}
33-
description="动作面板描述文字"
34-
items={['选项一', '选项二', '选项三', '选项四']}
52+
description="Email Settings"
53+
cancelText="cancel"
54+
items={items}
3555
onClose={() => {
3656
setAlignLeftVisible(false);
3757
}}

src/action-sheet/_example/list.tsx

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState } from 'react';
22
import { Button, ActionSheet } from 'tdesign-mobile-react';
3-
import { AppIcon } from 'tdesign-icons-react';
3+
import { EnterIcon, PinIcon, BookmarkIcon, CloudUploadIcon } from 'tdesign-icons-react';
44

55
export default function ListExample() {
66
const [normalVisible, setNormalVisible] = useState(false);
@@ -10,7 +10,7 @@ export default function ListExample() {
1010

1111
const openByMethod = () => {
1212
ActionSheet.show({
13-
items: ['选项一', '选项二', '选项三', '选项四'],
13+
items: ['Move', 'Mark as important', 'Unsubscribe', 'Add to Tasks'],
1414
onClose() {
1515
ActionSheet.close();
1616
},
@@ -45,7 +45,8 @@ export default function ListExample() {
4545

4646
<ActionSheet
4747
visible={normalVisible}
48-
items={['选项一', '选项二', '选项三', '选项四']}
48+
cancelText="cancel"
49+
items={['Move', 'Mark as important', 'Unsubscribe', 'Add to Tasks']}
4950
onClose={() => {
5051
setNormalVisible(false);
5152
}}
@@ -55,8 +56,9 @@ export default function ListExample() {
5556
/>
5657
<ActionSheet
5758
visible={descVisible}
58-
description="动作面板描述文字"
59-
items={['选项一', '选项二', '选项三', '选项四']}
59+
description="Email Settings"
60+
cancelText="cancel"
61+
items={['Move', 'Mark as important', 'Unsubscribe', 'Add to Tasks']}
6062
onClose={() => {
6163
setDescVisible(false);
6264
}}
@@ -66,23 +68,23 @@ export default function ListExample() {
6668
/>
6769
<ActionSheet
6870
visible={iconVisible}
69-
description="动作面板描述文字"
71+
cancelText="cancel"
7072
items={[
7173
{
72-
label: '选项一',
73-
icon: <AppIcon size={24} />,
74+
label: 'Move',
75+
icon: <EnterIcon />,
7476
},
7577
{
76-
label: '选项二',
77-
icon: <AppIcon size={24} />,
78+
label: 'Mark as important',
79+
icon: <BookmarkIcon />,
7880
},
7981
{
80-
label: '选项三',
81-
icon: <AppIcon size={24} />,
82+
label: 'Unsubscribe',
83+
icon: <PinIcon />,
8284
},
8385
{
84-
label: '选项四',
85-
icon: <AppIcon size={24} />,
86+
label: 'Add to Tasks',
87+
icon: <CloudUploadIcon />,
8688
},
8789
]}
8890
onClose={() => {
@@ -94,23 +96,24 @@ export default function ListExample() {
9496
/>
9597
<ActionSheet
9698
visible={badgeVisible}
97-
description="动作面板描述文字"
99+
description="Email Settings"
100+
cancelText="cancel"
98101
items={[
99102
{
100-
label: '选项一',
101-
badge: { count: 1 },
103+
label: 'Move',
104+
badge: { dot: true },
102105
},
103106
{
104-
label: '选项二',
105-
badge: { dot: true },
107+
label: 'Mark as important',
108+
badge: { count: 8, offset: [-6, 2] },
106109
},
107110
{
108-
label: '选项三',
109-
badge: { dot: true },
111+
label: 'Unsubscribe',
112+
badge: { count: 99, offset: [-6, 2] },
110113
},
111114
{
112-
label: '选项四',
113-
badge: { dot: true },
115+
label: 'Add to Tasks',
116+
badge: { count: 1000, offset: [-10, 2] },
114117
},
115118
]}
116119
onClose={() => {

0 commit comments

Comments
 (0)