@arcblock/ux 2.1.15 → 2.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Datatable/TableSearch.js +17 -2
- package/lib/Datatable/index.js +4 -2
- package/lib/Header/header.js +4 -3
- package/lib/Header/responsive-header.js +1 -1
- package/lib/Layout/dashboard/index.js +121 -100
- package/lib/Layout/dashboard/sidebar.js +23 -64
- package/lib/Layout/{dashboard → dashboard-legacy}/header.js +1 -1
- package/lib/Layout/dashboard-legacy/index.js +154 -0
- package/lib/Layout/dashboard-legacy/sidebar.js +117 -0
- package/lib/NavMenu/nav-menu.js +1 -3
- package/package.json +4 -4
- package/src/Datatable/TableSearch.js +16 -3
- package/src/Datatable/index.js +6 -2
- package/src/Header/header.js +9 -3
- package/src/Header/responsive-header.js +1 -1
- package/src/Layout/dashboard/index.js +117 -116
- package/src/Layout/dashboard/sidebar.js +69 -84
- package/src/Layout/{dashboard → dashboard-legacy}/header.js +0 -0
- package/src/Layout/dashboard-legacy/index.js +150 -0
- package/src/Layout/dashboard-legacy/sidebar.js +120 -0
- package/src/NavMenu/nav-menu.js +1 -3
|
@@ -1,15 +1,89 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
1
|
+
import React, { useEffect, useMemo } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
import { withRouter, Link, useLocation, matchPath } from 'react-router-dom';
|
|
3
4
|
import Helmet from 'react-helmet';
|
|
4
5
|
import styled from 'styled-components';
|
|
6
|
+
import useTheme from '@mui/styles/useTheme';
|
|
5
7
|
import Container from '@mui/material/Container';
|
|
8
|
+
import Hidden from '@mui/material/Hidden';
|
|
6
9
|
import Box from '@mui/material/Box';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
import Sidebar from './sidebar';
|
|
11
|
-
import Header from './header';
|
|
10
|
+
import DashboardLegacy from '../dashboard-legacy';
|
|
11
|
+
import { ResponsiveHeader } from '../../Header';
|
|
12
|
+
import NavMenu from '../../NavMenu';
|
|
12
13
|
import Footer from '../../Footer';
|
|
14
|
+
import Sidebar from './sidebar';
|
|
15
|
+
|
|
16
|
+
// 监听 location 变化并关闭 header 中的 menu (确保 drawer - disablePortal:false, keepMounted:true)
|
|
17
|
+
// 直接监听 menu 中 link 点击来触发 closeMenu 会有问题 (Suspense & lazy)
|
|
18
|
+
// eslint-disable-next-line react/prop-types
|
|
19
|
+
function NavMenuWrapper({ closeMenu, ...rest }) {
|
|
20
|
+
const location = useLocation();
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
closeMenu();
|
|
23
|
+
}, [location]);
|
|
24
|
+
return <NavMenu {...rest} />;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function Dashboard({ children, title, headerProps, links, ...rest }) {
|
|
28
|
+
const theme = useTheme();
|
|
29
|
+
const location = useLocation();
|
|
30
|
+
const navItems = useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
links.map(link => ({
|
|
33
|
+
...link,
|
|
34
|
+
label: <Link to={link.url}>{link.title}</Link>,
|
|
35
|
+
active: !!matchPath(location.pathname, { path: link.url, exact: false }),
|
|
36
|
+
})),
|
|
37
|
+
[location, links]
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Wrapper {...rest} className={`dashboard ${rest.className}`}>
|
|
42
|
+
<Helmet title={title} key={title} />
|
|
43
|
+
|
|
44
|
+
<StyledUxHeader {...headerProps}>
|
|
45
|
+
{({ isMobile, closeMenu }) => {
|
|
46
|
+
if (isMobile) {
|
|
47
|
+
return (
|
|
48
|
+
<NavMenuWrapper
|
|
49
|
+
mode="inline"
|
|
50
|
+
items={navItems}
|
|
51
|
+
closeMenu={closeMenu}
|
|
52
|
+
bgColor="transparent"
|
|
53
|
+
activeTextColor={theme.palette.primary.main}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}}
|
|
59
|
+
</StyledUxHeader>
|
|
60
|
+
|
|
61
|
+
<Box display="flex" className="dashboard-body">
|
|
62
|
+
<Hidden mdDown>
|
|
63
|
+
<Box className="dashboard-sidebar">
|
|
64
|
+
<Sidebar links={links} />
|
|
65
|
+
</Box>
|
|
66
|
+
</Hidden>
|
|
67
|
+
<Box className="dashboard-main">
|
|
68
|
+
<Container className="dashboard-content">{children}</Container>
|
|
69
|
+
<Footer className="dashboard-footer" />
|
|
70
|
+
</Box>
|
|
71
|
+
</Box>
|
|
72
|
+
</Wrapper>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Dashboard.propTypes = {
|
|
77
|
+
children: PropTypes.any.isRequired,
|
|
78
|
+
title: PropTypes.string,
|
|
79
|
+
links: PropTypes.array.isRequired,
|
|
80
|
+
headerProps: PropTypes.object,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
Dashboard.defaultProps = {
|
|
84
|
+
title: 'Home',
|
|
85
|
+
headerProps: {},
|
|
86
|
+
};
|
|
13
87
|
|
|
14
88
|
const Wrapper = styled.div`
|
|
15
89
|
&.dashboard {
|
|
@@ -17,134 +91,61 @@ const Wrapper = styled.div`
|
|
|
17
91
|
flex-direction: column;
|
|
18
92
|
height: 100vh;
|
|
19
93
|
}
|
|
20
|
-
.
|
|
94
|
+
.dashboard-body {
|
|
21
95
|
overflow: hidden;
|
|
22
96
|
flex: 1;
|
|
23
97
|
}
|
|
24
|
-
.
|
|
98
|
+
.dashboard-sidebar {
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
flex: 0 0 auto;
|
|
101
|
+
width: 104px;
|
|
102
|
+
padding: 0 24px;
|
|
103
|
+
&:hover {
|
|
104
|
+
overflow-y: auto;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
.dashboard-main {
|
|
25
108
|
display: flex;
|
|
26
109
|
flex-direction: column;
|
|
27
110
|
overflow: auto;
|
|
28
111
|
flex: 1;
|
|
29
112
|
}
|
|
30
|
-
.
|
|
113
|
+
.dashboard-content {
|
|
31
114
|
flex: 1;
|
|
32
115
|
}
|
|
33
|
-
.
|
|
116
|
+
.dashboard-footer {
|
|
34
117
|
padding-left: 30px;
|
|
35
118
|
}
|
|
119
|
+
`;
|
|
36
120
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
121
|
+
const StyledUxHeader = styled(ResponsiveHeader)`
|
|
122
|
+
.header-container {
|
|
123
|
+
max-width: 100%;
|
|
124
|
+
}
|
|
125
|
+
.header-logo {
|
|
126
|
+
display: flex;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
/* logo 与 sidebar 中的 icon 垂直对齐, 104 - 24 * 2 = 56 */
|
|
129
|
+
width: 56px;
|
|
45
130
|
}
|
|
46
131
|
`;
|
|
47
132
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
headerAddon,
|
|
55
|
-
images,
|
|
56
|
-
links,
|
|
57
|
-
prefix,
|
|
58
|
-
fullWidth,
|
|
59
|
-
contentLayout,
|
|
60
|
-
className,
|
|
61
|
-
homeUrl,
|
|
62
|
-
logo,
|
|
63
|
-
...rest
|
|
64
|
-
}) {
|
|
65
|
-
const breakpoint = 960;
|
|
66
|
-
const { width } = useWindowSize();
|
|
67
|
-
const [drawerMode, setDrawerMode] = useState(width >= breakpoint ? 'permanent' : 'temporary');
|
|
68
|
-
const [drawerOpen, setDrawerOpen] = useState(drawerMode === 'permanent');
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
const newMode = width >= breakpoint ? 'permanent' : 'temporary';
|
|
72
|
-
setDrawerMode(newMode);
|
|
73
|
-
setDrawerOpen(newMode !== 'temporary');
|
|
74
|
-
}, [width]);
|
|
75
|
-
|
|
76
|
-
const onToggleDrawer = () => {
|
|
77
|
-
setDrawerOpen(!drawerOpen);
|
|
78
|
-
};
|
|
79
|
-
const isFullWidth = fullWidth || contentLayout === 'row';
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<Wrapper className={`dashboard ${className}`} {...rest}>
|
|
83
|
-
<Helmet title={`${title}-${brand}`} />
|
|
84
|
-
|
|
85
|
-
<Header
|
|
86
|
-
className="dashboard__header"
|
|
87
|
-
onToggleMenu={onToggleDrawer}
|
|
88
|
-
brand={brand}
|
|
89
|
-
brandAddon={brandAddon}
|
|
90
|
-
description={description}
|
|
91
|
-
addons={headerAddon}
|
|
92
|
-
homeUrl={homeUrl}
|
|
93
|
-
logo={logo}
|
|
94
|
-
/>
|
|
95
|
-
<Box display="flex" className="dashboard__body">
|
|
96
|
-
<Drawer
|
|
97
|
-
variant={drawerMode}
|
|
98
|
-
className="drawer"
|
|
99
|
-
classes={{ paper: 'drawerPaper' }}
|
|
100
|
-
open={drawerOpen}
|
|
101
|
-
onClose={onToggleDrawer}
|
|
102
|
-
ModalProps={{ disablePortal: true, keepMounted: true }}>
|
|
103
|
-
<Sidebar
|
|
104
|
-
className="dashboard__sidebar"
|
|
105
|
-
images={images}
|
|
106
|
-
links={links}
|
|
107
|
-
prefix={prefix}
|
|
108
|
-
logo={logo}
|
|
109
|
-
/>
|
|
110
|
-
</Drawer>
|
|
111
|
-
<Box className="dashboard__main">
|
|
112
|
-
<Container maxWidth={isFullWidth ? false : 'lg'} className="dashboard__content">
|
|
113
|
-
{children}
|
|
114
|
-
</Container>
|
|
115
|
-
<Footer className="dashboard__footer" />
|
|
116
|
-
</Box>
|
|
117
|
-
</Box>
|
|
118
|
-
</Wrapper>
|
|
119
|
-
);
|
|
133
|
+
// 兼容旧版 dashboard
|
|
134
|
+
function DashboardWrapper({ legacy, ...rest }) {
|
|
135
|
+
if (legacy) {
|
|
136
|
+
return <DashboardLegacy {...rest} />;
|
|
137
|
+
}
|
|
138
|
+
return <Dashboard {...rest} />;
|
|
120
139
|
}
|
|
121
140
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
brand: PropTypes.string.isRequired,
|
|
126
|
-
links: PropTypes.array.isRequired,
|
|
127
|
-
images: PropTypes.object.isRequired,
|
|
128
|
-
brandAddon: PropTypes.object,
|
|
129
|
-
description: PropTypes.any.isRequired,
|
|
130
|
-
headerAddon: PropTypes.any,
|
|
131
|
-
prefix: PropTypes.string,
|
|
132
|
-
// 兼容旧版的设置,新版使用 fullWidth 进行设置
|
|
133
|
-
contentLayout: PropTypes.oneOf(['row', 'column']),
|
|
134
|
-
fullWidth: PropTypes.bool,
|
|
135
|
-
className: PropTypes.string,
|
|
136
|
-
homeUrl: PropTypes.string,
|
|
137
|
-
logo: PropTypes.any,
|
|
141
|
+
DashboardWrapper.propTypes = {
|
|
142
|
+
...Dashboard.propTypes,
|
|
143
|
+
legacy: PropTypes.bool,
|
|
138
144
|
};
|
|
139
145
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
headerAddon: null,
|
|
144
|
-
brandAddon: null,
|
|
145
|
-
prefix: '/images',
|
|
146
|
-
fullWidth: false,
|
|
147
|
-
className: '',
|
|
148
|
-
homeUrl: '/',
|
|
149
|
-
logo: null,
|
|
146
|
+
DashboardWrapper.defaultProps = {
|
|
147
|
+
...Dashboard.defaultProps,
|
|
148
|
+
legacy: true,
|
|
150
149
|
};
|
|
150
|
+
|
|
151
|
+
export default withRouter(DashboardWrapper);
|
|
@@ -1,120 +1,105 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
|
-
import withTheme from '@mui/styles/withTheme';
|
|
5
4
|
|
|
6
|
-
import {
|
|
7
|
-
import Button from '@mui/material/Button';
|
|
5
|
+
import { NavLink } from 'react-router-dom';
|
|
8
6
|
import Typography from '@mui/material/Typography';
|
|
9
7
|
import { teal } from '@mui/material/colors';
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
import Logo from '../../Logo';
|
|
13
|
-
|
|
14
|
-
function Sidebar({ location, theme, images, links, prefix, addons, logo, ...rest }) {
|
|
15
|
-
const isSelected = (url, name) => {
|
|
16
|
-
const pattern = new RegExp(`/${name}`);
|
|
17
|
-
return pattern.test(location.pathname);
|
|
18
|
-
};
|
|
19
|
-
|
|
9
|
+
function Sidebar({ links, addons, ...rest }) {
|
|
20
10
|
return (
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
})}
|
|
11
|
+
<Root {...rest}>
|
|
12
|
+
<ul>
|
|
13
|
+
{links.map(({ url, icon, title, showBadge }) => {
|
|
14
|
+
return (
|
|
15
|
+
<li key={url}>
|
|
16
|
+
<NavLink
|
|
17
|
+
to={url}
|
|
18
|
+
className="layout-sidebar-link"
|
|
19
|
+
activeClassName="layout-sidebar-link--active">
|
|
20
|
+
<span className={`layout-sidebar-icon ${showBadge ? 'layout-sidebar-badge' : ''}`}>
|
|
21
|
+
{icon}
|
|
22
|
+
</span>
|
|
23
|
+
<Typography component="span" className="layout-sidebar-link-text">
|
|
24
|
+
{title}
|
|
25
|
+
</Typography>
|
|
26
|
+
</NavLink>
|
|
27
|
+
</li>
|
|
28
|
+
);
|
|
29
|
+
})}
|
|
30
|
+
</ul>
|
|
42
31
|
<div style={{ flex: 1 }} />
|
|
43
32
|
{addons}
|
|
44
|
-
</
|
|
33
|
+
</Root>
|
|
45
34
|
);
|
|
46
35
|
}
|
|
47
36
|
|
|
48
37
|
Sidebar.propTypes = {
|
|
49
|
-
location: PropTypes.object.isRequired,
|
|
50
|
-
theme: PropTypes.object.isRequired,
|
|
51
|
-
images: PropTypes.object.isRequired,
|
|
52
38
|
links: PropTypes.array.isRequired,
|
|
53
|
-
prefix: PropTypes.string,
|
|
54
39
|
addons: PropTypes.any,
|
|
55
|
-
logo: PropTypes.any,
|
|
56
40
|
};
|
|
57
41
|
|
|
58
42
|
Sidebar.defaultProps = {
|
|
59
|
-
prefix: '/images',
|
|
60
43
|
addons: null,
|
|
61
|
-
logo: null,
|
|
62
44
|
};
|
|
63
45
|
|
|
64
|
-
const
|
|
65
|
-
|
|
46
|
+
const gradient = 'linear-gradient(32deg, rgba(144, 255, 230, 0.1), rgba(144, 255, 230, 0))';
|
|
47
|
+
|
|
48
|
+
const Root = styled.div`
|
|
66
49
|
display: flex;
|
|
67
50
|
flex-direction: column;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
background: ${props => props.theme.palette.background.default};
|
|
73
|
-
position: sticky;
|
|
74
|
-
top: 0;
|
|
75
|
-
z-index: 1;
|
|
76
|
-
padding: 10px 0;
|
|
77
|
-
text-align: center;
|
|
78
|
-
font-size: 0;
|
|
79
|
-
}
|
|
80
|
-
${props => props.theme.breakpoints.down('md')} {
|
|
81
|
-
&& .sidebar-logo {
|
|
82
|
-
display: block;
|
|
83
|
-
}
|
|
51
|
+
ul {
|
|
52
|
+
list-style: none;
|
|
53
|
+
margin: 0;
|
|
54
|
+
padding: 0;
|
|
84
55
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const gradient = 'linear-gradient(32deg, rgba(144, 255, 230, 0.1), rgba(144, 255, 230, 0))';
|
|
88
|
-
|
|
89
|
-
const MenuItem = styled(Button)`
|
|
90
|
-
&& {
|
|
56
|
+
.layout-sidebar-link {
|
|
91
57
|
display: flex;
|
|
92
58
|
flex-direction: column;
|
|
93
|
-
justify-content: center;
|
|
94
59
|
align-items: center;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
padding: 24px 0;
|
|
99
|
-
border-left: 2px solid ${props => (props.selected ? teal.A700 : 'transparent')};
|
|
100
|
-
border-radius: 0;
|
|
60
|
+
padding: 16px 0;
|
|
61
|
+
color: ${props => props.theme.palette.text.secondary};
|
|
62
|
+
text-decoration: none;
|
|
101
63
|
|
|
102
|
-
&:hover
|
|
64
|
+
&:hover,
|
|
65
|
+
&.layout-sidebar-link--active {
|
|
66
|
+
color: ${props => props.theme.palette.primary.main};
|
|
103
67
|
background: ${gradient};
|
|
104
68
|
border-left-color: ${teal.A700};
|
|
105
69
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
color: ${props => props.theme.palette.text.primary};
|
|
70
|
+
}
|
|
71
|
+
.layout-sidebar-icon {
|
|
72
|
+
display: inline-block;
|
|
73
|
+
width: 32px;
|
|
74
|
+
height: 32px;
|
|
75
|
+
> img,
|
|
76
|
+
> svg {
|
|
77
|
+
width: 32px;
|
|
78
|
+
height: 32px;
|
|
116
79
|
}
|
|
117
80
|
}
|
|
81
|
+
.layout-sidebar-badge {
|
|
82
|
+
position: relative;
|
|
83
|
+
&:after {
|
|
84
|
+
content: '';
|
|
85
|
+
position: absolute;
|
|
86
|
+
width: 10px;
|
|
87
|
+
height: 10px;
|
|
88
|
+
border-radius: 10px;
|
|
89
|
+
background-color: #fe4e44;
|
|
90
|
+
right: -2px;
|
|
91
|
+
top: 0;
|
|
92
|
+
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3), 0px 0px 20px rgba(0, 0, 0, 0.1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
.layout-sidebar-link-text {
|
|
96
|
+
margin-top: 8px;
|
|
97
|
+
font-size: 12px;
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
text-align: center;
|
|
100
|
+
text-transform: capitalize;
|
|
101
|
+
letter-spacing: normal;
|
|
102
|
+
}
|
|
118
103
|
`;
|
|
119
104
|
|
|
120
|
-
export default
|
|
105
|
+
export default Sidebar;
|
|
File without changes
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import Helmet from 'react-helmet';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import Container from '@mui/material/Container';
|
|
6
|
+
import Box from '@mui/material/Box';
|
|
7
|
+
import Drawer from '@mui/material/Drawer';
|
|
8
|
+
import useWindowSize from 'react-use/lib/useWindowSize';
|
|
9
|
+
|
|
10
|
+
import Sidebar from './sidebar';
|
|
11
|
+
import Header from './header';
|
|
12
|
+
import Footer from '../../Footer';
|
|
13
|
+
|
|
14
|
+
const Wrapper = styled.div`
|
|
15
|
+
&.dashboard {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
}
|
|
20
|
+
.dashboard__body {
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
flex: 1;
|
|
23
|
+
}
|
|
24
|
+
.dashboard__main {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
overflow: auto;
|
|
28
|
+
flex: 1;
|
|
29
|
+
}
|
|
30
|
+
.dashboard__content {
|
|
31
|
+
flex: 1;
|
|
32
|
+
}
|
|
33
|
+
.dashboard__footer {
|
|
34
|
+
padding-left: 30px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.drawerPaper {
|
|
38
|
+
position: relative;
|
|
39
|
+
white-space: nowrap;
|
|
40
|
+
width: 120px;
|
|
41
|
+
background: ${props => props.theme.palette.background.default};
|
|
42
|
+
box-shadow: 2px 16px 10px 0
|
|
43
|
+
rgba(0, 0, 0, ${props => (props.theme.mode === 'light' ? 0.05 : 0.5)});
|
|
44
|
+
border: 0;
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export default function Dashboard({
|
|
49
|
+
children,
|
|
50
|
+
title,
|
|
51
|
+
brand,
|
|
52
|
+
description,
|
|
53
|
+
brandAddon,
|
|
54
|
+
headerAddon,
|
|
55
|
+
images,
|
|
56
|
+
links,
|
|
57
|
+
prefix,
|
|
58
|
+
fullWidth,
|
|
59
|
+
contentLayout,
|
|
60
|
+
className,
|
|
61
|
+
homeUrl,
|
|
62
|
+
logo,
|
|
63
|
+
...rest
|
|
64
|
+
}) {
|
|
65
|
+
const breakpoint = 960;
|
|
66
|
+
const { width } = useWindowSize();
|
|
67
|
+
const [drawerMode, setDrawerMode] = useState(width >= breakpoint ? 'permanent' : 'temporary');
|
|
68
|
+
const [drawerOpen, setDrawerOpen] = useState(drawerMode === 'permanent');
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const newMode = width >= breakpoint ? 'permanent' : 'temporary';
|
|
72
|
+
setDrawerMode(newMode);
|
|
73
|
+
setDrawerOpen(newMode !== 'temporary');
|
|
74
|
+
}, [width]);
|
|
75
|
+
|
|
76
|
+
const onToggleDrawer = () => {
|
|
77
|
+
setDrawerOpen(!drawerOpen);
|
|
78
|
+
};
|
|
79
|
+
const isFullWidth = fullWidth || contentLayout === 'row';
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Wrapper className={`dashboard ${className}`} {...rest}>
|
|
83
|
+
<Helmet title={`${title}-${brand}`} />
|
|
84
|
+
|
|
85
|
+
<Header
|
|
86
|
+
className="dashboard__header"
|
|
87
|
+
onToggleMenu={onToggleDrawer}
|
|
88
|
+
brand={brand}
|
|
89
|
+
brandAddon={brandAddon}
|
|
90
|
+
description={description}
|
|
91
|
+
addons={headerAddon}
|
|
92
|
+
homeUrl={homeUrl}
|
|
93
|
+
logo={logo}
|
|
94
|
+
/>
|
|
95
|
+
<Box display="flex" className="dashboard__body">
|
|
96
|
+
<Drawer
|
|
97
|
+
variant={drawerMode}
|
|
98
|
+
className="drawer"
|
|
99
|
+
classes={{ paper: 'drawerPaper' }}
|
|
100
|
+
open={drawerOpen}
|
|
101
|
+
onClose={onToggleDrawer}
|
|
102
|
+
ModalProps={{ disablePortal: true, keepMounted: true }}>
|
|
103
|
+
<Sidebar
|
|
104
|
+
className="dashboard__sidebar"
|
|
105
|
+
images={images}
|
|
106
|
+
links={links}
|
|
107
|
+
prefix={prefix}
|
|
108
|
+
logo={logo}
|
|
109
|
+
/>
|
|
110
|
+
</Drawer>
|
|
111
|
+
<Box className="dashboard__main">
|
|
112
|
+
<Container maxWidth={isFullWidth ? false : 'lg'} className="dashboard__content">
|
|
113
|
+
{children}
|
|
114
|
+
</Container>
|
|
115
|
+
<Footer className="dashboard__footer" />
|
|
116
|
+
</Box>
|
|
117
|
+
</Box>
|
|
118
|
+
</Wrapper>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
Dashboard.propTypes = {
|
|
123
|
+
children: PropTypes.any.isRequired,
|
|
124
|
+
title: PropTypes.string,
|
|
125
|
+
brand: PropTypes.string.isRequired,
|
|
126
|
+
links: PropTypes.array.isRequired,
|
|
127
|
+
images: PropTypes.object.isRequired,
|
|
128
|
+
brandAddon: PropTypes.object,
|
|
129
|
+
description: PropTypes.any.isRequired,
|
|
130
|
+
headerAddon: PropTypes.any,
|
|
131
|
+
prefix: PropTypes.string,
|
|
132
|
+
// 兼容旧版的设置,新版使用 fullWidth 进行设置
|
|
133
|
+
contentLayout: PropTypes.oneOf(['row', 'column']),
|
|
134
|
+
fullWidth: PropTypes.bool,
|
|
135
|
+
className: PropTypes.string,
|
|
136
|
+
homeUrl: PropTypes.string,
|
|
137
|
+
logo: PropTypes.any,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
Dashboard.defaultProps = {
|
|
141
|
+
title: 'Home',
|
|
142
|
+
contentLayout: 'column',
|
|
143
|
+
headerAddon: null,
|
|
144
|
+
brandAddon: null,
|
|
145
|
+
prefix: '/images',
|
|
146
|
+
fullWidth: false,
|
|
147
|
+
className: '',
|
|
148
|
+
homeUrl: '/',
|
|
149
|
+
logo: null,
|
|
150
|
+
};
|