@arcblock/ux 1.17.14 → 1.17.15
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/Header/header.js +100 -0
- package/lib/Header/index.js +15 -116
- package/lib/Header/responsive-header.js +132 -0
- package/lib/NavMenu/nav-menu.js +73 -51
- package/lib/NavMenu/style.js +28 -0
- package/package.json +4 -4
- package/src/Header/header.js +157 -0
- package/src/Header/index.js +2 -156
- package/src/Header/responsive-header.js +136 -0
- package/src/NavMenu/nav-menu.js +56 -85
- package/src/NavMenu/style.js +182 -0
- package/lib/NavMenu/style.css +0 -48
- package/src/NavMenu/style.css +0 -48
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcblock/ux",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.15",
|
|
4
4
|
"description": "Common used react components for arcblock products",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"react": ">=16.12.0",
|
|
54
54
|
"react-ga": "^2.7.0"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "e51d6944347d5f6df5b43cb31104683ad290926f",
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@arcblock/icons": "^1.17.
|
|
59
|
-
"@arcblock/react-hooks": "^1.17.
|
|
58
|
+
"@arcblock/icons": "^1.17.15",
|
|
59
|
+
"@arcblock/react-hooks": "^1.17.15",
|
|
60
60
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
61
61
|
"@fontsource/lato": "^4.5.3",
|
|
62
62
|
"@material-ui/core": "^4.12.3",
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import Box from '@material-ui/core/Box';
|
|
5
|
+
import Container from '@material-ui/core/Container';
|
|
6
|
+
import useTheme from '@material-ui/core/styles/useTheme';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Header 组件
|
|
10
|
+
* TODO: Layout/dashboard 可以复用此处的 header
|
|
11
|
+
*/
|
|
12
|
+
function Header({
|
|
13
|
+
logo,
|
|
14
|
+
brand,
|
|
15
|
+
brandAddon,
|
|
16
|
+
description,
|
|
17
|
+
children,
|
|
18
|
+
addons,
|
|
19
|
+
prepend,
|
|
20
|
+
containerComponent: ContainerComponent,
|
|
21
|
+
...rest
|
|
22
|
+
}) {
|
|
23
|
+
const theme = useTheme();
|
|
24
|
+
return (
|
|
25
|
+
<Root {...rest} $theme={theme}>
|
|
26
|
+
<ContainerComponent className="header-container">
|
|
27
|
+
{prepend}
|
|
28
|
+
{logo && <div className="header-logo">{logo}</div>}
|
|
29
|
+
<div className="header-brand">
|
|
30
|
+
<div className="header-brand-title">
|
|
31
|
+
<h2>{brand}</h2>
|
|
32
|
+
<div className="header-brand-addon">{brandAddon}</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="header-brand-desc">{description}</div>
|
|
35
|
+
</div>
|
|
36
|
+
{children}
|
|
37
|
+
<Box flexGrow={1} />
|
|
38
|
+
<div className="header-addons">{addons}</div>
|
|
39
|
+
</ContainerComponent>
|
|
40
|
+
</Root>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Header.propTypes = {
|
|
45
|
+
logo: PropTypes.node,
|
|
46
|
+
// 相当于 Title
|
|
47
|
+
brand: PropTypes.string,
|
|
48
|
+
// brand 右侧的内容区域, 可显示一个 badge 或 tag
|
|
49
|
+
brandAddon: PropTypes.node,
|
|
50
|
+
// brand 下方的描述
|
|
51
|
+
description: PropTypes.string,
|
|
52
|
+
children: PropTypes.node,
|
|
53
|
+
// 右侧区域, 可以放置 icons/actions/login 等
|
|
54
|
+
addons: PropTypes.node,
|
|
55
|
+
// logo 左侧内容
|
|
56
|
+
prepend: PropTypes.node,
|
|
57
|
+
// 默认使用 mui 的 Container, 如果不想限制 header 内容区域的宽度, 可以设置为 div
|
|
58
|
+
containerComponent: PropTypes.elementType,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
Header.defaultProps = {
|
|
62
|
+
logo: null,
|
|
63
|
+
brand: null,
|
|
64
|
+
brandAddon: null,
|
|
65
|
+
description: null,
|
|
66
|
+
children: null,
|
|
67
|
+
addons: null,
|
|
68
|
+
prepend: null,
|
|
69
|
+
containerComponent: Container,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const Root = styled.div`
|
|
73
|
+
position: relative;
|
|
74
|
+
z-index: ${props => props.$theme.zIndex.drawer + 1};
|
|
75
|
+
font-size: 14px;
|
|
76
|
+
background: ${props => props.$theme.palette.common.white};
|
|
77
|
+
.header-container {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
height: 64px;
|
|
81
|
+
padding: 8px 16px;
|
|
82
|
+
}
|
|
83
|
+
.header-logo {
|
|
84
|
+
display: inline-flex;
|
|
85
|
+
justify-content: center;
|
|
86
|
+
position: relative;
|
|
87
|
+
height: 40px;
|
|
88
|
+
margin-right: 16px;
|
|
89
|
+
img,
|
|
90
|
+
svg {
|
|
91
|
+
width: auto;
|
|
92
|
+
height: 100%;
|
|
93
|
+
}
|
|
94
|
+
> a {
|
|
95
|
+
height: 100%;
|
|
96
|
+
line-height: 1;
|
|
97
|
+
}
|
|
98
|
+
> a::before {
|
|
99
|
+
position: absolute;
|
|
100
|
+
top: 0;
|
|
101
|
+
right: 0;
|
|
102
|
+
bottom: 0;
|
|
103
|
+
left: 0;
|
|
104
|
+
background-color: transparent;
|
|
105
|
+
content: '';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
.header-brand {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
flex-shrink: 0;
|
|
112
|
+
margin-right: 16px;
|
|
113
|
+
.header-brand-title {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
h2 {
|
|
117
|
+
font-size: 16px;
|
|
118
|
+
}
|
|
119
|
+
.header-brand-addon {
|
|
120
|
+
margin-left: 8px;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
.header-brand-desc {
|
|
124
|
+
color: #9397a1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
.header-addons {
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
}
|
|
131
|
+
${props => props.$theme.breakpoints.down('md')} {
|
|
132
|
+
.header-brand {
|
|
133
|
+
margin-right: 12px;
|
|
134
|
+
.header-brand-title {
|
|
135
|
+
h2 {
|
|
136
|
+
font-size: 14px;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
${props => props.$theme.breakpoints.down('sm')} {
|
|
142
|
+
.header-container {
|
|
143
|
+
height: 56px;
|
|
144
|
+
}
|
|
145
|
+
.header-menu {
|
|
146
|
+
display: inline-block;
|
|
147
|
+
}
|
|
148
|
+
.header-logo {
|
|
149
|
+
height: 32px;
|
|
150
|
+
}
|
|
151
|
+
.header-brand {
|
|
152
|
+
display: none;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
156
|
+
|
|
157
|
+
export default Header;
|
package/src/Header/index.js
CHANGED
|
@@ -1,156 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import styled from 'styled-components';
|
|
4
|
-
import Box from '@material-ui/core/Box';
|
|
5
|
-
import useTheme from '@material-ui/core/styles/useTheme';
|
|
6
|
-
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
|
7
|
-
import MenuIcon from '@material-ui/icons/Menu';
|
|
8
|
-
import Button from '@material-ui/core/IconButton';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Header 组件
|
|
12
|
-
* TODO: Layout/dashboard 可以复用此处的 header
|
|
13
|
-
*/
|
|
14
|
-
function Header({
|
|
15
|
-
logo,
|
|
16
|
-
brand,
|
|
17
|
-
brandAddon,
|
|
18
|
-
description,
|
|
19
|
-
children,
|
|
20
|
-
addons,
|
|
21
|
-
mobile,
|
|
22
|
-
onToggleMenu,
|
|
23
|
-
...rest
|
|
24
|
-
}) {
|
|
25
|
-
const theme = useTheme();
|
|
26
|
-
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
|
27
|
-
const _mobile = { ...Header.defaultProps.mobile, ...mobile };
|
|
28
|
-
// 暂时不要通过 display: none 隐藏 logo, https://blog.patw.me/archives/1820/inline-svg-same-id-and-display-none-issue/
|
|
29
|
-
const hideLogo = isMobile && _mobile.hideLogo;
|
|
30
|
-
return (
|
|
31
|
-
<Root {...rest} $theme={theme}>
|
|
32
|
-
{_mobile.enableMenu && (
|
|
33
|
-
<Button color="inherit" edge="start" className="header-menu" onClick={onToggleMenu}>
|
|
34
|
-
<MenuIcon />
|
|
35
|
-
</Button>
|
|
36
|
-
)}
|
|
37
|
-
{!hideLogo && <div className="header-logo">{logo}</div>}
|
|
38
|
-
<div className="header-brand">
|
|
39
|
-
<div className="header-brand-title">
|
|
40
|
-
<h2>{brand}</h2>
|
|
41
|
-
<div className="header-brand-addon">{brandAddon}</div>
|
|
42
|
-
</div>
|
|
43
|
-
<div className="header-brand-desc">{description}</div>
|
|
44
|
-
</div>
|
|
45
|
-
{children}
|
|
46
|
-
<Box flexGrow={1} />
|
|
47
|
-
<div className="header-addons">{addons}</div>
|
|
48
|
-
</Root>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
Header.propTypes = {
|
|
53
|
-
logo: PropTypes.any,
|
|
54
|
-
// 相当于 Title
|
|
55
|
-
brand: PropTypes.string,
|
|
56
|
-
// brand 右侧的内容区域, 可显示一个 badge 或 tag
|
|
57
|
-
brandAddon: PropTypes.any,
|
|
58
|
-
// brand 下方的描述
|
|
59
|
-
description: PropTypes.string,
|
|
60
|
-
children: PropTypes.any,
|
|
61
|
-
// 右侧区域, 可以放置 icons/actions/login 等
|
|
62
|
-
addons: PropTypes.any,
|
|
63
|
-
// 窄屏下配置
|
|
64
|
-
mobile: PropTypes.shape({
|
|
65
|
-
enableMenu: PropTypes.bool,
|
|
66
|
-
hideLogo: PropTypes.bool,
|
|
67
|
-
}),
|
|
68
|
-
onToggleMenu: PropTypes.func,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
Header.defaultProps = {
|
|
72
|
-
logo: null,
|
|
73
|
-
brand: null,
|
|
74
|
-
brandAddon: null,
|
|
75
|
-
description: null,
|
|
76
|
-
children: null,
|
|
77
|
-
addons: null,
|
|
78
|
-
mobile: { enableMenu: true, hideLogo: true },
|
|
79
|
-
onToggleMenu: () => {},
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const Root = styled.div`
|
|
83
|
-
display: flex;
|
|
84
|
-
align-items: center;
|
|
85
|
-
position: relative;
|
|
86
|
-
z-index: ${props => props.$theme.zIndex.drawer + 1};
|
|
87
|
-
height: 64px;
|
|
88
|
-
padding: 12px 24px;
|
|
89
|
-
font-size: 14px;
|
|
90
|
-
background: #fff;
|
|
91
|
-
.header-menu {
|
|
92
|
-
display: none;
|
|
93
|
-
}
|
|
94
|
-
.header-logo {
|
|
95
|
-
display: inline-flex;
|
|
96
|
-
justify-content: center;
|
|
97
|
-
position: relative;
|
|
98
|
-
height: 40px;
|
|
99
|
-
margin-right: 16px;
|
|
100
|
-
img,
|
|
101
|
-
svg {
|
|
102
|
-
width: auto;
|
|
103
|
-
height: 100%;
|
|
104
|
-
}
|
|
105
|
-
> a {
|
|
106
|
-
height: 100%;
|
|
107
|
-
line-height: 1;
|
|
108
|
-
}
|
|
109
|
-
> a::before {
|
|
110
|
-
position: absolute;
|
|
111
|
-
top: 0;
|
|
112
|
-
right: 0;
|
|
113
|
-
bottom: 0;
|
|
114
|
-
left: 0;
|
|
115
|
-
background-color: transparent;
|
|
116
|
-
content: '';
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
.header-brand {
|
|
120
|
-
display: flex;
|
|
121
|
-
flex-direction: column;
|
|
122
|
-
margin-right: 16px;
|
|
123
|
-
.header-brand-title {
|
|
124
|
-
display: flex;
|
|
125
|
-
align-items: center;
|
|
126
|
-
h2 {
|
|
127
|
-
font-size: 16px;
|
|
128
|
-
}
|
|
129
|
-
.header-brand-addon {
|
|
130
|
-
margin-left: 8px;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
.header-brand-desc {
|
|
134
|
-
color: #9397a1;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
.header-addons {
|
|
138
|
-
display: flex;
|
|
139
|
-
align-items: center;
|
|
140
|
-
}
|
|
141
|
-
${props => props.$theme.breakpoints.down('sm')} {
|
|
142
|
-
height: 56px;
|
|
143
|
-
padding: 8px 16px;
|
|
144
|
-
.header-menu {
|
|
145
|
-
display: inline-block;
|
|
146
|
-
}
|
|
147
|
-
.header-logo {
|
|
148
|
-
height: 32px;
|
|
149
|
-
}
|
|
150
|
-
.header-brand {
|
|
151
|
-
display: none;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
`;
|
|
155
|
-
|
|
156
|
-
export default Header;
|
|
1
|
+
export { default as Header } from './header';
|
|
2
|
+
export { default as ResponsiveHeader } from './responsive-header';
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import MenuIcon from '@material-ui/icons/Menu';
|
|
5
|
+
import Button from '@material-ui/core/IconButton';
|
|
6
|
+
import useTheme from '@material-ui/core/styles/useTheme';
|
|
7
|
+
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
|
8
|
+
import Drawer from '@material-ui/core/Drawer';
|
|
9
|
+
import Header from './header';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ResponsiveHeader
|
|
13
|
+
* - 窄屏下显示 burge menu
|
|
14
|
+
* - 窄屏下将 children 区域显示到 menu 中
|
|
15
|
+
*
|
|
16
|
+
* 注意: 暂时不要通过 display: none 隐藏 logo, https://blog.patw.me/archives/1820/inline-svg-same-id-and-display-none-issue/
|
|
17
|
+
*/
|
|
18
|
+
function ResponsiveHeader({ menu, prepend, children, ...rest }) {
|
|
19
|
+
const theme = useTheme();
|
|
20
|
+
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
|
21
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
22
|
+
const _children =
|
|
23
|
+
typeof children === 'function'
|
|
24
|
+
? children({ isMobile, closeMenu: () => setDrawerOpen(false) })
|
|
25
|
+
: children;
|
|
26
|
+
const { logo, brand, brandAddon, description } = rest;
|
|
27
|
+
// 如果 children 没有值, 则使用普通的 Header 组件渲染 (此时并没有什么内容需要在 menu 中显示)
|
|
28
|
+
if (!children) {
|
|
29
|
+
return <Header prepend={prepend} {...rest} />;
|
|
30
|
+
}
|
|
31
|
+
return (
|
|
32
|
+
<Root
|
|
33
|
+
prepend={
|
|
34
|
+
prepend || (
|
|
35
|
+
<Button
|
|
36
|
+
color="inherit"
|
|
37
|
+
edge="start"
|
|
38
|
+
className="header-menu"
|
|
39
|
+
onClick={() => setDrawerOpen(true)}>
|
|
40
|
+
<MenuIcon />
|
|
41
|
+
</Button>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
{...rest}
|
|
45
|
+
logo={isMobile ? null : logo}
|
|
46
|
+
$theme={theme}>
|
|
47
|
+
{!isMobile && _children}
|
|
48
|
+
{isMobile && (
|
|
49
|
+
<Drawer
|
|
50
|
+
open={drawerOpen}
|
|
51
|
+
onClose={() => setDrawerOpen(false)}
|
|
52
|
+
ModalProps={{ disablePortal: true, keepMounted: true }}>
|
|
53
|
+
<Sidebar>
|
|
54
|
+
<div className="header-sidebar-head">
|
|
55
|
+
<div className="header-sidebar-logo">{logo}</div>
|
|
56
|
+
{brand && <h2>{brand}</h2>}
|
|
57
|
+
{description && <p className="header-sidebar-description">{description}</p>}
|
|
58
|
+
{brandAddon && <div className="header-sidebar-brandaddon">{brandAddon}</div>}
|
|
59
|
+
</div>
|
|
60
|
+
<div className="header-sidebar-content">{_children}</div>
|
|
61
|
+
</Sidebar>
|
|
62
|
+
</Drawer>
|
|
63
|
+
)}
|
|
64
|
+
</Root>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
ResponsiveHeader.propTypes = {
|
|
69
|
+
...Header.PropTypes,
|
|
70
|
+
// 如果是 function, 则
|
|
71
|
+
// - 会传入一个 isMobile 参数, isMobile 为 true 时, 表示 children 会显示在 menu 中, 可以根据 isMobile 参数调整要渲染的内容, 比如如果 isMobile 为 true 则使用 inline 模式的 NavMenu (适用于移动端)
|
|
72
|
+
// - 会传入一个 closeMenu 参数, 可以关闭 menu
|
|
73
|
+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
ResponsiveHeader.defaultProps = {
|
|
77
|
+
...Header.defaultProps,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const Root = styled(Header)`
|
|
81
|
+
.header-menu {
|
|
82
|
+
display: none;
|
|
83
|
+
}
|
|
84
|
+
${props => props.$theme.breakpoints.down('sm')} {
|
|
85
|
+
.header-menu {
|
|
86
|
+
display: block;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Sidebar
|
|
93
|
+
*/
|
|
94
|
+
const Sidebar = styled.div`
|
|
95
|
+
min-width: 280px;
|
|
96
|
+
font-size: 14px;
|
|
97
|
+
.header-sidebar-head {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
align-items: center;
|
|
101
|
+
padding: 24px 0;
|
|
102
|
+
border-bottom: 1px solid #eee;
|
|
103
|
+
font-size: 12px;
|
|
104
|
+
.header-sidebar-logo {
|
|
105
|
+
min-width: 44px;
|
|
106
|
+
height: 44px;
|
|
107
|
+
font-size: 44px;
|
|
108
|
+
> * {
|
|
109
|
+
width: auto;
|
|
110
|
+
height: 100%;
|
|
111
|
+
}
|
|
112
|
+
> a {
|
|
113
|
+
display: block;
|
|
114
|
+
}
|
|
115
|
+
img {
|
|
116
|
+
max-width: 100%;
|
|
117
|
+
max-height: 100%;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
h2 {
|
|
121
|
+
margin-top: 12px;
|
|
122
|
+
font-size: 14px;
|
|
123
|
+
}
|
|
124
|
+
.header-sidebar-description {
|
|
125
|
+
margin: 2px 0 0 0;
|
|
126
|
+
}
|
|
127
|
+
.header-sidebar-brandaddon {
|
|
128
|
+
margin-top: 8px;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
.header-sidebar-content {
|
|
132
|
+
padding: 16px 0;
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
export default ResponsiveHeader;
|