@blocklet/ui-react 2.1.40 → 2.1.43

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.
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.publicPath = exports.parseNavigation = exports.getLocalizedNavigation = exports.formatTheme = exports.formatBlockletInfo = void 0;
7
+
8
+ var _utils = require("./utils");
9
+
10
+ var _window, _window$blocklet;
11
+
12
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
13
+
14
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
15
+
16
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
17
+
18
+ const publicPath = ((_window = window) === null || _window === void 0 ? void 0 : (_window$blocklet = _window.blocklet) === null || _window$blocklet === void 0 ? void 0 : _window$blocklet.prefix) || '/';
19
+ /**
20
+ * 格式化 theme (目前仅考虑 background)
21
+ */
22
+
23
+ exports.publicPath = publicPath;
24
+
25
+ const formatTheme = theme => {
26
+ const formatted = _objectSpread({}, theme);
27
+
28
+ const background = theme === null || theme === void 0 ? void 0 : theme.background;
29
+
30
+ if (typeof background === 'string') {
31
+ formatted.background = {
32
+ header: background,
33
+ footer: background,
34
+ default: background
35
+ };
36
+ } else if (background && typeof background === 'object') {
37
+ formatted.background = {
38
+ header: background.header || background.default,
39
+ footer: background.footer || background.default,
40
+ default: background.default
41
+ };
42
+ }
43
+
44
+ return formatted;
45
+ };
46
+ /**
47
+ * 获取指定 locale 对应的 navigation 数据, 仅考虑 zh/en
48
+ */
49
+
50
+
51
+ exports.formatTheme = formatTheme;
52
+
53
+ const getLocalizedNavigation = function getLocalizedNavigation(navigation) {
54
+ let locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en';
55
+
56
+ if (!(navigation !== null && navigation !== void 0 && navigation.length)) {
57
+ return navigation;
58
+ } // eslint-disable-next-line no-shadow
59
+
60
+
61
+ const getTitle = (title, locale) => {
62
+ if (typeof title === 'string') {
63
+ return title;
64
+ }
65
+
66
+ if (typeof title === 'object') {
67
+ return title[locale] || (title === null || title === void 0 ? void 0 : title.en) || (title === null || title === void 0 ? void 0 : title.zh);
68
+ }
69
+
70
+ return title;
71
+ };
72
+
73
+ return (0, _utils.mapRecursive)(navigation, item => {
74
+ return _objectSpread(_objectSpread({}, item), {}, {
75
+ title: getTitle(item.title, locale)
76
+ });
77
+ }, 'items');
78
+ };
79
+
80
+ exports.getLocalizedNavigation = getLocalizedNavigation;
81
+
82
+ const parseNavigation = navigation => {
83
+ if (!(navigation !== null && navigation !== void 0 && navigation.length)) {
84
+ return null;
85
+ }
86
+
87
+ const sections = {
88
+ header: [],
89
+ footer: [],
90
+ // 对应 footer social media
91
+ social: [],
92
+ // 对应 footer 底部 links
93
+ bottom: []
94
+ }; // 对 navigation 顶层元素按 section 分组
95
+
96
+ navigation.forEach(item => {
97
+ // item#section 为空时, 表示只存在于 header
98
+ if (!item.section) {
99
+ sections.header.push(item); // item 出现在指定几个 section 中 (array)
100
+ } else if (Array.isArray(item.section)) {
101
+ item.section.forEach(sectionKey => {
102
+ var _sections$sectionKey;
103
+
104
+ (_sections$sectionKey = sections[sectionKey]) === null || _sections$sectionKey === void 0 ? void 0 : _sections$sectionKey.push(item);
105
+ }); // item 出现在指定的一个 section 中 (string)
106
+ } else if (typeof item.section === 'string') {
107
+ var _sections$item$sectio;
108
+
109
+ (_sections$item$sectio = sections[item.section]) === null || _sections$item$sectio === void 0 ? void 0 : _sections$item$sectio.push(item);
110
+ }
111
+ });
112
+ return sections;
113
+ };
114
+ /**
115
+ * 格式化 blocklet info 数据
116
+ */
117
+
118
+
119
+ exports.parseNavigation = parseNavigation;
120
+
121
+ const formatBlockletInfo = blockletInfo => {
122
+ if (!blockletInfo) {
123
+ return null;
124
+ }
125
+
126
+ const formatted = _objectSpread({}, blockletInfo); // theme
127
+
128
+
129
+ formatted.theme = formatTheme(formatted.theme); // navigation
130
+
131
+ formatted.navigation = parseNavigation(formatted.navigation);
132
+ return formatted;
133
+ };
134
+
135
+ exports.formatBlockletInfo = formatBlockletInfo;
package/lib/types.js CHANGED
@@ -11,7 +11,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
11
11
 
