@blocklet/ui-react 1.17.13
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/LICENSE +13 -0
- package/README.md +87 -0
- package/lib/Footer/index.js +88 -0
- package/lib/Header/index.js +182 -0
- package/package.json +59 -0
- package/src/Footer/index.js +100 -0
- package/src/Header/index.js +134 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2018-2022 ArcBlock
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
> Some useful React components that can be used in Blocklets.
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @blocklet/ui-react
|
|
9
|
+
|
|
10
|
+
# or
|
|
11
|
+
yarn add @blocklet/ui-react
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 使用
|
|
15
|
+
|
|
16
|
+
### Header/Footer
|
|
17
|
+
|
|
18
|
+
```jsx
|
|
19
|
+
import Header from '@blocklet/ui-react/lib/Header';
|
|
20
|
+
import Footer from '@blocklet/ui-react/lib/Footer';
|
|
21
|
+
|
|
22
|
+
function App() {
|
|
23
|
+
return (
|
|
24
|
+
<div>
|
|
25
|
+
<Header />
|
|
26
|
+
...
|
|
27
|
+
<Footer />
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`<Header />` 或 `<Footer />` 会获取 blocklet meta (`window.blocklet`) 中的配置信息, 包括 app logo, app name, navigation links 等,然后基于这些配置信息渲染一个 page header 或 page footer。
|
|
34
|
+
|
|
35
|
+
如果 `<Header />` 上层存在 Session Context Provider,那么 Header 组件会在右侧区域自动渲染一个用户身份信息的按钮 (SessionManager)。
|
|
36
|
+
类似的,如果 `<Header />` 上层存在 Locale Context Provider,那么 Header 组件会在右侧区域自动渲染一个语言选择器按钮 (LocaleSelector)。
|
|
37
|
+
|
|
38
|
+
也可以显式地传入 blocklet meta:
|
|
39
|
+
|
|
40
|
+
```jsx
|
|
41
|
+
const blockletMeta = {
|
|
42
|
+
appName: 'Blocklet Name',
|
|
43
|
+
appLogo: 'logo url',
|
|
44
|
+
theme: {
|
|
45
|
+
background: '#cfefe8',
|
|
46
|
+
},
|
|
47
|
+
navigation: [
|
|
48
|
+
{
|
|
49
|
+
title: 'Home',
|
|
50
|
+
link: '/',
|
|
51
|
+
},
|
|
52
|
+
...
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function App() {
|
|
57
|
+
return (
|
|
58
|
+
<div>
|
|
59
|
+
<Header blockletMeta={blockletMeta} />
|
|
60
|
+
...
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
定制 Header 右侧 addons 区域:
|
|
67
|
+
|
|
68
|
+
```jsx
|
|
69
|
+
<Header
|
|
70
|
+
blockletMeta={meta}
|
|
71
|
+
addons={addons => {
|
|
72
|
+
return [
|
|
73
|
+
<Button
|
|
74
|
+
variant="contained"
|
|
75
|
+
color="primary"
|
|
76
|
+
size="small"
|
|
77
|
+
style={{ marginRight: 8 }}>
|
|
78
|
+
Addons
|
|
79
|
+
</Button>,
|
|
80
|
+
<Button color="primary" size="small" style={{ marginRight: 8 }}>
|
|
81
|
+
Addons
|
|
82
|
+
</Button>,
|
|
83
|
+
...addons,
|
|
84
|
+
];
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
```
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = Footer;
|
|
7
|
+
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
|
|
10
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
11
|
+
|
|
12
|
+
var _styledComponents = _interopRequireDefault(require("styled-components"));
|
|
13
|
+
|
|
14
|
+
var _useTheme = _interopRequireDefault(require("@material-ui/core/styles/useTheme"));
|
|
15
|
+
|
|
16
|
+
var _Container = _interopRequireDefault(require("@material-ui/core/Container"));
|
|
17
|
+
|
|
18
|
+
var _NavMenu = _interopRequireDefault(require("@arcblock/ux/lib/NavMenu"));
|
|
19
|
+
|
|
20
|
+
const _excluded = ["blockletMeta"];
|
|
21
|
+
|
|
22
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
23
|
+
|
|
24
|
+
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
25
|
+
|
|
26
|
+
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 专门用于 (composable) blocklet 的 Footer 组件, 基于 blocklet meta 中的数据渲染
|
|
30
|
+
*/
|
|
31
|
+
function Footer(_ref) {
|
|
32
|
+
let {
|
|
33
|
+
blockletMeta
|
|
34
|
+
} = _ref,
|
|
35
|
+
rest = _objectWithoutProperties(_ref, _excluded);
|
|
36
|
+
|
|
37
|
+
const theme = (0, _useTheme.default)(); // eslint-disable-next-line no-param-reassign
|
|
38
|
+
|
|
39
|
+
blockletMeta = blockletMeta || window.blocklet;
|
|
40
|
+
const {
|
|
41
|
+
appLogo,
|
|
42
|
+
appName,
|
|
43
|
+
theme: blockletTheme,
|
|
44
|
+
navigation
|
|
45
|
+
} = blockletMeta;
|
|
46
|
+
return /*#__PURE__*/_react.default.createElement(Root, Object.assign({}, rest, {
|
|
47
|
+
$bgcolor: blockletTheme === null || blockletTheme === void 0 ? void 0 : blockletTheme.background,
|
|
48
|
+
$theme: theme
|
|
49
|
+
}), /*#__PURE__*/_react.default.createElement(_Container.default, null, /*#__PURE__*/_react.default.createElement("div", {
|
|
50
|
+
className: "footer_line"
|
|
51
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
52
|
+
className: "footer_brand"
|
|
53
|
+
}, /*#__PURE__*/_react.default.createElement("img", {
|
|
54
|
+
src: appLogo,
|
|
55
|
+
alt: "logo"
|
|
56
|
+
}), /*#__PURE__*/_react.default.createElement("span", null, appName)), /*#__PURE__*/_react.default.createElement("div", {
|
|
57
|
+
className: "footer_nav"
|
|
58
|
+
}, !!(navigation !== null && navigation !== void 0 && navigation.length) && /*#__PURE__*/_react.default.createElement(_NavMenu.default, {
|
|
59
|
+
items: navigation.filter(item => item.link).map(item => ({
|
|
60
|
+
label: /*#__PURE__*/_react.default.createElement("a", {
|
|
61
|
+
href: item.link
|
|
62
|
+
}, item.title)
|
|
63
|
+
}))
|
|
64
|
+
}))), /*#__PURE__*/_react.default.createElement("div", {
|
|
65
|
+
className: "footer_line"
|
|
66
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
67
|
+
className: "footer_copyright"
|
|
68
|
+
}, /*#__PURE__*/_react.default.createElement("span", null, "Powered by Blocklet Server")), /*#__PURE__*/_react.default.createElement("div", {
|
|
69
|
+
className: "footer_extra"
|
|
70
|
+
}))));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
Footer.propTypes = {
|
|
74
|
+
blockletMeta: _propTypes.default.object
|
|
75
|
+
};
|
|
76
|
+
Footer.defaultProps = {
|
|
77
|
+
blockletMeta: null
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const Root = _styledComponents.default.div.withConfig({
|
|
81
|
+
displayName: "Footer__Root",
|
|
82
|
+
componentId: "sc-1iwl1nd-0"
|
|
83
|
+
})(["padding:32px 40px;border-top:1px solid #eee;color:#9397a1;", " .footer_line{display:flex;justify-content:space-between;}.footer_line + .footer_line{margin-top:32px;}.footer_brand{display:flex;align-items:center;color:#777;img{height:36px;}span{margin-left:8px;font-size:20px;}}.footer_nav{> *{background:transparent;}}", "{.footer_line{flex-direction:column;}.footer_line + .footer_line{margin-top:0;}.footer_brand{img{height:24px;}span{font-size:16px;}}}"], _ref2 => {
|
|
84
|
+
let {
|
|
85
|
+
$bgcolor
|
|
86
|
+
} = _ref2;
|
|
87
|
+
return $bgcolor && "background-color: ".concat($bgcolor, ";");
|
|
88
|
+
}, props => props.$theme.breakpoints.down('sm'));
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = Header;
|
|
7
|
+
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
|
|
10
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
11
|
+
|
|
12
|
+
var _styledComponents = _interopRequireDefault(require("styled-components"));
|
|
13
|
+
|
|
14
|
+
var _Header = _interopRequireDefault(require("@arcblock/ux/lib/Header"));
|
|
15
|
+
|
|
16
|
+
var _NavMenu = _interopRequireDefault(require("@arcblock/ux/lib/NavMenu"));
|
|
17
|
+
|
|
18
|
+
var _Session = require("@arcblock/did-connect/lib/Session");
|
|
19
|
+
|
|
20
|
+
var _SessionManager = _interopRequireDefault(require("@arcblock/did-connect/lib/SessionManager"));
|
|
21
|
+
|
|
22
|
+
var _selector = _interopRequireDefault(require("@arcblock/ux/lib/Locale/selector"));
|
|
23
|
+
|
|
24
|
+
var _context = require("@arcblock/ux/lib/Locale/context");
|
|
25
|
+
|
|
26
|
+
const _excluded = ["blockletMeta", "addons"];
|
|
27
|
+
|
|
28
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
29
|
+
|
|
30
|
+
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
31
|
+
|
|
32
|
+
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
33
|
+
|
|
34
|
+
const isLinkActive = link => {
|
|
35
|
+
if (window.location.pathname === '/') {
|
|
36
|
+
return link === '/';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return link && link.indexOf(window.location.pathname) > -1;
|
|
40
|
+
}; // blocklet meta 中的 navigation 数据 => NavMenu 组件的 items
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
const parseNavigation = navigation => {
|
|
44
|
+
const parseItem = item => {
|
|
45
|
+
if (item.items) {
|
|
46
|
+
return {
|
|
47
|
+
label: item.title,
|
|
48
|
+
children: item.items.map(parseItem)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let props = {}; // 临时处理: https://github.com/ArcBlock/blocklet-server/issues/4717
|
|
53
|
+
|
|
54
|
+
item.link = item.link.replace('/https:/', 'https://');
|
|
55
|
+
|
|
56
|
+
if (item.link.startsWith('http')) {
|
|
57
|
+
props = {
|
|
58
|
+
target: '_blank',
|
|
59
|
+
rel: 'noreferrer'
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
label: /*#__PURE__*/_react.default.createElement("a", Object.assign({
|
|
65
|
+
href: item.link
|
|
66
|
+
}, props), item.title),
|
|
67
|
+
active: isLinkActive(item.link)
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const items = navigation.map(parseItem);
|
|
72
|
+
return items;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* 专门用于 (composable) blocklet 的 Header 组件, 解析 blocklet meta 中的数据, 通过组合 UX 中的 Header & NavMenu 组件来渲染
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
function Header(_ref) {
|
|
80
|
+
let {
|
|
81
|
+
blockletMeta,
|
|
82
|
+
addons
|
|
83
|
+
} = _ref,
|
|
84
|
+
rest = _objectWithoutProperties(_ref, _excluded);
|
|
85
|
+
|
|
86
|
+
const sessionInfo = _react.default.useContext(_Session.SessionContext);
|
|
87
|
+
|
|
88
|
+
const {
|
|
89
|
+
locale
|
|
90
|
+
} = (0, _context.useLocaleContext)() || {}; // eslint-disable-next-line no-param-reassign
|
|
91
|
+
|
|
92
|
+
blockletMeta = blockletMeta || window.blocklet;
|
|
93
|
+
|
|
94
|
+
if (!blockletMeta) {
|
|
95
|
+
console.warn('blocklet meta is not found.');
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const {
|
|
100
|
+
appLogo,
|
|
101
|
+
appName,
|
|
102
|
+
theme: blockletTheme,
|
|
103
|
+
navigation = [],
|
|
104
|
+
enableConnect = true,
|
|
105
|
+
enableLocale = true
|
|
106
|
+
} = blockletMeta;
|
|
107
|
+
const navItems = parseNavigation(navigation);
|
|
108
|
+
|
|
109
|
+
const renderAddons = () => {
|
|
110
|
+
// 不关心内置的 session manager 和 locale selector, 直接覆盖 UX Header 的 addons
|
|
111
|
+
if (addons && typeof addons !== 'function') {
|
|
112
|
+
return addons;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let addonsArray = []; // 启用了多语言并且检测到了 locale context
|
|
116
|
+
|
|
117
|
+
if (enableLocale && locale) {
|
|
118
|
+
addonsArray.push( /*#__PURE__*/_react.default.createElement(_selector.default, {
|
|
119
|
+
showText: false
|
|
120
|
+
}));
|
|
121
|
+
} // 启用了连接钱包并且检测到了 session context
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if (enableConnect && sessionInfo) {
|
|
125
|
+
addonsArray.push( /*#__PURE__*/_react.default.createElement(_SessionManager.default, {
|
|
126
|
+
session: sessionInfo.session,
|
|
127
|
+
showRole: true
|
|
128
|
+
}));
|
|
129
|
+
} // 在内置 addons 基础上定制 addons
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if (typeof addons === 'function') {
|
|
133
|
+
addonsArray = addons(addonsArray) || [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const addonsFragment = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, ...addonsArray);
|
|
137
|
+
|
|
138
|
+
return addonsFragment;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return /*#__PURE__*/_react.default.createElement(StyledUxHeader, Object.assign({
|
|
142
|
+
logo: /*#__PURE__*/_react.default.createElement("a", {
|
|
143
|
+
href: "/"
|
|
144
|
+
}, /*#__PURE__*/_react.default.createElement("img", {
|
|
145
|
+
src: appLogo,
|
|
146
|
+
alt: "logo"
|
|
147
|
+
})),
|
|
148
|
+
brand: appName,
|
|
149
|
+
mobile: {
|
|
150
|
+
enableMenu: false,
|
|
151
|
+
hideLogo: false
|
|
152
|
+
},
|
|
153
|
+
addons: renderAddons()
|
|
154
|
+
}, rest, {
|
|
155
|
+
$bgcolor: blockletTheme === null || blockletTheme === void 0 ? void 0 : blockletTheme.background
|
|
156
|
+
}), !!(navItems !== null && navItems !== void 0 && navItems.length) && /*#__PURE__*/_react.default.createElement(_NavMenu.default, {
|
|
157
|
+
className: "header-nav",
|
|
158
|
+
items: navItems,
|
|
159
|
+
bgColor: "transparent"
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Header.propTypes = {
|
|
164
|
+
blockletMeta: _propTypes.default.object,
|
|
165
|
+
// 需要考虑 定制的 addons 与内置的 连接钱包/选择语言 addons 共存的情况
|
|
166
|
+
// - PropTypes.func: 可以把自定义 addons 插在 session-manager 或 locale-selector (如果存在的话) 前/中/后
|
|
167
|
+
// - PropTypes.node: 将 addons 原样传给 UX Header 组件
|
|
168
|
+
addons: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.node])
|
|
169
|
+
};
|
|
170
|
+
Header.defaultProps = {
|
|
171
|
+
blockletMeta: null,
|
|
172
|
+
addons: null
|
|
173
|
+
};
|
|
174
|
+
const StyledUxHeader = (0, _styledComponents.default)(_Header.default).withConfig({
|
|
175
|
+
displayName: "Header__StyledUxHeader",
|
|
176
|
+
componentId: "sc-kcf4st-0"
|
|
177
|
+
})(["", " .header-logo{min-width:40px;}.header-nav{.navmenu-sub .navmenu-item{min-width:80px;}}"], _ref2 => {
|
|
178
|
+
let {
|
|
179
|
+
$bgcolor
|
|
180
|
+
} = _ref2;
|
|
181
|
+
return $bgcolor && "background-color: ".concat($bgcolor, ";");
|
|
182
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@blocklet/ui-react",
|
|
3
|
+
"version": "1.17.13",
|
|
4
|
+
"description": "Some useful front-end web components that can be used in Blocklets.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"arcblock",
|
|
8
|
+
"component",
|
|
9
|
+
"blocklet"
|
|
10
|
+
],
|
|
11
|
+
"author": "wangshijun<wangshijun2010@gmail.com>",
|
|
12
|
+
"homepage": "https://github.com/ArcBlock/ux#readme",
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"lib",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/ArcBlock/ux.git"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"lint": "eslint src stories tests",
|
|
25
|
+
"build": "babel src --out-dir lib --copy-files",
|
|
26
|
+
"watch": "babel src --out-dir lib -w --copy-files",
|
|
27
|
+
"precommit": "CI=1 yarn test",
|
|
28
|
+
"prepush": "CI=1 yarn test",
|
|
29
|
+
"prepublish": "npm run build",
|
|
30
|
+
"test": "npm run lint && node tools/jest.js",
|
|
31
|
+
"coverage": "npm run lint && yarn test -- --coverage"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/ArcBlock/ux/issues"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@arcblock/did-connect": "^1.17.13",
|
|
38
|
+
"@arcblock/ux": "^1.17.13",
|
|
39
|
+
"@material-ui/core": "^4.12.3",
|
|
40
|
+
"core-js": "^3.6.4",
|
|
41
|
+
"styled-components": "^5.0.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"react": ">=16.12.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@babel/cli": "^7.8.4",
|
|
51
|
+
"@babel/core": "^7.8.4",
|
|
52
|
+
"@babel/preset-env": "^7.8.4",
|
|
53
|
+
"@babel/preset-react": "^7.8.3",
|
|
54
|
+
"babel-plugin-styled-components": "^1.10.7",
|
|
55
|
+
"eslint-plugin-react-hooks": "^4.2.0",
|
|
56
|
+
"jest": "^24.1.0"
|
|
57
|
+
},
|
|
58
|
+
"gitHead": "da903274c188ed5dbb9e318b1495af6faa5ce668"
|
|
59
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import useTheme from '@material-ui/core/styles/useTheme';
|
|
5
|
+
import Container from '@material-ui/core/Container';
|
|
6
|
+
import NavMenu from '@arcblock/ux/lib/NavMenu';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 专门用于 (composable) blocklet 的 Footer 组件, 基于 blocklet meta 中的数据渲染
|
|
10
|
+
*/
|
|
11
|
+
export default function Footer({ blockletMeta, ...rest }) {
|
|
12
|
+
const theme = useTheme();
|
|
13
|
+
// eslint-disable-next-line no-param-reassign
|
|
14
|
+
blockletMeta = blockletMeta || window.blocklet;
|
|
15
|
+
const { appLogo, appName, theme: blockletTheme, navigation } = blockletMeta;
|
|
16
|
+
return (
|
|
17
|
+
<Root {...rest} $bgcolor={blockletTheme?.background} $theme={theme}>
|
|
18
|
+
<Container>
|
|
19
|
+
<div className="footer_line">
|
|
20
|
+
<div className="footer_brand">
|
|
21
|
+
<img src={appLogo} alt="logo" />
|
|
22
|
+
<span>{appName}</span>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="footer_nav">
|
|
25
|
+
{!!navigation?.length && (
|
|
26
|
+
<NavMenu
|
|
27
|
+
items={navigation
|
|
28
|
+
.filter(item => item.link)
|
|
29
|
+
.map(item => ({
|
|
30
|
+
label: <a href={item.link}>{item.title}</a>,
|
|
31
|
+
}))}
|
|
32
|
+
/>
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div className="footer_line">
|
|
37
|
+
<div className="footer_copyright">
|
|
38
|
+
<span>Powered by Blocklet Server</span>
|
|
39
|
+
</div>
|
|
40
|
+
<div className="footer_extra" />
|
|
41
|
+
</div>
|
|
42
|
+
</Container>
|
|
43
|
+
</Root>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Footer.propTypes = {
|
|
48
|
+
blockletMeta: PropTypes.object,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
Footer.defaultProps = {
|
|
52
|
+
blockletMeta: null,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const Root = styled.div`
|
|
56
|
+
padding: 32px 40px;
|
|
57
|
+
border-top: 1px solid #eee;
|
|
58
|
+
color: #9397a1;
|
|
59
|
+
${({ $bgcolor }) => $bgcolor && `background-color: ${$bgcolor};`}
|
|
60
|
+
.footer_line {
|
|
61
|
+
display: flex;
|
|
62
|
+
justify-content: space-between;
|
|
63
|
+
}
|
|
64
|
+
.footer_line + .footer_line {
|
|
65
|
+
margin-top: 32px;
|
|
66
|
+
}
|
|
67
|
+
.footer_brand {
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
color: #777;
|
|
71
|
+
img {
|
|
72
|
+
height: 36px;
|
|
73
|
+
}
|
|
74
|
+
span {
|
|
75
|
+
margin-left: 8px;
|
|
76
|
+
font-size: 20px;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
.footer_nav {
|
|
80
|
+
> * {
|
|
81
|
+
background: transparent;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
${props => props.$theme.breakpoints.down('sm')} {
|
|
85
|
+
.footer_line {
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
}
|
|
88
|
+
.footer_line + .footer_line {
|
|
89
|
+
margin-top: 0;
|
|
90
|
+
}
|
|
91
|
+
.footer_brand {
|
|
92
|
+
img {
|
|
93
|
+
height: 24px;
|
|
94
|
+
}
|
|
95
|
+
span {
|
|
96
|
+
font-size: 16px;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import UxHeader from '@arcblock/ux/lib/Header';
|
|
5
|
+
import NavMenu from '@arcblock/ux/lib/NavMenu';
|
|
6
|
+
import { SessionContext } from '@arcblock/did-connect/lib/Session';
|
|
7
|
+
import SessionManager from '@arcblock/did-connect/lib/SessionManager';
|
|
8
|
+
import LocaleSelector from '@arcblock/ux/lib/Locale/selector';
|
|
9
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
10
|
+
|
|
11
|
+
const isLinkActive = link => {
|
|
12
|
+
if (window.location.pathname === '/') {
|
|
13
|
+
return link === '/';
|
|
14
|
+
}
|
|
15
|
+
return link && link.indexOf(window.location.pathname) > -1;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// blocklet meta 中的 navigation 数据 => NavMenu 组件的 items
|
|
19
|
+
const parseNavigation = navigation => {
|
|
20
|
+
const parseItem = item => {
|
|
21
|
+
if (item.items) {
|
|
22
|
+
return {
|
|
23
|
+
label: item.title,
|
|
24
|
+
children: item.items.map(parseItem),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
let props = {};
|
|
28
|
+
// 临时处理: https://github.com/ArcBlock/blocklet-server/issues/4717
|
|
29
|
+
item.link = item.link.replace('/https:/', 'https://');
|
|
30
|
+
if (item.link.startsWith('http')) {
|
|
31
|
+
props = {
|
|
32
|
+
target: '_blank',
|
|
33
|
+
rel: 'noreferrer',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
label: (
|
|
38
|
+
<a href={item.link} {...props}>
|
|
39
|
+
{item.title}
|
|
40
|
+
</a>
|
|
41
|
+
),
|
|
42
|
+
active: isLinkActive(item.link),
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
const items = navigation.map(parseItem);
|
|
46
|
+
return items;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 专门用于 (composable) blocklet 的 Header 组件, 解析 blocklet meta 中的数据, 通过组合 UX 中的 Header & NavMenu 组件来渲染
|
|
51
|
+
*/
|
|
52
|
+
export default function Header({ blockletMeta, addons, ...rest }) {
|
|
53
|
+
const sessionInfo = React.useContext(SessionContext);
|
|
54
|
+
const { locale } = useLocaleContext() || {};
|
|
55
|
+
// eslint-disable-next-line no-param-reassign
|
|
56
|
+
blockletMeta = blockletMeta || window.blocklet;
|
|
57
|
+
if (!blockletMeta) {
|
|
58
|
+
console.warn('blocklet meta is not found.');
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const {
|
|
62
|
+
appLogo,
|
|
63
|
+
appName,
|
|
64
|
+
theme: blockletTheme,
|
|
65
|
+
navigation = [],
|
|
66
|
+
enableConnect = true,
|
|
67
|
+
enableLocale = true,
|
|
68
|
+
} = blockletMeta;
|
|
69
|
+
const navItems = parseNavigation(navigation);
|
|
70
|
+
|
|
71
|
+
const renderAddons = () => {
|
|
72
|
+
// 不关心内置的 session manager 和 locale selector, 直接覆盖 UX Header 的 addons
|
|
73
|
+
if (addons && typeof addons !== 'function') {
|
|
74
|
+
return addons;
|
|
75
|
+
}
|
|
76
|
+
let addonsArray = [];
|
|
77
|
+
// 启用了多语言并且检测到了 locale context
|
|
78
|
+
if (enableLocale && locale) {
|
|
79
|
+
addonsArray.push(<LocaleSelector showText={false} />);
|
|
80
|
+
}
|
|
81
|
+
// 启用了连接钱包并且检测到了 session context
|
|
82
|
+
if (enableConnect && sessionInfo) {
|
|
83
|
+
addonsArray.push(<SessionManager session={sessionInfo.session} showRole />);
|
|
84
|
+
}
|
|
85
|
+
// 在内置 addons 基础上定制 addons
|
|
86
|
+
if (typeof addons === 'function') {
|
|
87
|
+
addonsArray = addons(addonsArray) || [];
|
|
88
|
+
}
|
|
89
|
+
const addonsFragment = React.createElement(React.Fragment, null, ...addonsArray);
|
|
90
|
+
return addonsFragment;
|
|
91
|
+
};
|
|
92
|
+
return (
|
|
93
|
+
<StyledUxHeader
|
|
94
|
+
logo={
|
|
95
|
+
<a href="/">
|
|
96
|
+
<img src={appLogo} alt="logo" />
|
|
97
|
+
</a>
|
|
98
|
+
}
|
|
99
|
+
brand={appName}
|
|
100
|
+
mobile={{ enableMenu: false, hideLogo: false }}
|
|
101
|
+
addons={renderAddons()}
|
|
102
|
+
{...rest}
|
|
103
|
+
$bgcolor={blockletTheme?.background}>
|
|
104
|
+
{!!navItems?.length && (
|
|
105
|
+
<NavMenu className="header-nav" items={navItems} bgColor="transparent" />
|
|
106
|
+
)}
|
|
107
|
+
</StyledUxHeader>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Header.propTypes = {
|
|
112
|
+
blockletMeta: PropTypes.object,
|
|
113
|
+
// 需要考虑 定制的 addons 与内置的 连接钱包/选择语言 addons 共存的情况
|
|
114
|
+
// - PropTypes.func: 可以把自定义 addons 插在 session-manager 或 locale-selector (如果存在的话) 前/中/后
|
|
115
|
+
// - PropTypes.node: 将 addons 原样传给 UX Header 组件
|
|
116
|
+
addons: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
Header.defaultProps = {
|
|
120
|
+
blockletMeta: null,
|
|
121
|
+
addons: null,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const StyledUxHeader = styled(UxHeader)`
|
|
125
|
+
${({ $bgcolor }) => $bgcolor && `background-color: ${$bgcolor};`}
|
|
126
|
+
.header-logo {
|
|
127
|
+
min-width: 40px;
|
|
128
|
+
}
|
|
129
|
+
.header-nav {
|
|
130
|
+
.navmenu-sub .navmenu-item {
|
|
131
|
+
min-width: 80px;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
`;
|