Skip to content

Commit e82e897

Browse files
committed
feat(Button): 按钮组件新增按钮组模式
1 parent 429bda7 commit e82e897

File tree

9 files changed

+286
-61
lines changed

9 files changed

+286
-61
lines changed

src/components/button/README.md

+27-11
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,36 @@ import { Button } from "aunt";
6262
通过 color 属性可以自定义按钮的颜色。
6363
<code src="./demos/demo-color.tsx"></code>
6464

65+
### 按钮组
66+
67+
通过 Button.Group 包裹可以实现按钮组的概念。
68+
<code src="./demos/demo-group.tsx"></code>
69+
6570
## 参数
6671

67-
| 参数 | 说明 | 类型 | 默认值 |
68-
| -------- | ----------------------------------------- | ------------------------------------------------------ | --------- |
69-
| type | 统一设置按钮类型 | `'default'\|'primary'\|'success'\|'warning'\|'danger'` | `default` |
70-
| size | 统一设置按钮尺寸 | `'large'\|'normal'\|'small'\|'mini'` | `normal` |
71-
| block | 是否是块级元素 | `boolean` | `false` |
72+
| 参数 | 说明 | 类型 | 默认值 |
73+
| -------- | ------ | ------- | --- |
74+
| type | 统一设置按钮类型 | `'default'\|'primary'\|'success'\|'warning'\|'danger'` | `default` |
75+
| size | 统一设置按钮尺寸 | `'large'\|'normal'\|'small'\|'mini'` | `normal` |
76+
| block | 是否是块级元素 | `boolean` | `false` |
7277
| color | 按钮颜色,支持传入 linear-gradient 渐变色 | `string` | `-` |
73-
| disabled | 是否禁用 | `boolean` | `false` |
74-
| shape | 按钮的形状 | `'default' \| 'square' \| 'round'` | `default` |
75-
| plain | 是否为朴素按钮 | `boolean` | `false` |
76-
| hairline | 是否使用 0.5px 边框 | `boolean` | `false` |
77-
78-
## 事件
78+
| disabled | 是否禁用 | `boolean` | `false` |
79+
| shape | 按钮的形状 | `'default' \| 'square' \| 'round'` | `default` |
80+
| plain | 是否为朴素按钮 | `boolean` | `false` |
81+
| hairline | 是否使用 0.5px 边框 | `boolean` | `false` |
82+
83+
84+
### Button.Group
85+
| 参数 | 说明 | 类型 | 默认值 |
86+
| --- | --- | --- | --- |
87+
| type | 统一设置按钮类型 | `string` | `default` |
88+
| size | 统一设置按钮尺寸 | `string` | `normal` |
89+
| iconPosition | 统一设置按钮图标展示位置 | `string` | `left` |
90+
| block | 统一设置按钮为块级元素 | `boolean` | `false` |
91+
| plain | 是否为朴素按钮组 | `boolean` | `false` |
92+
| disabled | 是否禁用按钮组 | `boolean` | `false` |
93+
94+
### 事件
7995

8096
| 事件名 | 说明 | 类型 | 默认值 |
8197
| ------- | -------------- | ----------------------------------------------------------------------------------- | ------ |
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react';
2+
import { ButtonGroupProps } from './types';
3+
4+
type ButtonContextType = {
5+
parent?: ButtonGroupProps;
6+
};
7+
8+
const ButtonContext = React.createContext<ButtonContextType>({});
9+
10+
export default ButtonContext;
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React, { useContext } from 'react';
2+
import ConfigProviderContext from '../config-provider/config-provider-context';
3+
import ButtonContext from './button-context';
4+
import { useNamespace } from '../../hooks';
5+
import { joinTrim } from '../../utils';
6+
import { ButtonGroupProps } from './types';
7+
8+
const ButtonGroup = (props: ButtonGroupProps) => {
9+
const { shape = 'default', type = 'default' } = props;
10+
11+
const internalClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
12+
if (props.disabled) return;
13+
props.onClick?.(e);
14+
};
15+
16+
const { prefix } = useContext(ConfigProviderContext);
17+
const ns = useNamespace('button', prefix);
18+
19+
return (
20+
<div
21+
className={joinTrim([
22+
props.className,
23+
ns.e('group'),
24+
ns.em('group', type),
25+
ns.em('group__shape', shape),
26+
props.disabled ? ns.em('group', 'disabled') : '',
27+
])}
28+
style={props.style}
29+
onClick={internalClick}
30+
>
31+
<ButtonContext.Provider
32+
value={{
33+
parent: {
34+
...props,
35+
shape: 'square',
36+
},
37+
}}
38+
>
39+
{props.children}
40+
</ButtonContext.Provider>
41+
</div>
42+
);
43+
};
44+
45+
export default ButtonGroup;