12
12
  /* eslint-disable import/prefer-default-export */
13
13
  const blockletMetaProps = _propTypes.default.shape({
14
- appLogo: _propTypes.default.string,
14
+ appLogo: _propTypes.default.node,
15
15
  appName: _propTypes.default.string,
16
16
  theme: _propTypes.default.shape({
17
17
  background: _propTypes.default.string
@@ -19,11 +19,11 @@ const blockletMetaProps = _propTypes.default.shape({
19
19
  enableConnect: _propTypes.default.bool,
20
20
  enableLocale: _propTypes.default.bool,
21
21
  navigation: _propTypes.default.arrayOf(_propTypes.default.shape({
22
- title: _propTypes.default.string,
22
+ title: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.object]),
23
23
  link: _propTypes.default.string,
24
24
  icon: _propTypes.default.string,
25
25
  items: _propTypes.default.arrayOf(_propTypes.default.shape({
26
- title: _propTypes.default.string,
26
+ title: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.object]),
27
27
  link: _propTypes.default.string
28
28
  }))
29
29
  }))
package/lib/utils.js CHANGED
@@ -3,9 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.publicPath = exports.matchPaths = exports.matchPath = exports.mapRecursive = exports.isUrl = exports.flatRecursive = void 0;
7
-
8
- var _window, _window$blocklet;
6
+ exports.matchPaths = exports.matchPath = exports.mapRecursive = exports.isUrl = exports.flatRecursive = exports.countRecursive = void 0;
9
7
 
10
8
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
11
9
 
@@ -34,11 +32,21 @@ const flatRecursive = function flatRecursive(array) {
34
32
  const result = [];
35
33
  mapRecursive(array, item => result.push(item), childrenKey);
36
34
  return result;
37
- }; // "http://", "https://", "//" 开头的 3 种情况
35
+ }; // 对有层级结构的 array 元素计数
38
36
 
39
37
 
40
38
  exports.flatRecursive = flatRecursive;
41
39
 
40
+ const countRecursive = function countRecursive(array) {
41
+ let childrenKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'children';
42
+ let counter = 0;
43
+ mapRecursive(array, () => counter++, childrenKey);
44
+ return counter;
45
+ }; // "http://", "https://", "//" 开头的 3 种情况
46
+
47
+
48
+ exports.countRecursive = countRecursive;
49
+
42
50
  const isUrl = str => {
43
51
  return /^(https?:)?\/\//.test(str);
44
52
  };
@@ -85,6 +93,4 @@ const matchPaths = function matchPaths() {
85
93
  return mostSpecific.index;
86
94
  };
87
95
 
88
- exports.matchPaths = matchPaths;
89
- const publicPath = ((_window = window) === null || _window === void 0 ? void 0 : (_window$blocklet = _window.blocklet) === null || _window$blocklet === void 0 ? void 0 : _window$blocklet.prefix) || '/';
90
- exports.publicPath = publicPath;
96
+ exports.matchPaths = matchPaths;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "2.1.40",
3
+ "version": "2.1.43",
4
4
  "description": "Some useful front-end web components that can be used in Blocklets.",
5
5
  "keywords": [
6
6
  "react",
@@ -34,8 +34,8 @@
34
34
  "url": "https://github.com/ArcBlock/ux/issues"
35
35
  },
36
36
  "dependencies": {
37
- "@arcblock/did-connect": "^2.1.40",
38
- "@arcblock/ux": "^2.1.40",
37
+ "@arcblock/did-connect": "^2.1.43",
38
+ "@arcblock/ux": "^2.1.43",
39
39
  "@iconify/iconify": "^2.2.1",
40
40
  "@mui/material": "^5.6.4",
41
41
  "core-js": "^3.6.4",
@@ -57,5 +57,5 @@
57
57
  "eslint-plugin-react-hooks": "^4.2.0",
58
58
  "jest": "^24.1.0"
59
59
  },
60
- "gitHead": "a5a95afc20fc56c089c46c81dda7e8ff4b48744a"
60
+ "gitHead": "a9cd5a9d9c5e58bba3a4adbf13069ebc37120515"
61
61
  }
