Added notification component on the Header.

This commit is contained in:
zuiidea
2018-10-16 14:44:35 +08:00
parent f6454e71f0
commit c1df88c25f
8 changed files with 180 additions and 30 deletions
+68 -3
View File
@@ -1,20 +1,31 @@
import React, { PureComponent, Fragment } from 'react'
import PropTypes from 'prop-types'
import { Menu, Icon, Layout, Avatar } from 'antd'
import { Menu, Icon, Layout, Avatar, Popover, Badge, List } from 'antd'
import { Ellipsis } from 'ant-design-pro'
import { Trans, withI18n } from '@lingui/react'
import { setLocale } from 'utils'
import moment from 'moment'
import classnames from 'classnames'
import config from 'config'
import styles from './Header.less'
const { SubMenu } = Menu
@withI18n()
class Header extends PureComponent {
handleClickMenu = e => {
e.key === 'SignOut' && this.props.onSignOut()
}
render() {
const { username, avatar, collapsed, onCollapseChange, i18n } = this.props
const {
i18n,
avatar,
username,
collapsed,
notifications,
onCollapseChange,
onAllNotificationsRead,
} = this.props
const rightContent = [
<Menu key="user" mode="horizontal" onClick={this.handleClickMenu}>
@@ -66,8 +77,60 @@ class Header extends PureComponent {
</Menu>
)
}
rightContent.unshift(
<Popover
placement="bottomRight"
trigger="click"
key="notifications"
overlayClassName={styles.notificationPopover}
getPopupContainer={() => document.querySelector('#layoutHeader')}
content={
<div className={styles.notification}>
<List
itemLayout="horizontal"
dataSource={notifications}
locale={{
emptyText: <Trans>You have viewed all notifications.</Trans>,
}}
renderItem={item => (
<List.Item className={styles.notificationItem}>
<List.Item.Meta
title={<Ellipsis lines={1}>{item.title}</Ellipsis>}
description={moment(item.date).fromNow()}
/>
<Icon
style={{ fontSize: 10, color: '#ccc' }}
type="right"
theme="outlined"
/>
</List.Item>
)}
/>
{notifications.length ? (
<div
onClick={onAllNotificationsRead}
className={styles.clearButton}
>
<Trans>Clear notifications</Trans>
</div>
) : null}
</div>
}
>
<Badge
count={notifications.length}
dot
offset={[-10, 10]}
className={styles.iconButton}
>
<Icon className={styles.iconFont} type="bell" />
</Badge>
</Popover>
)
return (
<Layout.Header className={styles.header}>
<Layout.Header className={styles.header} id="layoutHeader">
<div
className={styles.button}
onClick={onCollapseChange.bind(this, !collapsed)}
@@ -91,7 +154,9 @@ Header.propTypes = {
user: PropTypes.object,
collapsed: PropTypes.bool,
onSignOut: PropTypes.func,
notifications: PropTypes.array,
onCollapseChange: PropTypes.func,
onAllNotificationsRead: PropTypes.func,
}
export default Header
+67 -23
View File
@@ -2,6 +2,15 @@
.header {
padding: 0;
box-shadow: @shadow-2;
position: relative;
display: flex;
justify-content: space-between;
height: 72px;
z-index: 9;
align-items: center;
background-color: #fff;
:global {
.ant-menu-submenu-title {
height: 72px;
@@ -11,8 +20,8 @@
line-height: 72px;
& > .ant-menu-submenu:hover {
color: #1890ff;
background-color: rgba(24, 144, 255, 0.15);
color: @primary-color;
background-color: @hover-color;
}
}
@@ -42,17 +51,10 @@
border-bottom: none;
}
}
box-shadow: @shadow-2;
position: relative;
display: flex;
justify-content: space-between;
height: 72px;
z-index: 9;
align-items: center;
background-color: #fff;
.rightContainer {
display: flex;
align-items: center;
}
.button {
@@ -66,26 +68,68 @@
&:hover {
color: @primary-color;
background-color: fade(@primary-color, 15%);
background-color: @hover-color;
}
}
}
.popovermenu {
width: 280px;
margin-left: 6px;
.iconButton {
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 24px;
cursor: pointer;
.background-hover();
:global .ant-popover-inner-content {
padding: 0;
.ant-menu-inline .ant-menu-item,
.ant-menu-vertical .ant-menu-item {
border-right: 0;
&:hover {
.iconFont {
color: @primary-color;
}
}
.ant-menu-inline .ant-menu-item-selected,
.ant-menu-inline .ant-menu-selected {
border-right: 0;
& + .iconButton {
margin-left: 8px;
}
.iconFont {
color: #b2b0c7;
font-size: 24px;
}
}
.notification {
padding: 24px 0;
width: 320px;
.notificationItem {
transition: all 0.3s;
padding: 12px 24px;
cursor: pointer;
&:hover {
background-color: @hover-color;
}
}
.clearButton {
text-align: center;
height: 48px;
line-height: 48px;
cursor: pointer;
.background-hover();
}
}
.notificationPopover {
:global {
.ant-popover-inner-content {
padding: 0;
}
.ant-popover-arrow {
display: none;
}
.ant-list-item-content {
flex: 0;
margin-left: 16px;
}
}
}
+12 -1
View File
@@ -46,7 +46,14 @@ class PrimaryLayout extends PureComponent {
render() {
const { app, location, dispatch, children } = this.props
const { user, theme, routeList, permissions, collapsed } = app
const {
user,
theme,
routeList,
permissions,
collapsed,
notifications,
} = app
const { isMobile } = this.state
const { onCollapseChange } = this
@@ -66,9 +73,13 @@ class PrimaryLayout extends PureComponent {
const headerProps = {
menus,
collapsed,
notifications,
onCollapseChange,
avatar: user.avatar,
username: user.username,
onAllNotificationsRead() {
dispatch({ type: 'app/allNotificationsRead' })
},
onSignOut() {
dispatch({ type: 'app/signOut' })
},
+6 -2
View File
@@ -1,11 +1,15 @@
{
"/dashboard": "/dashboard",
"Clear notifications": "Clear notifications",
"Dark": "Dark",
"Dashboard": "Dashboard",
"Hi,": "Hi,",
"Light": "Light",
"Not Found": "Not Found",
"Password": "Password",
"Sign in": "Sign in",
"Sign out": "Sign out",
"Switch Theme": "Switch Theme",
"Username": "Username"
}
"Username": "Username",
"You have viewed all notifications.": "You have viewed all notifications."
}
+5 -1
View File
@@ -1,11 +1,15 @@
{
"/dashboard": "/zh/dashboard",
"Clear notifications": "清空消息",
"Dark": "暗",
"Dashboard": "仪表盘",
"Hi,": "你好,",
"Light": "明",
"Not Found": "未找到",
"Password": "密码",
"Sign in": "登录",
"Sign out": "退出登录",
"Switch Theme": "切换主题",
"Username": "用户名"
"Username": "用户名",
"You have viewed all notifications.": "您已查看所有通知"
}
+14
View File
@@ -28,6 +28,16 @@ export default {
locationQuery: {},
theme: store.get('theme') || 'light',
collapsed: store.get('collapsed') || false,
notifications: [
{
title: 'New User is registered.',
date: new Date(Date.now() - 10000000),
},
{
title: 'Application has been approved.',
date: new Date(Date.now() - 50000000),
},
],
},
subscriptions: {
setupHistory({ dispatch, history }) {
@@ -149,5 +159,9 @@ export default {
store.set('collapsed', payload)
state.collapsed = payload
},
allNotificationsRead(state) {
state.notifications = []
},
},
}
+1
View File
@@ -13,3 +13,4 @@
@border-color-base: #e5e5e5;
@font-size-base: 13px;
@text-color: #666;
@hover-color: #f9f9fc;
+7
View File
@@ -26,3 +26,10 @@
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.background-hover {
transition: @transition-ease-in;
&:hover {
background-color: @hover-color;
}
}