Skip to content

Commit 4f8f434

Browse files
author
liyuan-meng
committed
✨[Notification]新增消息通知组件
1 parent 2603d13 commit 4f8f434

11 files changed

Lines changed: 348 additions & 158 deletions

File tree

src/components/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ export { default as Message } from './message';
7575

7676
export { default as Modal } from './modal';
7777

78+
export { default as Notification } from './notification';
79+
7880
export { default as PageHeader } from './pageHeader';
7981

8082
export { default as Pagination } from './pagination';

src/components/notification/constant.js

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/components/notification/demos/basic.md

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,18 @@ desc: 抽屉组件基础用法
55
---
66

77
```jsx
8-
import React from 'react';
9-
import { Notification, Button, Message } from 'cloud-react';
10-
11-
const placementList = [
12-
{ label: '左上', value: 'top-left' },
13-
{ label: '右上', value: 'top-right' },
14-
{ label: '左下', value: 'bottom-left' },
15-
{ label: '右下', value: 'bottom-right' },
16-
];
8+
import React, { useState } from 'react';
9+
import { Notification, Button } from 'cloud-react';
1710

1811
export default function NotificationDemo() {
1912
const open = () => {
2013
Notification.open({
2114
title: '这里是标题',
22-
content: '消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息',
23-
showIcon: true,
24-
showCancelBtn: true,
25-
showConfirmBtn: true,
26-
// showDetailBtn: true,
27-
// duration: 3000,
28-
onConfirm: id => {
29-
Message.info('点击确定关闭了!')
30-
Notification.close(id);
31-
},
15+
content: '消息通知内容消息通知内容消息',
3216
});
3317
}
3418
return (
35-
<Button onClick={open}>打开消息通知</Button>
19+
<Button onClick={open}>打开消息</Button>
3620
);
3721
}
3822
```
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
order: 1
3+
title: Drawer
4+
desc: 抽屉组件基础用法
5+
---
6+
7+
```jsx
8+
import React, { useState } from 'react';
9+
import { Notification, Button, Message, Icon, Radio, Checkbox } from 'cloud-react';
10+
11+
export default function NotificationDemo() {
12+
const open = (props = {}) => {
13+
Notification.open({
14+
title: '这里是标题',
15+
content: '消息通知内容消息通知内容消息',
16+
showIcon: true,
17+
duration: 0,
18+
...props
19+
});
20+
}
21+
return (
22+
<div style={{ display: 'flex', gap: 20, marginBottom: 20, marginTop: 15 }}>
23+
<Button
24+
onClick={() => open({
25+
style: {
26+
border: '1px solid #496BF5',
27+
background: '#F5F7FF'
28+
}
29+
})}>
30+
自定义边框
31+
</Button>
32+
<Button onClick={() => open({ isLightTheme: true, style: { background: '#496BF5' } })}>自定义背景</Button>
33+
</div>
34+
);
35+
}
36+
```

src/components/notification/demos/placement.md

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,28 @@ desc: 使用 placement 可以配置通知从右上角、右下角、左下角、
88
import React from 'react';
99
import { Notification, Button, Message } from 'cloud-react';
1010

