@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 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
+ ![@blocklet/ui-react](https://www.arcblock.io/.netlify/functions/badge/?text=@blocklet/ui-react)
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
+ `;