src/components/button/button.tsx

+51-16
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,82 @@ import React, { CSSProperties, FunctionComponent, useContext, useMemo } from 're
22
import { ButtonProps } from './types';
33
import Loading from '../loading';
44
import ConfigProviderContext from '../config-provider/config-provider-context';
5+
import ButtonContext from './button-context';
56
import { useNamespace } from '../../hooks';
67
import { joinTrim } from '../../utils';
78

89
export const Button: FunctionComponent<Partial<ButtonProps>> = props => {
910
const {
1011
color,
1112
shape = 'default',
12-
plain = false,
13+
iconPosition = 'left',
1314
hairline = false,
1415
loading = false,
15-
disabled = false,
16-
type = 'default',
17-
size = 'normal',
18-
block = false,
1916
children,
20-
iconPosition = 'left',
2117
icon,
2218
className,
2319
style,
2420
loadingText,
21+
block,
22+
plain,
2523
...rest
2624
} = props;
2725

26+
const { parent } = useContext(ButtonContext);
27+
28+
const currentSize = useMemo(() => props.size || parent?.size || 'normal', [
29+
props.size,
30+
parent?.size,
31+
]);
32+
33+
const currentType = useMemo(() => props.type || parent?.type || 'default', [
34+
props.type,
35+
parent?.type,
36+
]);
37+
38+
const currentPlain = useMemo(() => !!plain || !!parent?.plain, [plain, parent?.plain]);
39+
40+
const currentBlock = useMemo(() => !!block || !!parent?.block, [parent?.block, block]);
41+
42+
const currentIconPosition = useMemo(() => parent?.iconPosition || iconPosition, [
43+
parent?.iconPosition,
44+
iconPosition,
45+
]);
46+
47+
const currentDisabled = React.useMemo(() => props.disabled ?? parent?.disabled, [
48+
parent?.disabled,
49+
props.disabled,
50+
]);
51+
2852
const { prefix } = useContext(ConfigProviderContext);
2953
const ns = useNamespace('button', prefix);
3054

3155
const varClasses = useMemo(() => {
3256
return joinTrim([
3357
ns.b(),
34-
type ? ns.m(type) : '',
35-
size ? ns.m(size) : '',
58+
ns.m(currentType),
59+
ns.m(currentSize),
3660
shape ? ns.em('shape', shape) : '',
37-
plain ? ns.m('plain') : '',
38-
block ? ns.m('block') : '',
39-
disabled ? ns.m('disabled') : '',
61+
currentPlain ? ns.m('plain') : '',
62+
currentBlock ? ns.m('block') : '',
63+
currentDisabled ? ns.m('disabled') : '',
4064
hairline ? ns.m('hairline') : '',
4165
icon ? ns.e('icon') : '',
4266
loading ? ns.e('loading') : '',
4367
className,
4468
]);
45-
}, [type, size, shape, plain, block, disabled, hairline, icon, loading, className]);
69+
}, [
70+
currentBlock,
71+
currentPlain,
72+
currentDisabled,
73+
currentType,
74+
currentSize,
75+
shape,
76+
hairline,
77+
icon,
78+
loading,
79+
className,
80+
]);
4681