@@ -37,7 +37,6 @@ Brand.defaultProps = {
37
37
  const Root = styled.div`
38
38
  display: flex;
39
39
  flex-direction: column;
40
- width: 240px;
41
40
  font-size: 14px;
42
41
  a {
43
42
  text-decoration: none;
@@ -21,5 +21,5 @@ Copyright.defaultProps = {
21
21
 
22
22
  const Root = styled.p`
23
23
  margin: 0;
24
- font-size: 14px;
24
+ font-size: 13px;
25
25
  `;
@@ -1,15 +1,12 @@
1
+ import { useMemo } from 'react';
1
2
  import styled from 'styled-components';
2
3
  import { useTheme } from '@mui/material/styles';
3
- import Box from '@mui/material/Box';
4
- import Container from '@mui/material/Container';
5
4
  import { withErrorBoundary } from 'react-error-boundary';
5
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
6
6
  import { ErrorFallback } from '@arcblock/ux/lib/ErrorBoundary';
7
- import Brand from './brand';
8
- import Links from './links';
9
- import SocialMedia from './social-media';
10
- import Copyright from './copyright';
11
- import Row from './row';
7
+ import InternalFooter from './internal-footer';
12
8
  import { mapRecursive } from '../utils';
9
+ import { formatBlockletInfo, getLocalizedNavigation } from '../blocklets';
13
10
 
14
11
  import { blockletMetaProps } from '../types';
15
12
 
@@ -17,52 +14,50 @@ import { blockletMetaProps } from '../types';
17
14
  * 专门用于 (composable) blocklet 的 Footer 组件, 基于 blocklet meta 中的数据渲染
18
15
  */
19
16
  function Footer({ meta, ...rest }) {
20
- const theme = useTheme();
17
+ const muiTheme = useTheme();
18
+ const { locale } = useLocaleContext() || {};
21
19
  const blocklet = Object.assign({}, window.blocklet, meta);
20
+ const formattedBlocklet = useMemo(() => {
21
+ try {
22
+ return formatBlockletInfo(blocklet);
23
+ } catch (e) {
24
+ console.error('Failed to format blocklet info', e, blocklet);
25
+ return blocklet;
26
+ }
27
+ }, [blocklet]);
22
28
  if (!blocklet.appName) {
23
29
  return null;
24
30
  }
25
31
 
26
- const {
27
- appLogo,
28
- appName,
29
- appDescription,
30
- description,
31
- theme: blockletTheme,
32
- navigation = [],
33
- copyrightOwner,
34
- footerNavigation,
35
- footerLinks,
36
- socialMedia,
37
- } = blocklet;
38
- const navMenuItems = mapRecursive(
39
- // TODO: 需要讨论 blocklet.yml 是否需要专门为 footer navigation 提供配置, 暂时与 header 共用 navigation 配置
40
- footerNavigation || navigation,
41
- (item) => ({
42
- ...item,
43
- label: item.title,
44
- link: item.link,
45
- }),
46
- 'items'
47
- );
32
+ const { appLogo, appName, appDescription, description, theme, copyright } = formattedBlocklet;
48
33
 
49
- return (
50
- <Root {...rest} $bgcolor={blockletTheme?.background} $theme={theme}>
51
- <Container>
52
- <Row>
53
- <Box>
54
- <Brand name={appName} logo={appLogo} description={appDescription || description} />
55
- <SocialMedia items={socialMedia} style={{ marginTop: 24 }} />
56
- </Box>
57
- <Links links={navMenuItems} />
58
- </Row>
59
- <Row autoCenter style={{ marginTop: 72 }}>
60
- <Copyright owner={copyrightOwner || appName} />
61
- <Links flowLayout links={footerLinks} style={{ color: '#999' }} />
62
- </Row>
63
- </Container>
64
- </Root>
65
- );
34
+ const localized = {
35
+ footerNav: getLocalizedNavigation(formattedBlocklet?.navigation?.footer, locale) || [],
36
+ socialMedia: getLocalizedNavigation(formattedBlocklet?.navigation?.social, locale) || [],
37
+ links: getLocalizedNavigation(formattedBlocklet?.navigation?.bottom, locale) || [],
38
+ };
39
+
40
+ const props = {
41
+ brand: {
42
+ name: appName,
43
+ description: appDescription || description,
44
+ logo: appLogo,
45
+ },
46
+ navigation: mapRecursive(
47
+ localized.footerNav,
48
+ (item) => ({
49
+ ...item,
50
+ label: item.title,
51
+ link: item.link,
52
+ }),
53
+ 'items'
54
+ ),
55
+ copyright,
56
+ socialMedia: localized.socialMedia,
57
+ links: localized.links.map((item) => ({ ...item, label: item.title })),
58
+ };
59
+
60
+ return <StyledInternalFooter {...props} {...rest} $bgcolor={theme?.background?.footer} $theme={muiTheme} />;
66
61
  }
67
62
 
68
63
  Footer.propTypes = {
@@ -73,11 +68,12 @@ Footer.defaultProps = {
73
68
  meta: {},
74
69
  };
75
70
 
76
- const Root = styled.div`
77
- padding: 48px 0;
71
+ const StyledInternalFooter = styled(InternalFooter)`
78
72
  border-top: 1px solid #eee;
79
73
  color: ${(props) => props.$theme.palette.grey[600]};
80
74
  ${({ $bgcolor }) => $bgcolor && `background-color: ${$bgcolor};`}
75
+ font-family: Lato, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
76
+ 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
81
77
  `;
82
78
 
83
79
  export default withErrorBoundary(Footer, {
@@ -0,0 +1,98 @@
1
+ import PropTypes from 'prop-types';
2
+ import Brand from './brand';
3
+ import Links from './links';
4
+ import SocialMedia from './social-media';
5
+ import Copyright from './copyright';
6
+ import StandardLayout from './layout/standard';
7
+ import PlainLayout from './layout/plain';
8
+
9
+ const layouts = [
10
+ {
11
+ // navigation 数据为空时, 使用简单布局
12
+ support: (_, data) => !data.navigation?.length,
13
+ component: PlainLayout,
14
+ },
15
+ {
16
+ // 默认标准布局
17
+ support: () => true,
18
+ component: StandardLayout,
19
+ },
20
+ ];
21
+
22
+ /**
23
+ * 通用的内部 footer 组件, 定义并渲染常见的几种 footer 元素: brand/navigation/social medial 等
24
+ */
25
+ function InternalFooter(props) {
26
+ const { brand, navigation, socialMedia, copyright, links, ...rest } = props;
27
+ const renderBrand = () => {
28
+ return brand ? <Brand {...brand} /> : null;
29
+ };
30
+ const renderNavigation = () => {
31
+ return navigation?.length ? <Links links={navigation} /> : null;
32
+ };
33
+ const renderSocialMedia = () => {
34
+ return socialMedia?.length ? <SocialMedia items={socialMedia} /> : null;
35
+ };
36
+ const renderCopyright = () => {
37
+ // 如果 copyright.owner 不存在, 则使用 brand.name, 如果 brand.name 也不存在, copyright 元素为空
38
+ const copyrightOwner = copyright?.owner || brand?.name;
39
+ if (!copyrightOwner) {
40
+ return null;
41
+ }
42
+ return <Copyright owner={copyrightOwner} year={copyright?.year || undefined} />;
43
+ };
44
+ const renderLinks = () => {
45
+ return links?.length ? <Links flowLayout links={links} /> : null;
46
+ };
47
+
48
+ const elements = {
49
+ brand: renderBrand(),
50
+ navigation: renderNavigation(),
51
+ socialMedia: renderSocialMedia(),
52
+ copyright: renderCopyright(),
53
+ links: renderLinks(),
54
+ };
55
+ const LayoutComponent = layouts.find((layout) => layout.support(elements, props)).component;
56
+ return <LayoutComponent elements={elements} {...rest} />;
57
+ }
58
+
59
+ InternalFooter.propTypes = {
60
+ brand: PropTypes.shape({
61
+ name: PropTypes.node,
62
+ description: PropTypes.string,
63
+ logo: PropTypes.node,
64
+ }),
65
+ navigation: PropTypes.arrayOf(
66
+ PropTypes.shape({
67
+ label: PropTypes.node,
68
+ link: PropTypes.string,
69
+ })
70
+ ),
71
+ socialMedia: PropTypes.arrayOf(
72
+ PropTypes.shape({
73
+ icon: PropTypes.node,
74
+ link: PropTypes.string,
75
+ })
76
+ ),
77
+ copyright: PropTypes.shape({
78
+ owner: PropTypes.string,
79
+ year: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
80
+ }),
81
+ // privacy/legal 等链接, 常放于 footer 右下侧或最底部
82
+ links: PropTypes.arrayOf(
83
+ PropTypes.shape({
84
+ label: PropTypes.node,
85
+ link: PropTypes.string,
86
+ })
87
+ ),
88
+ };
89
+
90
+ InternalFooter.defaultProps = {
91
+ brand: null,
92
+ navigation: null,
93
+ copyright: null,
94
+ socialMedia: null,
95
+ links: null,
96
+ };
97
+
98
+ export default InternalFooter;
@@ -0,0 +1,42 @@
1
+ import { cloneElement } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import styled from 'styled-components';
4
+ import Container from '@mui/material/Container';
5
+
6
+ /**
7
+ * footer plain layout
8
+ */
9
+ function PlainLayout({ elements, ...rest }) {
10
+ return (
11
+ <Root {...rest}>
12
+ <Container className="plain-layout-container">
13
+ {cloneElement(elements.brand, { name: null, description: null })}
14
+ {elements.copyright}
15
+ </Container>
16
+ </Root>
17
+ );
18
+ }
19
+
20
+ PlainLayout.propTypes = {
21
+ elements: PropTypes.shape({
22
+ brand: PropTypes.element,
23
+ navigation: PropTypes.element,
24
+ socialMedia: PropTypes.element,
25
+ copyright: PropTypes.element,
26
+ links: PropTypes.element,
27
+ }).isRequired,
28
+ };
29
+
30
+ const Root = styled.div`
31
+ padding: 24px 0;
32
+ border-top: 1px solid #eee;
33
+ .plain-layout-container {
34
+ display: flex;
35
+ justify-content: space-between;
36
+ align-items: center;
37
+ flex-wrap: wrap;
38
+ gap: 8px;
39
+ }
40
+ `;
41
+
42
+ export default PlainLayout;
File without changes
@@ -0,0 +1,44 @@
1
+ import PropTypes from 'prop-types';
2
+ import styled from 'styled-components';
3
+ import Box from '@mui/material/Box';
4
+ import Container from '@mui/material/Container';
5
+ import Row from './row';
6
+
7
+ /**
8
+ * footer standard layout
9
+ */
10
+ function Footer({ elements, ...rest }) {
11
+ return (
12
+ <Root {...rest}>
13
+ <Container>
14
+ <Row>
15
+ <Box>
16
+ <Box width={{ md: 240 }}>{elements.brand}</Box>
17
+ <Box mt={2}>{elements.socialMedia}</Box>
18
+ </Box>
19
+ {elements.navigation}
20
+ </Row>
21
+ <Row autoCenter style={{ marginTop: 72 }}>
22
+ {elements.copyright}
23
+ {elements.links}
24
+ </Row>
25
+ </Container>
26
+ </Root>
27
+ );
28
+ }
29
+
30
+ Footer.propTypes = {
31
+ elements: PropTypes.shape({
32
+ brand: PropTypes.element,
33
+ navigation: PropTypes.element,
34
+ socialMedia: PropTypes.element,
35
+ copyright: PropTypes.element,
36
+ links: PropTypes.element,
37
+ }).isRequired,
38
+ };
39
+
40
+ const Root = styled.div`
41
+ padding: 48px 0;
42
+ `;
43
+
44
+ export default Footer;
@@ -1,4 +1,4 @@
1
- import { useContext, createElement, Fragment } from 'react';
1
+ import { useContext, createElement, Fragment, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import styled from 'styled-components';
4
4
  import { ThemeProvider } from '@mui/material/styles';
@@ -13,10 +13,11 @@ import LocaleSelector from '@arcblock/ux/lib/Locale/selector';
13
13
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
14
14
  import DidAddress from '@arcblock/did-connect/lib/Address';
15
15
  import DidAvatar from '@arcblock/did-connect/lib/Avatar';
16
- import '@iconify/iconify';
16
+ import Icon from '../Icon';
17
17
 
18
18
  import { blockletMetaProps, sessionManagerProps } from '../types';
19
- import { mapRecursive, flatRecursive, matchPaths, publicPath } from '../utils';
19
+ import { mapRecursive, flatRecursive, matchPaths } from '../utils';
20
+ import { publicPath, formatBlockletInfo, getLocalizedNavigation } from '../blocklets';
20
21
 
21
22
  const muiTheme = create();
22
23
 
@@ -27,7 +28,7 @@ const parseNavigation = (navigation) => {
27
28
  }
28
29
  let counter = 1;
29
30
  const parseItem = (item) => {
30
- const icon = item.icon ? <span className="iconify" data-icon={item.icon} /> : null;
31
+ const icon = item.icon ? <Icon icon={item.icon} /> : null;
31
32
  if (item.items) {
32
33
  return {
33
34
  id: `${counter++}`,
@@ -68,11 +69,20 @@ function Header({ meta, addons, sessionManagerProps, ...rest }) {
68
69
  const sessionInfo = useContext(SessionContext);
69
70
  const { locale } = useLocaleContext() || {};
70
71
  const blocklet = Object.assign({}, window.blocklet, meta);
72
+ const formattedBlocklet = useMemo(() => {
73
+ try {
74
+ return formatBlockletInfo(blocklet);
75
+ } catch (e) {
76
+ console.error('Failed to format blocklet info', e, blocklet);
77
+ return blocklet;
78
+ }
79
+ }, [blocklet]);
80
+
71
81
  if (!blocklet.appName) {
72
82
  return null;
73
83
  }
74
-
75
- const { did, appLogo, appName, theme, navigation = [], enableConnect = true, enableLocale = true } = blocklet;
84
+ const { did, appLogo, appName, theme, enableConnect = true, enableLocale = true } = formattedBlocklet;
85
+ const navigation = getLocalizedNavigation(formattedBlocklet?.navigation?.header, locale);
76
86
  const { navItems, activeId } = parseNavigation(navigation);
77
87
 
78
88
  const renderAddons = () => {
@@ -119,7 +129,7 @@ function Header({ meta, addons, sessionManagerProps, ...rest }) {
119
129
  }
120
130
  addons={renderAddons()}
121
131
  {...rest}
122
- $bgcolor={theme?.background}
132
+ $bgcolor={theme?.background?.header}
123
133
  $theme={muiTheme}>
124
134
  {/* blocklet.yml 没有配置 navigation 时, 则为 children 传入 null, 此时 ResponsiveHeader 会渲染普通的不带 menu 的 Header */}
125
135
  {!navItems?.length
@@ -131,6 +141,7 @@ function Header({ meta, addons, sessionManagerProps, ...rest }) {
131
141
  items={navItems}
132
142
  className="header-nav"
133
143
  bgColor="transparent"
144
+ textColor="#888"
134
145
  />
135
146
  )}
136
147
  </StyledUxHeader>
@@ -159,6 +170,8 @@ Header.defaultProps = {
159
170
 
160
171
  const StyledUxHeader = styled(ResponsiveHeader)`
161
172
  ${({ $bgcolor }) => `background-color: ${$bgcolor || '#fff'};`}
173
+ font-family: Lato, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
174
+ 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
162
175
  .header-logo {
163
176
  min-width: 44px;
164
177
  }
package/src/Icon/index.js CHANGED
@@ -6,20 +6,29 @@ import { isUrl } from '../utils';
6
6
  /**
7
7
  * Icon 组件, 基于 mui Avatar 组件扩展对 iconify 的支持
8
8
  */
9
- export default function Icon({ icon, size, ...rest }) {
10
- const sx = { ...rest.sx };
9
+ export default function Icon({ icon, size, sx, ...rest }) {
10
+ const _sx = [...(Array.isArray(sx) ? sx : [sx])];
11
11
  if (size) {
12
- sx.width = size;
13
- sx.height = size;
12
+ _sx.push({ width: size, height: size });
13
+ }
14
+ // 禁用默认的 circular variant 样式
15
+ if (!rest.variant) {
16
+ _sx.push({
17
+ '&.MuiAvatar-root': {
18
+ color: 'inherit',
19
+ backgroundColor: 'transparent',
20
+ borderRadius: 0,
21
+ },
22
+ });
14
23
  }
15
24
  if (isUrl(icon)) {
16
- return <Avatar {...rest} src={icon} sx={sx} />;
25
+ return <Avatar as="span" {...rest} src={icon} sx={_sx} />;
17
26
  }
18
27
  if (icon) {
19
28
  // y = 0.6 * x + 4
20
29
  const iconHeight = size ? 0.6 * size + 4 : 0;
21
30
  return (
22
- <Avatar {...rest} sx={sx}>
31
+ <Avatar as="span" {...rest} sx={_sx}>
23
32
  <span className="iconify" data-icon={icon} {...(iconHeight && { 'data-height': iconHeight })} />
24
33
  </Avatar>
25
34
  );
@@ -33,8 +42,10 @@ Icon.propTypes = {
33
42
  // 2. url
34
43
  icon: PropTypes.string.isRequired,
35
44
  size: PropTypes.number,
45
+ sx: PropTypes.oneOfType([PropTypes.array, PropTypes.func, PropTypes.object]),
36
46
  };
37
47
 
38
48
  Icon.defaultProps = {
39
49
  size: null,
50
+ sx: null,
40
51
  };