Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 11 additions & 18 deletions src/action-sheet/ActionSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const ActionSheet: React.FC<ActionSheetProps> = (props) => {
onCancel?.(ev);
};

const handleSelected = (idx) => {
const handleSelected = (idx: number) => {
const found = items?.[idx];

onSelected?.(found, idx);
Expand All @@ -53,29 +53,22 @@ export const ActionSheet: React.FC<ActionSheetProps> = (props) => {
<Popup
{...popupProps}
visible={visible}
className={actionSheetClass}
className={cx({
[`${actionSheetClass}`]: true,
[`${actionSheetClass}--${theme}`]: true,
[`${actionSheetClass}--${align}`]: true,
[`${actionSheetClass}--no-description`]: !description,
})}
placement="bottom"
onVisibleChange={(value) => {
setVisible(value, { trigger: 'overlay' });
}}
showOverlay={showOverlay}
>
<div className={cx(`${actionSheetClass}__content`)}>
{description ? (
<p
className={cx({
[`${actionSheetClass}__description`]: true,
[`${actionSheetClass}__description--left`]: align === 'left',
[`${actionSheetClass}__description--grid`]: theme === 'grid',
})}
>
{description}
</p>
) : null}
{theme === 'list' ? <ActionSheetList items={items} align={align} onSelected={handleSelected} /> : null}
{theme === 'grid' ? (
<ActionSheetGrid items={items} align={align} onSelected={handleSelected} count={count} />
) : null}
<div className={`${actionSheetClass}__content`}>
{description ? <p className={`${actionSheetClass}__description`}>{description}</p> : null}
{theme === 'list' ? <ActionSheetList items={items} onSelected={handleSelected} /> : null}
{theme === 'grid' ? <ActionSheetGrid items={items} onSelected={handleSelected} count={count} /> : null}
{showCancel ? (
<div className={`${actionSheetClass}__footer`}>
<div className={`${actionSheetClass}__gap-${theme}`}></div>
Expand Down
5 changes: 3 additions & 2 deletions src/action-sheet/ActionSheetGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Grid, GridItem } from '../grid';
import { Swiper, SwiperProps } from '../swiper';
import { usePrefixClass } from '../hooks/useClass';

type ActionSheetGridProps = Pick<ActionSheetProps, 'items' | 'align'> & {
type ActionSheetGridProps = Pick<ActionSheetProps, 'items'> & {
onSelected?: (idx: number) => void;
count?: number;
};
Expand Down Expand Up @@ -55,7 +55,7 @@ export function ActionSheetGrid(props: ActionSheetGridProps) {
{actionItems.map((item, idx1) => (
<Swiper.SwiperItem key={idx1}>
<Grid gutter={0} column={gridColumn} style={{ width: '100%' }}>
{item.map((it, idx2) => {
{item.map((it: ActionSheetItem, idx2: number) => {
let label: string;
let image: TNode;
let badge: ActionSheetItem['badge'];
Expand All @@ -71,6 +71,7 @@ export function ActionSheetGrid(props: ActionSheetGridProps) {
key={`${idx1}-${idx2}`}
image={image}
text={label}
description={it.description}
badge={badge}
// @ts-ignore
onClick={() => {
Expand Down
43 changes: 33 additions & 10 deletions src/action-sheet/ActionSheetList.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import React from 'react';
import cx from 'classnames';

import type { TElement } from '../common';
import type { TNode } from '../common';
import type { ActionSheetProps } from './ActionSheet';
import type { ActionSheetItem } from './type';

import { Button } from '../button';
import { Badge } from '../badge';
import { usePrefixClass } from '../hooks/useClass';

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

export function ActionSheetList(props: ActionSheetListProps) {
const { items = [], align, onSelected } = props;
const { items = [], onSelected } = props;

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

const renderTNode = (node: TNode) => {
if (!node) return null;
if (typeof node === 'function') {
return node();
}
return node;
};

return (
<div className={cx(`${actionSheetClass}__list`)}>
{items?.map((item, idx) => {
{items?.map((item: ActionSheetItem, idx: number) => {
let label: React.ReactNode;
let disabled: ActionSheetItem['disabled'];
let icon: ActionSheetItem['icon'];
let color: ActionSheetItem['color'];

if (typeof item === 'string') {
Expand All @@ -37,7 +44,7 @@ export function ActionSheetList(props: ActionSheetListProps) {
dot={item?.badge?.dot}
content={item?.badge?.content}
size={item?.badge?.size}
offset={item?.badge?.offset || [-16, 20]}
offset={item?.badge?.offset}
>
<span className={cx([`${actionSheetClass}__list-item-text`])}>{item?.label}</span>
</Badge>
Expand All @@ -46,30 +53,46 @@ export function ActionSheetList(props: ActionSheetListProps) {
label = <span className={cx([`${actionSheetClass}__list-item-text`])}>{item?.label}</span>;
}
disabled = item?.disabled;
icon = item?.icon;
color = item?.color;
}

const prefixIcon = renderTNode(item?.icon);
const suffixIcon = renderTNode(item?.suffixIcon);
const desc = item?.description;
const content = (
<>
<div className={`${actionSheetClass}__list-item-content`}>
{prefixIcon && <div className={`${actionSheetClass}__list-item-icon`}>{prefixIcon}</div>}
{label}
{suffixIcon && (
<div className={`${actionSheetClass}__list-item-icon ${actionSheetClass}__list-item-suffix-icon`}>
{suffixIcon}
</div>
)}
</div>
{desc && <div className={`${actionSheetClass}__list-item-desc`}>{desc}</div>}
</>
);

return (
<Button
key={idx}
variant="text"
block
className={cx({
[`${actionSheetClass}__list-item`]: true,
[`${actionSheetClass}__list-item--left`]: align === 'left',
[`${actionSheetClass}__list-item--disabled`]: disabled,
})}
onClick={() => {
onSelected?.(idx);
}}
disabled={disabled}
icon={icon as TElement}
style={{
color,
}}
shape="rectangle"
>
{label}
{content}
</Button>
);
})}
Expand Down
7 changes: 0 additions & 7 deletions src/action-sheet/__tests__/ActionSheetList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,6 @@ describe('ActionSheetList', () => {
expect(queryByTestId('mock-icon')).toBeInTheDocument();
});

it(':align - should apply left alignment', () => {
const items = ['选项一'];
const { container } = render(<ActionSheetList items={items} align="left" onSelected={mockOnSelected} />);

expect(container.querySelector('.t-action-sheet__list-item--left')).toBeInTheDocument();
});

it('should handle empty items array', () => {
const { container } = render(<ActionSheetList items={[]} onSelected={mockOnSelected} />);

Expand Down
10 changes: 3 additions & 7 deletions src/action-sheet/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,12 @@ describe('ActionSheet', () => {
expect(container.textContent).toContain('Test description');
});

it(':description - should apply left alignment class', () => {
const { container } = render(<ActionSheet {...defaultProps} description="Test" align="left" />);
const description = container.querySelector('.t-action-sheet__description--left');
expect(description).toBeInTheDocument();
});

it(':description - should apply grid theme class', () => {
const { container } = render(<ActionSheet {...defaultProps} description="Test" theme="grid" />);
const description = container.querySelector('.t-action-sheet__description--grid');
const description = container.querySelector('.t-action-sheet__description');
const grid = container.querySelector('.t-action-sheet--grid');
expect(description).toBeInTheDocument();
expect(grid).toBeInTheDocument();
});

it(':align - should handle align prop', () => {
Expand Down
30 changes: 25 additions & 5 deletions src/action-sheet/_example/align.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
import React, { useState } from 'react';
import { Button, ActionSheet } from 'tdesign-mobile-react';
import { EnterIcon, BookmarkIcon, PinIcon, CloudUploadIcon } from 'tdesign-icons-react';

export default function ListExample() {
const [alignCenterVisible, setAlignCenterVisible] = useState(false);
const [alignLeftVisible, setAlignLeftVisible] = useState(false);

const items = [
{
label: 'Move',
icon: () => <EnterIcon />,
},
{
label: 'Mark as important',
icon: <BookmarkIcon />,
},
{
label: 'Unsubscribe',
icon: <PinIcon />,
},
{
label: 'Add to Tasks',
icon: <CloudUploadIcon />,
},
];
return (
<div className="action-sheet-demo">
<div className="action-sheet-demo-btns">
Expand All @@ -18,8 +36,9 @@ export default function ListExample() {
<ActionSheet
align="center"
visible={alignCenterVisible}
description="动作面板描述文字"
items={['选项一', '选项二', '选项三', '选项四']}
description="Email Settings"
cancelText="cancel"
items={items}
onClose={() => {
setAlignCenterVisible(false);
}}
Expand All @@ -30,8 +49,9 @@ export default function ListExample() {
<ActionSheet
align="left"
visible={alignLeftVisible}
description="动作面板描述文字"
items={['选项一', '选项二', '选项三', '选项四']}
description="Email Settings"
cancelText="cancel"
items={items}
onClose={() => {
setAlignLeftVisible(false);
}}
Expand Down
49 changes: 26 additions & 23 deletions src/action-sheet/_example/list.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Button, ActionSheet } from 'tdesign-mobile-react';
import { AppIcon } from 'tdesign-icons-react';
import { EnterIcon, PinIcon, BookmarkIcon, CloudUploadIcon } from 'tdesign-icons-react';

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

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

<ActionSheet
visible={normalVisible}
items={['选项一', '选项二', '选项三', '选项四']}
cancelText="cancel"
items={['Move', 'Mark as important', 'Unsubscribe', 'Add to Tasks']}
onClose={() => {
setNormalVisible(false);
}}
Expand All @@ -55,8 +56,9 @@ export default function ListExample() {
/>
<ActionSheet
visible={descVisible}
description="动作面板描述文字"
items={['选项一', '选项二', '选项三', '选项四']}
description="Email Settings"
cancelText="cancel"
items={['Move', 'Mark as important', 'Unsubscribe', 'Add to Tasks']}
onClose={() => {
setDescVisible(false);
}}
Expand All @@ -66,23 +68,23 @@ export default function ListExample() {
/>
<ActionSheet
visible={iconVisible}
description="动作面板描述文字"
cancelText="cancel"
items={[
{
label: '选项一',
icon: <AppIcon size={24} />,
label: 'Move',
icon: <EnterIcon />,
},
{
label: '选项二',
icon: <AppIcon size={24} />,
label: 'Mark as important',
icon: <BookmarkIcon />,
},
{
label: '选项三',
icon: <AppIcon size={24} />,
label: 'Unsubscribe',
icon: <PinIcon />,
},
{
label: '选项四',
icon: <AppIcon size={24} />,
label: 'Add to Tasks',
icon: <CloudUploadIcon />,
},
]}
onClose={() => {
Expand All @@ -94,23 +96,24 @@ export default function ListExample() {
/>
<ActionSheet
visible={badgeVisible}
description="动作面板描述文字"
description="Email Settings"
cancelText="cancel"
items={[
{
label: '选项一',
badge: { count: 1 },
label: 'Move',
badge: { dot: true },
},
{
label: '选项二',
badge: { dot: true },
label: 'Mark as important',
badge: { count: 8, offset: [-6, 2] },
},
{
label: '选项三',
badge: { dot: true },
label: 'Unsubscribe',
badge: { count: 99, offset: [-6, 2] },
},
{
label: '选项四',
badge: { dot: true },
label: 'Add to Tasks',
badge: { count: 1000, offset: [-10, 2] },
},
]}
onClose={() => {
Expand Down
Loading
Loading