4782
const varStyles = useMemo(() => {
4883
const styles: CSSProperties = {};
@@ -59,7 +94,7 @@ export const Button: FunctionComponent<Partial<ButtonProps>> = props => {
5994
}, [plain, color, style]);
6095

6196
const handleClick = (event: any) => {
62-
if (!loading && !disabled && props.onClick) {
97+
if (!loading && !currentDisabled && props.onClick) {
6398
props.onClick(event);
6499
}
65100
};
@@ -85,7 +120,7 @@ export const Button: FunctionComponent<Partial<ButtonProps>> = props => {
85120
<Loading
86121
size={loadingSize}
87122
type={loadingType}
88-
color={type === 'default' ? undefined : ''}
123+
color={currentType === 'default' ? undefined : ''}
89124
className={ns.em('icon', position)}
90125
/>
91126
);
@@ -108,9 +143,9 @@ export const Button: FunctionComponent<Partial<ButtonProps>> = props => {
108143

109144
return (
110145
<div className={varClasses} style={{ ...varStyles }} {...rest} onClick={handleClick}>
111-
{iconPosition === 'left' && renderIcon('left')}
146+
{currentIconPosition === 'left' && renderIcon('left')}
112147
{renderText()}
113-
{iconPosition === 'right' && renderIcon('right')}
148+
{currentIconPosition === 'right' && renderIcon('right')}
114149
</div>
115150
);
116151
};
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react';
2+
import { Button, Space, AuntIconArrowLeft, AuntIconArrowRight, AuntIconRefreshCcw } from 'aunt';
3+
4+
export default () => (
5+
<Space direction='vertical'>
6+
<Button.Group plain type='primary' shape='round' disabled>
7+
<Button>按钮1</Button>
8+
<Button>按钮2</Button>
9+
<Button>按钮3</Button>
10+
</Button.Group>
11+
<Button.Group shape='round'>
12+
<Button type='primary'>按钮1</Button>
13+
<Button type='primary'>按钮2</Button>
14+
<Button type='primary'>按钮3</Button>
15+
</Button.Group>
16+
<Button.Group type='success' shape='round'>
17+
<Button type='success'>按钮1</Button>
18+
<Button type='default'>按钮2</Button>
19+
<Button type='success'>按钮3</Button>
20+
</Button.Group>
21+
<Button.Group shape='round'>
22+
<Button icon={<AuntIconArrowLeft size={18} />}>上一步</Button>
23+
<Button icon={<AuntIconRefreshCcw size={18} />}>刷新</Button>
24+
<Button icon={<AuntIconArrowRight size={18} />} iconPosition='right'>
25+
下一步
26+
</Button>
27+
</Button.Group>
28+
</Space>
29+
);

src/components/button/demos/demo.tsx

+37-31
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,58 @@ import DemoShape from './demo-shape';
1010
import DemoSize from './demo-size';
1111
import DemoButtonBlock from './demo-block';
1212
import DemoColor from './demo-color';
13+
import DemoGroup from './demo-group';
1314

1415
import './index.less';
1516

1617
function Demo() {
1718
return (
18-
<div className='demo-button'>
19-
<DemoBlock title='按钮类型'>
20-
<DemoType />
21-
</DemoBlock>
19+
<>
20+
<div className='demo-button'>
21+
<DemoBlock title='按钮类型'>
22+
<DemoType />
23+
</DemoBlock>
2224

23-
<DemoBlock title='按钮类型'>
24-
<DemoPlain />
25-
</DemoBlock>
25+
<DemoBlock title='按钮类型'>
26+
<DemoPlain />
27+
</DemoBlock>
2628

27-
<DemoBlock title='细边按钮'>
28-
<DemoHairline />
29-
</DemoBlock>
29+
<DemoBlock title='细边按钮'>
30+
<DemoHairline />
31+
</DemoBlock>
3032

31-
<DemoBlock title='图标按钮'>
32-
<DemoIcon />
33-
</DemoBlock>
33+
<DemoBlock title='图标按钮'>
34+
<DemoIcon />
35+
</DemoBlock>
3436

35-
<DemoBlock title='禁用状态'>
36-
<DemoDisabled />
37-
</DemoBlock>
37+
<DemoBlock title='禁用状态'>
38+
<DemoDisabled />
39+
</DemoBlock>
3840

39-
<DemoBlock title='加载状态'>
40-
<DemoLoading />
41-
</DemoBlock>
41+
<DemoBlock title='加载状态'>
42+
<DemoLoading />
43+
</DemoBlock>
4244

43-
<DemoBlock title='按钮形状'>
44-
<DemoShape />
45-
</DemoBlock>
45+
<DemoBlock title='按钮形状'>
46+
<DemoShape />
47+
</DemoBlock>
4648

47-
<DemoBlock title='按钮尺寸'>
48-
<DemoSize />
49-
</DemoBlock>
49+
<DemoBlock title='按钮尺寸'>
50+
<DemoSize />
51+
</DemoBlock>
5052

51-
<DemoBlock title='块级按钮'>
52-
<DemoButtonBlock />
53-
</DemoBlock>
53+
<DemoBlock title='块级按钮'>
54+
<DemoButtonBlock />
55+
</DemoBlock>
5456

55-
<DemoBlock title='按钮颜色'>
56-
<DemoColor />
57+
<DemoBlock title='按钮颜色'>
58+
<DemoColor />
59+
</DemoBlock>
60+
</div>
61+
<DemoBlock title='按钮组'>
62+
<DemoGroup />
5763
</DemoBlock>
58-
</div>
64+
</>
5965
);
6066
}
6167

src/components/button/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import './styles/index.less';
2-
import { Button } from './button';
2+
import { Button as _Button } from './button';
3+
import ButtonGroup from './button-group';
34

45
export type { ButtonProps, ButtonType, ButtonSize, ButtonShape } from './types';
56

7+
const Button = Object.assign(_Button, {
8+
Group: ButtonGroup,
9+
});
10+
611
export { Button };
712
export default Button;

0 commit comments

Comments
 (0)