11-
const placementList = [
12-
{ label: '左上', value: 'top-left' },
13-
{ label: '右上', value: 'top-right' },
14-
{ label: '左下', value: 'bottom-left' },
15-
{ label: '右下', value: 'bottom-right' },
16-
];
17-
1811
export default function NotificationDemo() {
1912
const open = placement => {
2013
Notification.open({
2114
title: '这里是标题',
2215
content: '消息通知内容消息通知内容消息',
2316
showIcon: true,
2417
placement,
25-
// showCancelBtn: true,
26-
// showConfirmBtn: true,
27-
// showDetailBtn: true,
2818
duration: 0,
29-
// onConfirm: id => {
30-
// Message.info('点击确定关闭了!')
31-
// Notification.close(id);
32-
// },
3319
});
3420
}
3521
return (
36-
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
37-
<Button onClick={() => open('top-left')}>从左上打开</Button>
38-
<Button onClick={() => open('top-right')}>从右上打开</Button>
39-
<Button onClick={() => open('bottom-left')}>从左下打开</Button>
40-
<Button onClick={() => open('bottom-right')}>从右下打开</Button>
41-
<Button onClick={() => open('top')}>从上打开</Button>
42-
<Button onClick={() => open('bottom')}>从下打开</Button>
22+
<div>
23+
<div style={{ display: 'flex', gap: 20, marginBottom: 20 }}>
24+
<Button onClick={() => open('top-left')}>从左上打开</Button>
25+
<Button onClick={() => open('top')}>从上打开</Button>
26+
<Button onClick={() => open('top-right')}>从右上打开</Button>
27+
</div>
28+
<div style={{ display: 'flex', gap: 20 }}>
29+
<Button onClick={() => open('bottom-left')}>从左下打开</Button>
30+
<Button onClick={() => open('bottom')}>从下打开</Button>
31+
<Button onClick={() => open('bottom-right')}>从右下打开</Button>
32+
</div>
4333
</div>
4434
);
4535
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
order: 1
3+
title: Drawer
4+
desc: 抽屉组件基础用法
5+
---
6+
7+
```jsx
8+
import React, { useState } from 'react';
9+
import { Notification, Button, Message, Icon, Radio, Checkbox } from 'cloud-react';
10+
11+
export default function NotificationDemo() {
12+
const [borderRadiusSize, setBorderRadiusSize] = useState('default');
13+
const open = (iconType, icon) => {
14+
Notification.open({
15+
title: '这里是标题',
16+
content: '消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息消息通知内容消息通知内容消息通知内容消息',
17+
showIcon: true,
18+
showCancelBtn: true,
19+
showConfirmBtn: true,
20+
iconType,
21+
icon,
22+
borderRadiusSize,
23+
onConfirm: id => {
24+
Message.info('点击确定关闭了!')
25+
Notification.close(id);
26+
},
27+
});
28+
}
29+
return (
30+
<div>
31+
<Radio.Group
32+
value={borderRadiusSize}
33+
onChange={v => {
34+
setBorderRadiusSize(v);
35+
}}>
36+
<Radio value="small">圆角:3px</Radio>
37+
<Radio value="middle">圆角:6px</Radio>
38+
<Radio value="default">圆角:12px</Radio>
39+
</Radio.Group>
40+
<div style={{ display: 'flex', gap: 20, marginBottom: 20, marginTop: 15 }}>
41+
<Button onClick={() => open('info')}>提示类消息</Button>
42+
<Button onClick={() => open('success')}>成功类消息</Button>
43+
<Button onClick={() => open('warn')}>警告类消息</Button>
44+
<Button onClick={() => open('fail')}>失败类消息</Button>
45+
<Button
46+
onClick={() => {
47+
open('', <Icon type="question-circle-solid" style={{ color: '#FFBB00', fontSize: 20, position: 'relative', top: 1 }}/>);
48+
}}
49+
>自定义图标
50+
</Button>
51+
</div>
52+
</div>
53+
);
54+
}
55+
```

src/components/notification/index.js

Lines changed: 79 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,78 @@ import ReactDOM from 'react-dom';
33
import PropTypes from 'prop-types';
44
import NotificationBody from './notification-body';
55
import './index.less';
6-
import { DEFAULT_BODY_PROPS, NOTIFICATION_BODY_PROPS } from './constant';
76
import { getRootWindow, prefixCls } from '../../utils';
87

98
const CONTAINER_CLS = `${prefixCls}-notification-container`;
109
const WRAPPER_CLS = `${prefixCls}-notification-wrapper`;
1110

1211
const PLACEMENT_LIST = [ 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'top', 'bottom' ];
1312

13+
const DEFAULT_BODY_PROPS = {
14+
placement: 'top-right',
15+
container: null,
16+
duration: 4500,
17+
showCloseIcon: null,
18+
btn: null,
19+
className: '',
20+
borderRadiusSize: 'default',
21+
showIcon: false,
22+
iconType: 'info',
23+
icon: null,
24+
showCancelBtn: false,
25+
showConfirmBtn: false,
26+
showDetailBtn: false,
27+
onConfirm: () => {},
28+
style: {},
29+
isLightTheme: false,
30+
}
31+
1432
/**
1533
* 更新各条信息提示的样式
1634
*/
17-
function updateNotifyStyle(excludeId = null) {
35+
function updateNotifyStyle(placement, excludeId = null) {
1836
const rootDocument = getRootWindow().document;
19-
const wrapperContainer = rootDocument.querySelector(`.${CONTAINER_CLS}`);
37+
const wrapperContainer = rootDocument.querySelector(`.${CONTAINER_CLS}.${placement}`);
2038
const wrapperNodes = Array.from(wrapperContainer.childNodes);
2139

22-
wrapperNodes
23-
.filter(node => node.dataset.id !== excludeId)
24-
.forEach((node, index) => {
25-
const yHeight = wrapperNodes.slice(0, index).reduce((sum, n) => {
26-
sum += n.clientHeight;
27-
return sum;
28-
}, 0) + 20 * index;
29-
node.style.transform = `translate3d(0px, ${yHeight}px, 0px) scaleX(1)`;
30-
});
40+
if (placement.includes('top')) {
41+
wrapperNodes
42+
.filter(node => node.dataset.id !== excludeId)
43+
.forEach((node, index) => {
44+
const yHeight = wrapperNodes.slice(0, index).reduce((sum, n) => {
45+
sum += n.clientHeight;
46+
return sum;
47+
}, 0) + 20 * index;
48+
node.style.transform = `translate3d(0px, ${yHeight}px, 0px) scaleX(1)`;
49+
});
50+
} else {
51+
wrapperNodes
52+
.filter(node => node.dataset.id !== excludeId)
53+
.reverse()
54+
.forEach((node, index) => {
55+
const yHeight = wrapperNodes.slice(0, index).reduce((sum, n) => {
56+
sum += n.clientHeight;
57+
return sum;
58+
}, 0) + 20 * index;
59+
node.style.transform = `translate3d(0px, -${yHeight}px, 0px) scaleX(1)`;
60+
});
61+
}
3162
}
3263

3364
/**
3465
* 关闭信息提示
3566
* @param dataSetId
67+
* @param placement
3668
*/
37-
function closeNotification(dataSetId) {
69+
function closeNotification(dataSetId, placement) {
3870
const rootDocument = getRootWindow().document;
39-
const wrapperContainer = rootDocument.querySelector(`.${CONTAINER_CLS}`);
71+
const wrapperContainer = rootDocument.querySelector(`.${CONTAINER_CLS}.${placement}`);
4072
const wrapperNodes = Array.from(wrapperContainer.childNodes);
4173
const wrapper = wrapperNodes.find(node => node.dataset.id === dataSetId);
4274
if (wrapper) {
4375
wrapper.classList.add('fade-out');
4476

45-
updateNotifyStyle(dataSetId);
77+
updateNotifyStyle(placement, dataSetId);
4678

4779
setTimeout(() => {
4880
wrapperContainer.removeChild(wrapper);
@@ -55,29 +87,37 @@ function closeNotification(dataSetId) {
5587
* @param props
5688
*/
5789
function openNotification(props) {
90+
const _props = { ...DEFAULT_BODY_PROPS, ...props };
5891
const rootDocument = getRootWindow().document;
59-
const rootContainer = props.container || rootDocument.body;
92+
const rootContainer = _props.container || rootDocument.body;
6093

6194
// wrapperContainer:所有提示信息外层的 div
62-
let wrapperContainer = rootDocument.querySelector(`.${CONTAINER_CLS}`);
95+
let wrapperContainer = rootDocument.querySelector(`.${CONTAINER_CLS}.${_props.placement}`);
6396
if (!wrapperContainer) {
6497
wrapperContainer = rootDocument.createElement('div');
65-
wrapperContainer.className = CONTAINER_CLS;
98+
wrapperContainer.classList.add(CONTAINER_CLS);
99+
wrapperContainer.classList.add(_props.placement);
66100
rootContainer.appendChild(wrapperContainer);
67101
}
68102

69103
// wrapper:每个提示信息外层的 div
70104
const wrapper = rootDocument.createElement('div');
71105
wrapper.classList.add(WRAPPER_CLS);
72106
wrapper.dataset.id = `${new Date().getTime()}`;
73-
wrapperContainer.insertBefore(wrapper, wrapperContainer.firstChild); // 将新的消息放在最上方
107+
if (_props.placement.includes('top')) {
108+
// 将新的消息放在最上方
109+
wrapperContainer.insertBefore(wrapper, wrapperContainer.firstChild);
110+
} else {
111+
wrapperContainer.appendChild(wrapper);
112+
}
113+
74114
setTimeout(() => {
75115
wrapper.classList.add('fade-in');
76-
updateNotifyStyle();
116+
updateNotifyStyle(_props.placement);
77117
})
78118

79119
ReactDOM.render(
80-
<NotificationBody {...props} dataSetId={wrapper.dataset.id} close={closeNotification} />,
120+
<NotificationBody {..._props} dataSetId={wrapper.dataset.id} close={closeNotification} />,
81121
wrapper,
82122
);
83123
return new Promise(resolve => {
@@ -87,18 +127,29 @@ function openNotification(props) {
87127

88128
const Notification = {
89129
open: openNotification,
90-
close: dataSetId => closeNotification(dataSetId)
130+
close: (dataSetId, placement) => closeNotification(dataSetId, placement)
91131
};
92132

93133
export default Notification;
94134

95135
Notification.propTypes = {
96-
...NOTIFICATION_BODY_PROPS,
136+
title: PropTypes.any.isRequired,
137+
content: PropTypes.any.isRequired,
138+
duration: PropTypes.number,
139+
showCloseIcon: PropTypes.bool,
140+
btn: PropTypes.any,
141+
className: PropTypes.string,
142+
borderRadiusSize: PropTypes.oneOf([ 'small', 'default', 'large' ]),
143+
showIcon: PropTypes.bool,
144+
iconType: PropTypes.oneOf(['info', 'success', 'warn', 'fail']),
145+
icon: PropTypes.any,
146+
showCancelBtn: PropTypes.bool,
147+
showConfirmBtn: PropTypes.bool,
148+
showDetailBtn: PropTypes.bool,
149+
onConfirm: PropTypes.func,
97150
placement: PropTypes.oneOf(PLACEMENT_LIST),
98151
container: PropTypes.any,
152+
style: PropTypes.object,
153+
isLightTheme: PropTypes.bool,
99154
};
100-
Notification.defaultProps = {
101-
...DEFAULT_BODY_PROPS,
102-
placement: 'top-left',
103-
container: null,
104-
};
155+
Notification.defaultProps = DEFAULT_BODY_PROPS;

0 commit comments

Comments
 (0)