@arcblock/ux 1.16.0 → 1.16.4

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.
Files changed (90) hide show
  1. package/lib/CodeBlock/index.js +3 -1
  2. package/package.json +6 -5
  3. package/src/ActionButton/index.js +65 -0
  4. package/src/ActivityIndicator/index.js +186 -0
  5. package/src/Alert/index.js +104 -0
  6. package/src/Async/index.js +39 -0
  7. package/src/Badge/index.js +71 -0
  8. package/src/Blocklet/index.js +335 -0
  9. package/src/Button/index.js +4 -0
  10. package/src/Button/wrap.js +88 -0
  11. package/src/ButtonGroup/index.js +19 -0
  12. package/src/Center/index.js +17 -0
  13. package/src/ClickToCopy/index.js +90 -0
  14. package/src/CodeBlock/index.js +160 -0
  15. package/src/Colors/index.js +1 -0
  16. package/src/Colors/themes/default.js +53 -0
  17. package/src/ContactForm/index.js +240 -0
  18. package/src/CookieConsent/index.js +90 -0
  19. package/src/CountDown/index.js +151 -0
  20. package/src/Dialog/confirm.js +76 -0
  21. package/src/Dialog/dialog.js +162 -0
  22. package/src/Dialog/index.js +2 -0
  23. package/src/DriftBot/index.js +81 -0
  24. package/src/Earth/countries.json +8057 -0
  25. package/src/Earth/index.js +511 -0
  26. package/src/Earth/util.js +69 -0
  27. package/src/Empty/index.js +41 -0
  28. package/src/Footer/index.js +84 -0
  29. package/src/Icon/image.js +55 -0
  30. package/src/Icon/index.js +69 -0
  31. package/src/Img/index.js +172 -0
  32. package/src/InfoRow/index.js +83 -0
  33. package/src/Layout/dashboard/header.js +145 -0
  34. package/src/Layout/dashboard/index.js +140 -0
  35. package/src/Layout/dashboard/sidebar.js +120 -0
  36. package/src/Layout/index.js +318 -0
  37. package/src/Locale/browser-lang.js +63 -0
  38. package/src/Locale/context.js +88 -0
  39. package/src/Locale/images/globe-dark.png +0 -0
  40. package/src/Locale/images/globe-light.png +0 -0
  41. package/src/Locale/selector.js +138 -0
  42. package/src/Logo/images/logo-dark-text.svg +3 -0
  43. package/src/Logo/images/logo-dark-top.svg +6 -0
  44. package/src/Logo/images/logo-light-text.svg +3 -0
  45. package/src/Logo/images/logo-light-top.svg +6 -0
  46. package/src/Logo/index.js +47 -0
  47. package/src/Metric/index.js +115 -0
  48. package/src/NFTDisplay/README.md +59 -0
  49. package/src/NFTDisplay/aspect-ratio-container.js +34 -0
  50. package/src/NFTDisplay/broken.js +18 -0
  51. package/src/NFTDisplay/index.js +230 -0
  52. package/src/NFTDisplay/loading.js +17 -0
  53. package/src/NFTDisplay/svg-embedder/img.js +36 -0
  54. package/src/NFTDisplay/svg-embedder/inline-svg.js +37 -0
  55. package/src/PageScroller/index.js +342 -0
  56. package/src/PageScroller/usePrevValue.js +12 -0
  57. package/src/PricingTable/PricingPlan.js +112 -0
  58. package/src/PricingTable/index.js +43 -0
  59. package/src/Screenshot/devices.css +1366 -0
  60. package/src/Screenshot/index.js +181 -0
  61. package/src/Spinner/index.js +33 -0
  62. package/src/Switch/index.js +78 -0
  63. package/src/Tabs/index.js +46 -0
  64. package/src/Tag/index.js +73 -0
  65. package/src/Terminal/Player.js +364 -0
  66. package/src/Terminal/index.js +150 -0
  67. package/src/Terminal/player.css +378 -0
  68. package/src/Terminal/util.js +167 -0
  69. package/src/Terminal/xterm.css +171 -0
  70. package/src/TextCollapse/index.js +92 -0
  71. package/src/Theme/index.js +169 -0
  72. package/src/Theme/responsiveFontSizes.js +94 -0
  73. package/src/Toast/index.js +118 -0
  74. package/src/Util/index.js +264 -0
  75. package/src/Video/index.js +72 -0
  76. package/src/Wallet/Action.js +105 -0
  77. package/src/Wallet/Download.js +130 -0
  78. package/src/Wallet/Open.js +50 -0
  79. package/src/Wallet/images/abtwallet.png +0 -0
  80. package/src/Wallet/images/android_download.svg +23 -0
  81. package/src/Wallet/images/app-store.svg +20 -0
  82. package/src/Wallet/images/google-play.svg +70 -0
  83. package/src/WechatPrompt/images/android.png +0 -0
  84. package/src/WechatPrompt/images/ios.png +0 -0
  85. package/src/WechatPrompt/index.js +81 -0
  86. package/src/index.js +63 -0
  87. package/src/withTheme/index.js +72 -0
  88. package/src/withTracker/README.md +34 -0
  89. package/src/withTracker/error_boundary.js +34 -0
  90. package/src/withTracker/index.js +70 -0
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import styled from 'styled-components';
4
+
5
+ import Img from '../Img';
6
+
7
+ export default function ImageIcon({ name, size, alt, color, style, prefix, showBadge, ...rest }) {
8
+ const src = `${prefix}/${name}-${color.replace(/^#/, '')}.png`;
9
+ return (
10
+ <Div style={{ width: size, height: size }}>
11
+ <Img
12
+ width={size}
13
+ height={size}
14
+ alt={alt || name}
15
+ src={src}
16
+ style={Object.assign({ width: size }, style)}
17
+ {...rest}
18
+ />
19
+ {showBadge && <i className="badge-point" />}
20
+ </Div>
21
+ );
22
+ }
23
+
24
+ ImageIcon.propTypes = {
25
+ name: PropTypes.string.isRequired,
26
+ size: PropTypes.number,
27
+ color: PropTypes.string,
28
+ alt: PropTypes.string,
29
+ style: PropTypes.object,
30
+ prefix: PropTypes.string,
31
+ showBadge: PropTypes.bool,
32
+ };
33
+
34
+ ImageIcon.defaultProps = {
35
+ size: 36,
36
+ color: '#000000',
37
+ alt: '',
38
+ style: {},
39
+ prefix: '/images',
40
+ showBadge: false,
41
+ };
42
+
43
+ const Div = styled.div`
44
+ position: relative;
45
+ .badge-point {
46
+ position: absolute;
47
+ width: 10px;
48
+ height: 10px;
49
+ border-radius: 10px;
50
+ background-color: #fe4e44;
51
+ right: -2px;
52
+ top: 0;
53
+ box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3), 0px 0px 20px rgba(0, 0, 0, 0.1);
54
+ }
55
+ `;
@@ -0,0 +1,69 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import styled from 'styled-components';
4
+
5
+ import colors from '../Colors';
6
+
7
+ const variants = {
8
+ light: 'fal',
9
+ regular: 'far',
10
+ solid: 'fas',
11
+ };
12
+
13
+ const Icon = ({ name, color, size, variant, rounded, style, className, forwardedRef, ...rest }) => {
14
+ const content = (
15
+ <i
16
+ ref={forwardedRef}
17
+ className={`${variants[variant]} fa-${name} ${rounded ? '' : className}`}
18
+ style={Object.assign({ color, fontSize: `${size}px` }, style || {})}
19
+ />
20
+ );
21
+
22
+ if (rounded) {
23
+ return (
24
+ <Span ref={forwardedRef} size={size} color={color} className={className} {...rest}>
25
+ {content}
26
+ </Span>
27
+ );
28
+ }
29
+
30
+ return content;
31
+ };
32
+
33
+ Icon.propTypes = {
34
+ name: PropTypes.string.isRequired,
35
+ className: PropTypes.string,
36
+ color: PropTypes.string,
37
+ rounded: PropTypes.bool,
38
+ variant: PropTypes.string,
39
+ size: PropTypes.number,
40
+ style: PropTypes.object,
41
+ };
42
+
43
+ Icon.defaultProps = {
44
+ rounded: false,
45
+ variant: 'light',
46
+ color: colors.grey[900],
47
+ className: '',
48
+ size: 24,
49
+ style: {},
50
+ };
51
+
52
+ const Span = styled.span`
53
+ width: ${props => props.size * 2}px;
54
+ height: ${props => props.size * 2}px;
55
+ border-radius: 50%;
56
+ border: 1px solid ${props => props.color};
57
+ display: flex;
58
+ justify-content: center;
59
+ align-items: center;
60
+
61
+ .fa,
62
+ .fas,
63
+ .fal,
64
+ .far {
65
+ line-height: ${props => props.size}px;
66
+ }
67
+ `;
68
+
69
+ export default React.forwardRef((props, ref) => <Icon {...props} forwardedRef={ref} />);
@@ -0,0 +1,172 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { makeStyles, SvgIcon } from '@material-ui/core';
4
+ import { useInView } from 'react-intersection-observer';
5
+ import Alert from 'mdi-material-ui/Alert';
6
+ import ImageIcon from 'mdi-material-ui/Image';
7
+
8
+ const useStyles = makeStyles(() => ({
9
+ root: {
10
+ position: 'relative',
11
+ overflow: 'hidden',
12
+ '& .image--state, & .image--img': {
13
+ minWidth: '100%',
14
+ minHeight: '100%',
15
+ position: 'absolute',
16
+ top: 0,
17
+ left: '50%',
18
+ transform: 'translateX(-50%)',
19
+ },
20
+ '& .image--state': {
21
+ display: 'flex',
22
+ flexDirection: 'column',
23
+ justifyContent: 'center',
24
+ alignItems: 'center',
25
+ color: '#999',
26
+ background: '#eee',
27
+ width: '100%',
28
+ height: '100%',
29
+ },
30
+ '& .image--img': {
31
+ opacity: 0,
32
+ },
33
+ '& .image--icon': {
34
+ fontSize: 30,
35
+ maxWidth: '80%',
36
+ maxHeight: '80%',
37
+ },
38
+ },
39
+ }));
40
+
41
+ function Img({
42
+ lazy,
43
+ width,
44
+ height,
45
+ repeat,
46
+ ratio,
47
+ alt,
48
+ size,
49
+ position,
50
+ src,
51
+ placeholder,
52
+ fallback,
53
+ style,
54
+ className,
55
+ onError,
56
+ onSuccess,
57
+ ...rest
58
+ }) {
59
+ const classes = useStyles();
60
+ const [ref, inView] = lazy ? useInView({ threshold: 0, triggerOnce: true }) : [null, true];
61
+
62
+ const [imgState, setImgState] = useState('init');
63
+
64
+ const actualSrc = useMemo(() => {
65
+ switch (imgState) {
66
+ case 'init':
67
+ case 'loading':
68
+ return placeholder;
69
+ case 'error':
70
+ return fallback;
71
+ case 'loaded':
72
+ return src;
73
+ default:
74
+ return null;
75
+ }
76
+ }, [placeholder, fallback, src, imgState]);
77
+
78
+ const actualRatio = useMemo(() => {
79
+ if (width && height) return (100 * height) / width;
80
+ return ratio * 100;
81
+ });
82
+
83
+ const mergedStyle = useMemo(
84
+ () => ({
85
+ backgroundImage: actualSrc ? `url(${actualSrc})` : '',
86
+ backgroundPosition: position,
87
+ backgroundSize: size,
88
+ backgroundRepeat: repeat,
89
+ paddingTop: `${actualRatio}%`,
90
+ }),
91
+ [actualSrc, position, size, imgState]
92
+ );
93
+
94
+ const outerStyle = {
95
+ ...style,
96
+ display: 'inline-block',
97
+ width: width ? `${width}px` : '100%',
98
+ };
99
+
100
+ function loadImg() {
101
+ const img = new Image();
102
+ img.src = src;
103
+ setImgState('loading');
104
+ img.onload = () => {
105
+ setImgState('loaded');
106
+ onSuccess();
107
+ };
108
+ img.onerror = err => {
109
+ setImgState('error');
110
+ onError(err);
111
+ };
112
+ }
113
+
114
+ useEffect(() => {
115
+ if (inView) loadImg();
116
+ }, [inView]);
117
+
118
+ return (
119
+ // paddingTop 要求元素本身的宽度为 100%,所以只能加一个外层元素去限制宽度
120
+ <div ref={ref} style={outerStyle} {...rest}>
121
+ <div className={`image ${className} ${classes.root}`} style={mergedStyle}>
122
+ {!fallback && imgState === 'error' && (
123
+ <div className="image--state" title="loading image">
124
+ <SvgIcon component={Alert} className="image--icon" />
125
+ </div>
126
+ )}
127
+ {!placeholder && imgState === 'loading' && (
128
+ <div className="image--state" title="Image load error">
129
+ <SvgIcon component={ImageIcon} className="image--icon" />
130
+ </div>
131
+ )}
132
+ {imgState === 'loaded' && <img className="image--img" src={src} alt={alt} />}
133
+ </div>
134
+ </div>
135
+ );
136
+ }
137
+
138
+ Img.propTypes = {
139
+ src: PropTypes.string.isRequired,
140
+ alt: PropTypes.string,
141
+ size: PropTypes.string,
142
+ position: PropTypes.string,
143
+ style: PropTypes.object,
144
+ ratio: PropTypes.number,
145
+ repeat: PropTypes.string,
146
+ width: PropTypes.number,
147
+ height: PropTypes.number,
148
+ lazy: PropTypes.bool,
149
+ placeholder: PropTypes.string,
150
+ fallback: PropTypes.string,
151
+ className: PropTypes.string,
152
+ onError: PropTypes.func,
153
+ onSuccess: PropTypes.func,
154
+ };
155
+ Img.defaultProps = {
156
+ alt: null,
157
+ size: 'cover',
158
+ position: 'top center',
159
+ repeat: 'no-repeat',
160
+ style: null,
161
+ ratio: 1,
162
+ width: null,
163
+ height: null,
164
+ lazy: true,
165
+ placeholder: null,
166
+ fallback: null,
167
+ className: '',
168
+ onError: () => {},
169
+ onSuccess: () => {},
170
+ };
171
+
172
+ export default Img;
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import camelCase from 'lodash/camelCase';
4
+ import upperFirst from 'lodash/upperFirst';
5
+ import styled from 'styled-components';
6
+ import Typography from '@material-ui/core/Typography';
7
+
8
+ const InfoRow = ({ name, nameFormatter, layout, children, valueComponent, nameWidth, ...rest }) => (
9
+ <Container layout={layout} width={nameWidth} {...rest}>
10
+ <Typography color="textSecondary" className="info-row__name">
11
+ {nameFormatter(name)}
12
+ </Typography>
13
+
14
+ {children && (
15
+ <Typography color="textPrimary" component={valueComponent} className="info-row__value">
16
+ {children}
17
+ </Typography>
18
+ )}
19
+ </Container>
20
+ );
21
+
22
+ InfoRow.propTypes = {
23
+ name: PropTypes.any.isRequired,
24
+ layout: PropTypes.oneOf(['horizontal', 'vertical']),
25
+ children: PropTypes.any,
26
+ nameFormatter: PropTypes.func,
27
+ valueComponent: PropTypes.string,
28
+ nameWidth: PropTypes.number,
29
+ };
30
+
31
+ InfoRow.defaultProps = {
32
+ children: null,
33
+ nameWidth: 90,
34
+ nameFormatter: name => {
35
+ const resetName = name
36
+ .split(' ')
37
+ .map(x => upperFirst(camelCase(x)))
38
+ .join(' ');
39
+
40
+ return typeof name === 'string' ? resetName : name;
41
+ },
42
+ valueComponent: 'p',
43
+ layout: 'horizontal',
44
+ };
45
+
46
+ const Container = styled.div`
47
+ display: flex;
48
+ flex-direction: ${props => (props.layout === 'vertical' ? 'column' : 'row')};
49
+ justify-content: flex-start;
50
+ align-items: ${props => (props.layout === 'vertical' ? 'flex-start' : 'center')};
51
+ margin-bottom: 16px;
52
+
53
+ .info-row__name {
54
+ width: ${props => props.width}px;
55
+ margin-right: ${props => (props.layout === 'vertical' ? '0' : '8px')};
56
+ margin-bottom: ${props => (props.layout === 'vertical' ? '8px' : '0')};
57
+ text-transform: capitalize;
58
+ }
59
+
60
+ .info-row__value {
61
+ flex: 1;
62
+ overflow: auto;
63
+ word-wrap: break-word;
64
+ font-weight: 500;
65
+ }
66
+
67
+ @media (max-width: ${props => props.theme.breakpoints.values.md}px) {
68
+ flex-direction: column;
69
+ justify-content: flex-start;
70
+ align-items: flex-start;
71
+ .info-row__name {
72
+ font-weight: bold;
73
+ }
74
+
75
+ .info-row__value {
76
+ width: 100%;
77
+ font-size: 12px;
78
+ margin-top: 8px;
79
+ }
80
+ }
81
+ `;
82
+
83
+ export default InfoRow;
@@ -0,0 +1,145 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import styled from 'styled-components';
4
+ import Button from '@material-ui/core/IconButton';
5
+ import AppBar from '@material-ui/core/AppBar';
6
+ import Toolbar from '@material-ui/core/Toolbar';
7
+ import Box from '@material-ui/core/Box';
8
+ import Typography from '@material-ui/core/Typography';
9
+ import MenuIcon from '@material-ui/icons/Menu';
10
+ import { Link } from 'react-router-dom';
11
+
12
+ import Logo from '../../Logo';
13
+
14
+ const StyledAppBar = styled(AppBar)`
15
+ && {
16
+ z-index: ${props => props.theme.zIndex.drawer + 1};
17
+ background: ${props => props.theme.palette.background.default};
18
+ box-shadow: none;
19
+ top: 0;
20
+ height: auto;
21
+ }
22
+ .header-toolbar {
23
+ background: ${props => props.theme.palette.background.default};
24
+ color: ${props => props.theme.palette.text.primary};
25
+ margin: ${props => props.theme.spacing(1)}px 0;
26
+ }
27
+ .header-link {
28
+ display: flex;
29
+ text-decoration: none;
30
+ flex-shrink: 1;
31
+ overflow: hidden;
32
+ }
33
+ .header-logo {
34
+ margin-right: 20px;
35
+ }
36
+ .header-title {
37
+ display: flex;
38
+ flex-direction: column;
39
+ justify-content: center;
40
+ align-items: flex-start;
41
+ }
42
+ .header-title__primary {
43
+ font-size: 24px;
44
+ font-weight: 800;
45
+ color: ${props => props.theme.typography.color.main};
46
+ text-transform: uppercase;
47
+ display: flex;
48
+ align-items: center;
49
+ }
50
+ .header-title__secondary {
51
+ font-size: 14px;
52
+ line-height: 1.71;
53
+ color: ${props => props.theme.typography.color.gray};
54
+ }
55
+
56
+ .header-addons {
57
+ display: flex;
58
+ justify-content: center;
59
+ align-items: center;
60
+ flex-shrink: 9999999;
61
+
62
+ .user-addon {
63
+ .header-avatar {
64
+ width: 32px;
65
+ border-radius: 16px;
66
+ height: auto;
67
+ }
68
+ }
69
+ }
70
+ .header-menu {
71
+ display: none;
72
+ }
73
+ @media (max-width: ${props => props.theme.breakpoints.values.md}px) {
74
+ .header-logo,
75
+ .header-title {
76
+ display: none;
77
+ }
78
+ .header-title__primary {
79
+ font-size: 20px;
80
+ }
81
+ .header-menu {
82
+ display: block;
83
+ }
84
+ }
85
+ `;
86
+
87
+ export default function Header({
88
+ children,
89
+ brand,
90
+ brandAddon,
91
+ description,
92
+ addons,
93
+ onToggleMenu,
94
+ homeUrl,
95
+ ...rest
96
+ }) {
97
+ return (
98
+ <StyledAppBar position="sticky" className="header" {...rest}>
99
+ <Toolbar disableGutters={false} className="header-toolbar">
100
+ <Button
101
+ color="inherit"
102
+ aria-label="open drawer"
103
+ edge="start"
104
+ className="header-menu"
105
+ onClick={onToggleMenu}>
106
+ <MenuIcon />
107
+ </Button>
108
+ <Link to={homeUrl} className="header-link">
109
+ <div className="header-logo">
110
+ <Logo showText={false} />
111
+ </div>
112
+ <div className="header-title">
113
+ <Typography component="h2" noWrap className="header-title__primary">
114
+ {brand}
115
+ {brandAddon}
116
+ </Typography>
117
+ <Typography component="p" noWrap className="header-title__secondary">
118
+ {description}
119
+ </Typography>
120
+ </div>
121
+ </Link>
122
+ <Box flexGrow={1} />
123
+ {!!children && <div className="header-children">{children}</div>}
124
+ <div className="header-addons">{addons}</div>
125
+ </Toolbar>
126
+ </StyledAppBar>
127
+ );
128
+ }
129
+
130
+ Header.propTypes = {
131
+ onToggleMenu: PropTypes.func.isRequired,
132
+ brand: PropTypes.string.isRequired,
133
+ brandAddon: PropTypes.object,
134
+ description: PropTypes.string.isRequired,
135
+ children: PropTypes.any,
136
+ addons: PropTypes.any,
137
+ homeUrl: PropTypes.string,
138
+ };
139
+
140
+ Header.defaultProps = {
141
+ children: null,
142
+ addons: null,
143
+ brandAddon: null,
144
+ homeUrl: '/',
145
+ };
@@ -0,0 +1,140 @@
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 '@material-ui/core/Container';
6
+ import Box from '@material-ui/core/Box';
7
+ import Drawer from '@material-ui/core/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
+ ...rest
63
+ }) {
64
+ const breakpoint = 960;
65
+ const { width } = useWindowSize();
66
+ const [drawerMode, setDrawerMode] = useState(width > breakpoint ? 'permanent' : 'temporary');
67
+ const [drawerOpen, setDrawerOpen] = useState(drawerMode === 'permanent');
68
+
69
+ useEffect(() => {
70
+ const newMode = width > breakpoint ? 'permanent' : 'temporary';
71
+ setDrawerMode(newMode);
72
+ setDrawerOpen(newMode !== 'temporary');
73
+ }, [width]);
74
+
75
+ const onToggleDrawer = () => {
76
+ setDrawerOpen(!drawerOpen);
77
+ };
78
+ const isFullWidth = fullWidth || contentLayout === 'row';
79
+
80
+ return (
81
+ <Wrapper className={`dashboard ${className}`} {...rest}>
82
+ <Helmet title={`${title}-${brand}`} />
83
+
84
+ <Header
85
+ className="dashboard__header"
86
+ onToggleMenu={onToggleDrawer}
87
+ brand={brand}
88
+ brandAddon={brandAddon}
89
+ description={description}
90
+ addons={headerAddon}
91
+ homeUrl={homeUrl}
92
+ />
93
+ <Box display="flex" className="dashboard__body">
94
+ <Drawer
95
+ variant={drawerMode}
96
+ className="drawer"
97
+ classes={{ paper: 'drawerPaper' }}
98
+ open={drawerOpen}
99
+ onClose={onToggleDrawer}
100
+ ModalProps={{ disablePortal: true, keepMounted: true }}>
101
+ <Sidebar className="dashboard__sidebar" images={images} links={links} prefix={prefix} />
102
+ </Drawer>
103
+ <Box className="dashboard__main">
104
+ <Container maxWidth={isFullWidth ? false : 'lg'} className="dashboard__content">
105
+ {children}
106
+ </Container>
107
+ <Footer className="dashboard__footer" />
108
+ </Box>
109
+ </Box>
110
+ </Wrapper>
111
+ );
112
+ }
113
+
114
+ Dashboard.propTypes = {
115
+ children: PropTypes.any.isRequired,
116
+ title: PropTypes.string,
117
+ brand: PropTypes.string.isRequired,
118
+ links: PropTypes.array.isRequired,
119
+ images: PropTypes.object.isRequired,
120
+ brandAddon: PropTypes.object,
121
+ description: PropTypes.string.isRequired,
122
+ headerAddon: PropTypes.any,
123
+ prefix: PropTypes.string,
124
+ // 兼容旧版的设置,新版使用 fullWidth 进行设置
125
+ contentLayout: PropTypes.oneOf(['row', 'column']),
126
+ fullWidth: PropTypes.bool,
127
+ className: PropTypes.string,
128
+ homeUrl: PropTypes.string,
129
+ };
130
+
131
+ Dashboard.defaultProps = {
132
+ title: 'Home',
133
+ contentLayout: 'column',
134
+ headerAddon: null,
135
+ brandAddon: null,
136
+ prefix: '/images',
137
+ fullWidth: false,
138
+ className: '',
139
+ homeUrl: '/',
140
+ };