@blocklet/ui-react 2.4.4 → 2.4.7

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.
@@ -49,4 +49,4 @@ Copyright.defaultProps = {
49
49
  owner: 'ArcBlock, Inc.',
50
50
  year: "".concat(new Date().getFullYear())
51
51
  };
52
- const Root = (0, _Theme.styled)('p')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n margin: 0;\n font-size: 13px;\n"])));
52
+ const Root = (0, _Theme.styled)('p')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: flex;\n align-items: center;\n margin: 0;\n font-size: 13px;\n"])));
@@ -21,21 +21,22 @@ var _plain = _interopRequireDefault(require("./layout/plain"));
21
21
 
22
22
  var _jsxRuntime = require("react/jsx-runtime");
23
23
 
24
- const _excluded = ["brand", "navigation", "socialMedia", "copyright", "links"];
24
+ const _excluded = ["brand", "navigation", "socialMedia", "copyright", "links", "layout"];
25
25
 
26
26
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
27
 
28
+ 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; }
29
+
30
+ 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; }
31
+
28
32
  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; }
29
33
 
30
34
  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; }
31
35
 
32
36
  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; }
33
37
 
34
- 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; }
35
-
36
- 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; }
37
-
38
38
  const layouts = [{
39
+ name: 'plain',
39
40
  // navigation 数据为空时, 使用简单布局
40
41
  support: (_, data) => {
41
42
  var _data$navigation;
@@ -44,10 +45,14 @@ const layouts = [{
44
45
  },
45
46
  component: _plain.default
46
47
  }, {
48
+ name: 'standard',
47
49
  // 默认标准布局
48
50
  support: () => true,
49
51
  component: _standard.default
50
52
  }];
53
+ const layoutsKeyByName = layouts.reduce((acc, cur) => _objectSpread(_objectSpread({}, acc), {}, {
54
+ [cur.name]: cur
55
+ }), {});
51
56
  /**
52
57
  * 通用的内部 footer 组件, 定义并渲染常见的几种 footer 元素: brand/navigation/social medial 等
53
58
  */
@@ -58,7 +63,8 @@ function InternalFooter(props) {
58
63
  navigation,
59
64
  socialMedia,
60
65
  copyright,
61
- links
66
+ links,
67
+ layout
62
68
  } = props,
63
69
  rest = _objectWithoutProperties(props, _excluded);
64
70
 
@@ -106,9 +112,23 @@ function InternalFooter(props) {
106
112
  copyright: renderCopyright(),
107
113
  links: renderLinks()
108
114
  };
109
- const LayoutComponent = layouts.find(layout => layout.support(elements, props)).component;
115
+ let LayoutComponent = null;
116
+
117
+ if (layout === 'auto') {
118
+ LayoutComponent = layouts.find(item => item.support(elements, props)).component;
119
+ } else {
120
+ var _layoutsKeyByName$lay;
121
+
122
+ LayoutComponent = (_layoutsKeyByName$lay = layoutsKeyByName[layout]) === null || _layoutsKeyByName$lay === void 0 ? void 0 : _layoutsKeyByName$lay.component;
123
+ }
124
+
125
+ if (!LayoutComponent) {
126
+ throw new Error("layout ".concat(layout, " is not supported."));
127
+ }
128
+
110
129
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(LayoutComponent, _objectSpread({
111
- elements: elements
130
+ elements: elements,
131
+ data: props
112
132
  }, rest));
113
133
  }
114
134
 
@@ -134,14 +154,17 @@ InternalFooter.propTypes = {
134
154
  links: _propTypes.default.arrayOf(_propTypes.default.shape({
135
155
  label: _propTypes.default.node,
136
156
  link: _propTypes.default.string
137
- }))
157
+ })),
158
+ // 可显式指定 footer layout, 默认根据内容自动决定 layout
159
+ layout: _propTypes.default.oneOf(['auto', 'standard', 'plain'])
138
160
  };
139
161
  InternalFooter.defaultProps = {
140
162
  brand: null,
141
163
  navigation: null,
142
164
  copyright: null,
143
165
  socialMedia: null,
144
- links: null
166
+ links: null,
167
+ layout: 'auto'
145
168
  };
146
169
  var _default = InternalFooter;
147
170
  exports.default = _default;
@@ -13,11 +13,13 @@ var _Container = _interopRequireDefault(require("@mui/material/Container"));
13
13
 
14
14
  var _Theme = require("@arcblock/ux/lib/Theme");
15
15
 
16
+ var _row = _interopRequireDefault(require("./row"));
17
+
16
18
  var _jsxRuntime = require("react/jsx-runtime");
17
19
 
18
20
  var _templateObject;
19
21
 
20
- const _excluded = ["elements"];
22
+ const _excluded = ["elements", "data"];
21
23
 
22
24
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
25
 
@@ -38,17 +40,26 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
38
40
  */
39
41
  function PlainLayout(_ref) {
40
42
  let {
41
- elements
43
+ elements,
44
+ data
42
45
  } = _ref,
43
46
  rest = _objectWithoutProperties(_ref, _excluded);
44
47
 
45
48
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(Root, _objectSpread(_objectSpread({}, rest), {}, {
46
49
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Container.default, {
47
50
  className: "plain-layout-container",
48
- children: [/*#__PURE__*/(0, _react.cloneElement)(elements.brand, {
49
- name: null,
50
- description: null
51
- }), elements.copyright]
51
+ children: [!!data.links && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_row.default, {
52
+ sx: {
53
+ width: 1
54
+ },
55
+ autoCenter: true,
56
+ children: [elements.copyright, elements.links]
57
+ }), !data.links && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
58
+ children: [/*#__PURE__*/(0, _react.cloneElement)(elements.brand, {
59
+ name: null,
60
+ description: null
61
+ }), elements.copyright]
62
+ })]
52
63
  })
53
64
  }));
54
65
  }
@@ -60,7 +71,8 @@ PlainLayout.propTypes = {
60
71
  socialMedia: _propTypes.default.element,
61
72
  copyright: _propTypes.default.element,
62
73
  links: _propTypes.default.element
63
- }).isRequired
74
+ }).isRequired,
75
+ data: _propTypes.default.object.isRequired
64
76
  };
65
77
  const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n padding: 24px 0;\n border-top: 1px solid #eee;\n .plain-layout-container {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n }\n"])));
66
78
  var _default = PlainLayout;
@@ -7,6 +7,8 @@ exports.default = Row;
7
7
 
8
8
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
 
10
+ var _Box = _interopRequireDefault(require("@mui/material/Box"));
11
+
10
12
  var _Theme = require("@arcblock/ux/lib/Theme");
11
13
 
12
14
  var _clsx = _interopRequireDefault(require("clsx"));
@@ -61,4 +63,4 @@ Row.defaultProps = {
61
63
  children: null,
62
64
  autoCenter: false
63
65
  };
64
- const RowRoot = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: flex;\n justify-content: space-between;\n & + & {\n margin-top: 24px;\n }\n &.footer-row-auto-center > *:only-child {\n margin: 0 auto;\n }\n\n ", " {\n align-items: stretch;\n flex-direction: column;\n gap: 16px;\n > * {\n flex: 1 0 100%;\n }\n &.footer-row-auto-center > * {\n margin: 0 auto;\n }\n }\n"])), props => props.$theme.breakpoints.down('md'));
66
+ const RowRoot = (0, _Theme.styled)(_Box.default)(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: flex;\n justify-content: space-between;\n & + & {\n margin-top: 24px;\n }\n &.footer-row-auto-center > *:only-child {\n margin: 0 auto;\n }\n\n ", " {\n align-items: stretch;\n flex-direction: column;\n gap: 16px;\n > * {\n flex: 1 0 100%;\n }\n &.footer-row-auto-center > * {\n margin: 0 auto;\n }\n }\n"])), props => props.$theme.breakpoints.down('md'));
@@ -19,7 +19,7 @@ var _jsxRuntime = require("react/jsx-runtime");
19
19
 
20
20
  var _templateObject;
21
21
 
22
- const _excluded = ["elements"];
22
+ const _excluded = ["elements", "data"];
23
23
 
24
24
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
25
 
@@ -38,46 +38,61 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
38
38
  /**
39
39
  * footer standard layout
40
40
  */
41
- function Footer(_ref) {
41
+ function StandardLayout(_ref) {
42
+ var _data$navigation;
43
+
42
44
  let {
43
- elements
45
+ elements,
46
+ data
44
47
  } = _ref,
45
48
  rest = _objectWithoutProperties(_ref, _excluded);
46
49
 
47
50
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(Root, _objectSpread(_objectSpread({}, rest), {}, {
48
51
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Container.default, {
49
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_row.default, {
50
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_Box.default, {
51
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Box.default, {
52
- width: {
53
- md: 240
54
- },
55
- children: elements.brand
56
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Box.default, {
57
- mt: 2,
58
- children: elements.socialMedia
59
- })]
60
- }), elements.navigation]
52
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_Box.default, {
53
+ sx: {
54
+ display: 'flex',
55
+ justifyContent: 'space-between',
56
+ pb: 3
57
+ },
58
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Box.default, {
59
+ children: elements.brand
60
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Box.default, {
61
+ lineHeight: 1,
62
+ alignSelf: "end",
63
+ children: elements.socialMedia
64
+ })]
65
+ }), !!((_data$navigation = data.navigation) !== null && _data$navigation !== void 0 && _data$navigation.length) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Box.default, {
66
+ sx: {
67
+ mb: 6,
68
+ pt: 3,
69
+ borderTop: 1,
70
+ borderColor: 'grey.200'
71
+ },
72
+ children: elements.navigation
61
73
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_row.default, {
62
- autoCenter: true,
63
- style: {
64
- marginTop: 72
74
+ sx: {
75
+ pt: 3,
76
+ borderTop: 1,
77
+ borderColor: 'grey.200'
65
78
  },
79
+ autoCenter: true,
66
80
  children: [elements.copyright, elements.links]
67
81
  })]
68
82
  })
69
83
  }));
70
84
  }
71
85
 
72
- Footer.propTypes = {
86
+ StandardLayout.propTypes = {
73
87
  elements: _propTypes.default.shape({
74
88
  brand: _propTypes.default.element,
75
89
  navigation: _propTypes.default.element,
76
90
  socialMedia: _propTypes.default.element,
77
91
  copyright: _propTypes.default.element,
78
92
  links: _propTypes.default.element
79
- }).isRequired
93
+ }).isRequired,
94
+ data: _propTypes.default.object.isRequired
80
95
  };
81
- const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n padding: 48px 0;\n"])));
82
- var _default = Footer;
96
+ const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n padding: 32px 0 24px 0;\n .footer-brand-desc {\n display: none;\n }\n"])));
97
+ var _default = StandardLayout;
83
98
  exports.default = _default;
@@ -13,6 +13,10 @@ var _Theme = require("@arcblock/ux/lib/Theme");
13
13
 
14
14
  var _clsx = _interopRequireDefault(require("clsx"));
15
15
 
16
+ var _ExpandMore = _interopRequireDefault(require("@mui/icons-material/ExpandMore"));
17
+
18
+ var _Icon = _interopRequireDefault(require("../Icon"));
19
+
16
20
  var _jsxRuntime = require("react/jsx-runtime");
17
21
 
18
22
  var _templateObject;
@@ -62,6 +66,7 @@ function Links(_ref) {
62
66
  let {
63
67
  label,
64
68
  link,
69
+ icon,
65
70
  render,
66
71
  props
67
72
  } = _ref2;
@@ -81,7 +86,15 @@ function Links(_ref) {
81
86
  }));
82
87
  }
83
88
 
84
- return result;
89
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
90
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
91
+ icon: icon,
92
+ size: 20,
93
+ sx: {
94
+ mr: 0.5
95
+ }
96
+ }), result]
97
+ });
85
98
  };
86
99
 
87
100
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(Root, _objectSpread(_objectSpread({}, rest), {}, {
@@ -99,14 +112,22 @@ function Links(_ref) {
99
112
  const {
100
113
  items
101
114
  } = item;
115
+ const isActive = i === activeIndex;
102
116
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
103
117
  className: (0, _clsx.default)('footer-links-group', {
104
- 'footer-links-group--active': i === activeIndex
118
+ 'footer-links-group--active': isActive
105
119
  }),
106
120
  onClick: () => setActiveIndex(activeIndex === i ? -1 : i),
107
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
121
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
108
122
  className: "footer-links-item",
109
- children: renderItem(item)
123
+ children: [renderItem(item), !!(items !== null && items !== void 0 && items.length) && /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
124
+ className: "footer-links-group-expand-icon",
125
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandMore.default, {
126
+ style: {
127
+ transform: "rotate(".concat(isActive ? 180 : 0, "deg)")
128
+ }
129
+ })
130
+ })]
110
131
  }), !!(items !== null && items !== void 0 && items.length) && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
111
132
  className: "footer-links-sub",
112
133
  children: items.map((child, j) => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
@@ -134,4 +155,4 @@ Links.defaultProps = {
134
155
  links: [],
135
156
  flowLayout: false
136
157
  };
137
- const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n overflow: hidden;\n color: ", ";\n .footer-links-inner {\n display: flex;\n justify-content: space-between;\n margin: 0 -8px;\n }\n .footer-links-group,\n .footer-links-sub {\n display: flex;\n flex-direction: column;\n }\n .footer-links-item {\n display: inline-block;\n padding: 0 8px;\n font-size: 14px;\n line-height: 1.6;\n }\n .footer-links-group + .footer-links-group {\n margin-left: 128px;\n }\n &.footer-links--grouped {\n .footer-links-group {\n > .footer-links-item {\n font-weight: bold;\n }\n .footer-links-sub {\n margin-top: 8px;\n }\n }\n }\n a {\n color: inherit;\n text-decoration: none;\n &:hover {\n text-decoration: underline;\n }\n }\n\n &.footer-links--flow {\n display: inline-flex;\n .footer-links-inner {\n justify-content: center;\n flex-wrap: wrap;\n margin: 0 -8px;\n .footer-links-item {\n padding: 0 8px;\n }\n }\n }\n\n ", " {\n .footer-links-group + .footer-links-group {\n margin-left: 56px;\n }\n }\n\n ", " {\n .footer-links-group + .footer-links-group {\n margin-left: 40px;\n }\n }\n\n ", " {\n .footer-links-inner {\n flex-direction: column;\n margin: 0;\n }\n .footer-links-sub {\n display: none;\n }\n .footer-links-group {\n padding: 12px 0;\n border-top: 1px solid ", ";\n }\n .footer-links-group + .footer-links-group {\n margin-left: 0;\n }\n .footer-links-group--active {\n .footer-links-sub {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n .footer-links-item {\n flex: 0 0 50%;\n }\n }\n }\n .footer-links-item {\n padding: 0;\n font-size: 13px;\n }\n &.footer-links--grouped {\n .footer-links-group {\n > .footer-links-item {\n font-size: 14px;\n }\n }\n }\n\n &.footer-links--flow {\n .footer-links-inner {\n flex-direction: row;\n }\n }\n }\n"])), props => props.$theme.palette.grey[600], props => props.$theme.breakpoints.down('lg'), props => props.$theme.breakpoints.down('lg'), props => props.$theme.breakpoints.down('sm'), props => props.$theme.palette.grey[200]);
158
+ const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n overflow: hidden;\n color: ", ";\n .footer-links-inner {\n display: flex;\n justify-content: space-between;\n margin: 0 -8px;\n }\n .footer-links-group,\n .footer-links-sub {\n display: flex;\n flex-direction: column;\n }\n .footer-links-group-expand-icon {\n display: none;\n position: absolute;\n right: 16px;\n top: 50%;\n transform: translate(0, -50%);\n svg {\n width: auto;\n height: 0.75em;\n }\n }\n .footer-links-item {\n display: inline-flex;\n align-items: center;\n position: relative;\n padding: 0 8px;\n font-size: 14px;\n line-height: 1.6;\n }\n &.footer-links--grouped {\n .footer-links-group {\n > .footer-links-item {\n font-weight: bold;\n }\n .footer-links-sub {\n margin-top: 8px;\n }\n }\n }\n a {\n display: inline-block;\n max-width: 150px;\n color: inherit;\n text-decoration: none;\n &:hover {\n text-decoration: underline;\n }\n }\n\n &.footer-links--flow {\n display: inline-flex;\n .footer-links-inner {\n justify-content: center;\n flex-wrap: wrap;\n margin: 0 -8px;\n .footer-links-item {\n padding: 0 8px;\n }\n .footer-links-item + .footer-links-item::before {\n content: '';\n position: absolute;\n left: 0;\n top: 50%;\n transform: translate(0, -50%);\n height: 1em;\n border-left: 1px solid ", ";\n }\n }\n }\n\n ", " {\n .footer-links-inner {\n flex-direction: column;\n margin: 0;\n }\n .footer-links-sub {\n display: none;\n }\n .footer-links-group {\n position: relative;\n padding: 12px 0;\n .footer-links-item .footer-links-group-expand-icon {\n display: inline-block;\n }\n }\n .footer-links-group + .footer-links-group {\n border-top: 1px solid ", ";\n }\n .footer-links-group--active {\n .footer-links-sub {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n .footer-links-item {\n flex: 0 0 50%;\n }\n }\n }\n .footer-links-item {\n padding: 0;\n font-size: 13px;\n }\n &.footer-links--grouped {\n .footer-links-group {\n > .footer-links-item {\n font-size: 14px;\n }\n }\n }\n\n &.footer-links--flow {\n .footer-links-inner {\n flex-direction: row;\n }\n }\n }\n"])), props => props.$theme.palette.grey[600], props => props.theme.palette.grey[400], props => props.$theme.breakpoints.down('md'), props => props.$theme.palette.grey[200]);
@@ -58,8 +58,7 @@ function SocialMedia(_ref) {
58
58
  bgcolor: theme.palette.grey[600],
59
59
  color: '#fff'
60
60
  },
61
- size: 44,
62
- variant: "rounded",
61
+ size: 24,
63
62
  component: "span"
64
63
  })
65
64
  }, i)
@@ -78,4 +77,4 @@ SocialMedia.propTypes = {
78
77
  SocialMedia.defaultProps = {
79
78
  items: null
80
79
  };
81
- const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: inline-flex;\n align-items: center;\n a + a {\n margin-left: 12px;\n }\n"])));
80
+ const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: inline-flex;\n align-items: center;\n a {\n color: ", ";\n &:hover {\n color: ", ";\n }\n }\n a + a {\n margin-left: 20px;\n }\n"])), props => props.theme.palette.grey[400], props => props.theme.palette.primary.light);
@@ -261,7 +261,7 @@ Header.defaultProps = {
261
261
  },
262
262
  homeLink: _blocklets.publicPath
263
263
  };
264
- const StyledUxHeader = (0, _Theme.styled)(_Header.ResponsiveHeader)(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n ", "\n font-family: Lato, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,\n 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';\n .header-logo {\n min-width: 44px;\n }\n ", " {\n .header-logo {\n min-width: 32px;\n }\n }\n .header-nav {\n .navmenu-sub .navmenu-item {\n min-width: 80px;\n }\n }\n .header-nav.navmenu--horizontal {\n .navmenu-root > .navmenu-sub,\n .navmenu-root > .navmenu-item {\n padding: 16px 4px;\n\n .navmenu-sub-container {\n padding-top: 0;\n }\n }\n }\n"])), _ref3 => {
264
+ const StyledUxHeader = (0, _Theme.styled)(_Header.ResponsiveHeader)(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n ", "\n font-family: Lato, Avenir, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,\n 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';\n .header-logo {\n min-width: 44px;\n }\n ", " {\n .header-logo {\n min-width: 32px;\n }\n }\n .header-nav {\n .navmenu-sub .navmenu-item {\n min-width: 80px;\n }\n }\n .header-nav.navmenu--horizontal {\n .navmenu-root > .navmenu-sub,\n .navmenu-root > .navmenu-item {\n padding: 16px 4px;\n\n .navmenu-sub-container {\n padding-top: 0;\n }\n }\n .navmenu-item-icon > .MuiAvatar-root,\n .navmenu-sub-icon > .MuiAvatar-root {\n width: 20px;\n height: 20px;\n }\n }\n"])), _ref3 => {
265
265
  let {
266
266
  $bgcolor
267
267
  } = _ref3;
package/lib/Icon/index.js CHANGED
@@ -56,6 +56,11 @@ function Icon(_ref) {
56
56
  color: 'inherit',
57
57
  backgroundColor: 'transparent',
58
58
  borderRadius: 0
59
+ },
60
+ // 无 icon 背景时, svg icon 尺寸与窗口尺寸一致
61
+ '&.MuiAvatar-root svg': {
62
+ width: '100%',
63
+ height: '100%'
59
64
  }
60
65
  });
61
66
  }
package/lib/blocklets.js CHANGED
@@ -68,11 +68,34 @@ const getLocalizedNavigation = function getLocalizedNavigation(navigation) {
68
68
  }
69
69
 
70
70
  return title;
71
+ }; // eslint-disable-next-line no-shadow
72
+
73
+
74
+ const getLink = (link, locale) => {
75
+ if (typeof link === 'string') {
76
+ // http[s] 开头的 url
77
+ if ((0, _utils.isUrl)(link)) {
78
+ const url = new URL(link);
79
+ url.searchParams.set('locale', locale);
80
+ return url.href;
81
+ }
82
+
83
+ const url = new URL(link, window.location.origin);
84
+ url.searchParams.set('locale', locale);
85
+ return url.pathname + url.search;
86
+ }
87
+
88
+ if (typeof link === 'object') {
89
+ return link[locale] || (link === null || link === void 0 ? void 0 : link.en) || (link === null || link === void 0 ? void 0 : link.zh);
90
+ }
91
+
92
+ return link;
71
93
  };
72
94
 
73
95
  return (0, _utils.mapRecursive)(navigation, item => {
74
96
  return _objectSpread(_objectSpread({}, item), {}, {
75
- title: getTitle(item.title, locale)
97
+ title: getTitle(item.title, locale),
98
+ link: getLink(item.link, locale)
76
99
  });
77
100
  }, 'items');
78
101
  };
package/lib/utils.js CHANGED
@@ -42,13 +42,13 @@ const countRecursive = function countRecursive(array) {
42
42
  let counter = 0;
43
43
  mapRecursive(array, () => counter++, childrenKey);
44
44
  return counter;
45
- }; // "http://", "https://", "//" 开头的 3 种情况
45
+ }; // "http://", "https://" 2 种情况
46
46
 
47
47
 
48
48
  exports.countRecursive = countRecursive;
49
49
 
50
50
  const isUrl = str => {
51
- return /^(https?:)?\/\//.test(str);
51
+ return /^https?:\/\//.test(str);
52
52
  };
53
53
  /**
54
54
  * 检测 path 是否匹配当前 location, path 只考虑 "/" 开头的相对路径
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "2.4.4",
3
+ "version": "2.4.7",
4
4
  "description": "Some useful front-end web components that can be used in Blocklets.",
5
5
  "keywords": [
6
6
  "react",
@@ -30,8 +30,8 @@
30
30
  "url": "https://github.com/ArcBlock/ux/issues"
31
31
  },
32
32
  "dependencies": {
33
- "@arcblock/did-connect": "^2.4.4",
34
- "@arcblock/ux": "^2.4.4",
33
+ "@arcblock/did-connect": "^2.4.7",
34
+ "@arcblock/ux": "^2.4.7",
35
35
  "@emotion/react": "^11.10.0",
36
36
  "@emotion/styled": "^11.10.0",
37
37
  "@iconify/iconify": "^2.2.1",
@@ -53,5 +53,5 @@
53
53
  "eslint-plugin-react-hooks": "^4.6.0",
54
54
  "jest": "^24.9.0"
55
55
  },
56
- "gitHead": "655d38787fa04e77a719601a46b93d49cde243e4"
56
+ "gitHead": "bf9bb3c3501333a9e218f6a78892431769530e41"
57
57
  }
@@ -20,6 +20,8 @@ Copyright.defaultProps = {
20
20
  };
21
21
 
22
22
  const Root = styled('p')`
23
+ display: flex;
24
+ align-items: center;
23
25
  margin: 0;
24
26
  font-size: 13px;
25
27
  `;
@@ -8,22 +8,26 @@ import PlainLayout from './layout/plain';
8
8
 
9
9
  const layouts = [
10
10
  {
11
+ name: 'plain',
11
12
  // navigation 数据为空时, 使用简单布局
12
13
  support: (_, data) => !data.navigation?.length,
13
14
  component: PlainLayout,
14
15
  },
15
16
  {
17
+ name: 'standard',
16
18
  // 默认标准布局
17
19
  support: () => true,
18
20
  component: StandardLayout,
19
21
  },
20
22
  ];
21
23
 
24
+ const layoutsKeyByName = layouts.reduce((acc, cur) => ({ ...acc, [cur.name]: cur }), {});
25
+
22
26
  /**
23
27
  * 通用的内部 footer 组件, 定义并渲染常见的几种 footer 元素: brand/navigation/social medial 等
24
28
  */
25
29
  function InternalFooter(props) {
26
- const { brand, navigation, socialMedia, copyright, links, ...rest } = props;
30
+ const { brand, navigation, socialMedia, copyright, links, layout, ...rest } = props;
27
31
  const renderBrand = () => {
28
32
  return brand ? <Brand {...brand} /> : null;
29
33
  };
@@ -52,8 +56,16 @@ function InternalFooter(props) {
52
56
  copyright: renderCopyright(),
53
57
  links: renderLinks(),
54
58
  };
55
- const LayoutComponent = layouts.find((layout) => layout.support(elements, props)).component;
56
- return <LayoutComponent elements={elements} {...rest} />;
59
+ let LayoutComponent = null;
60
+ if (layout === 'auto') {
61
+ LayoutComponent = layouts.find((item) => item.support(elements, props)).component;
62
+ } else {
63
+ LayoutComponent = layoutsKeyByName[layout]?.component;
64
+ }
65
+ if (!LayoutComponent) {
66
+ throw new Error(`layout ${layout} is not supported.`);
67
+ }
68
+ return <LayoutComponent elements={elements} data={props} {...rest} />;
57
69
  }
58
70
 
59
71
  InternalFooter.propTypes = {
@@ -85,6 +97,8 @@ InternalFooter.propTypes = {
85
97
  link: PropTypes.string,
86
98
  })
87
99
  ),
100
+ // 可显式指定 footer layout, 默认根据内容自动决定 layout
101
+ layout: PropTypes.oneOf(['auto', 'standard', 'plain']),
88
102
  };
89
103
 
90
104
  InternalFooter.defaultProps = {
@@ -93,6 +107,7 @@ InternalFooter.defaultProps = {
93
107
  copyright: null,
94
108
  socialMedia: null,
95
109
  links: null,
110
+ layout: 'auto',
96
111
  };
97
112
 
98
113
  export default InternalFooter;
@@ -2,16 +2,27 @@ import { cloneElement } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import Container from '@mui/material/Container';
4
4
  import { styled } from '@arcblock/ux/lib/Theme';
5
+ import Row from './row';
5
6
 
6
7
  /**
7
8
  * footer plain layout
8
9
  */
9
- function PlainLayout({ elements, ...rest }) {
10
+ function PlainLayout({ elements, data, ...rest }) {
10
11
  return (
11
12
  <Root {...rest}>
12
13
  <Container className="plain-layout-container">
13
- {cloneElement(elements.brand, { name: null, description: null })}
14
- {elements.copyright}
14
+ {!!data.links && (
15
+ <Row sx={{ width: 1 }} autoCenter>
16
+ {elements.copyright}
17
+ {elements.links}
18
+ </Row>
19
+ )}
20
+ {!data.links && (
21
+ <>
22
+ {cloneElement(elements.brand, { name: null, description: null })}
23
+ {elements.copyright}
24
+ </>
25
+ )}
15
26
  </Container>
16
27
  </Root>
17
28
  );
@@ -25,6 +36,7 @@ PlainLayout.propTypes = {
25
36
  copyright: PropTypes.element,
26
37
  links: PropTypes.element,
27
38
  }).isRequired,
39
+ data: PropTypes.object.isRequired,
28
40
  };
29
41
 
30
42
  const Root = styled('div')`
@@ -1,4 +1,5 @@
1
1
  import PropTypes from 'prop-types';
2
+ import Box from '@mui/material/Box';
2
3
  import { useTheme, styled } from '@arcblock/ux/lib/Theme';
3
4
  import clsx from 'clsx';
4
5
 
@@ -24,7 +25,7 @@ Row.defaultProps = {
24
25
  autoCenter: false,
25
26
  };
26
27
 
27
- const RowRoot = styled('div')`
28
+ const RowRoot = styled(Box)`
28
29
  display: flex;
29
30
  justify-content: space-between;
30
31
  & + & {
@@ -8,18 +8,25 @@ import Row from './row';
8
8
  /**
9
9
  * footer standard layout
10
10
  */
11
- function Footer({ elements, ...rest }) {
11
+ function StandardLayout({ elements, data, ...rest }) {
12
12
  return (
13
13
  <Root {...rest}>
14
14
  <Container>
15
- <Row>
16
- <Box>
17
- <Box width={{ md: 240 }}>{elements.brand}</Box>
18
- <Box mt={2}>{elements.socialMedia}</Box>
15
+ <Box
16
+ sx={{
17
+ display: 'flex',
18
+ justifyContent: 'space-between',
19
+ pb: 3,
20
+ }}>
21
+ <Box>{elements.brand}</Box>
22
+ <Box lineHeight={1} alignSelf="end">
23
+ {elements.socialMedia}
19
24
  </Box>
20
- {elements.navigation}
21
- </Row>
22
- <Row autoCenter style={{ marginTop: 72 }}>
25
+ </Box>
26
+ {!!data.navigation?.length && (
27
+ <Box sx={{ mb: 6, pt: 3, borderTop: 1, borderColor: 'grey.200' }}>{elements.navigation}</Box>
28
+ )}
29
+ <Row sx={{ pt: 3, borderTop: 1, borderColor: 'grey.200' }} autoCenter>
23
30
  {elements.copyright}
24
31
  {elements.links}
25
32
  </Row>
@@ -28,7 +35,7 @@ function Footer({ elements, ...rest }) {
28
35
  );
29
36
  }
30
37
 
31
- Footer.propTypes = {
38
+ StandardLayout.propTypes = {
32
39
  elements: PropTypes.shape({
33
40
  brand: PropTypes.element,
34
41
  navigation: PropTypes.element,
@@ -36,10 +43,14 @@ Footer.propTypes = {
36
43
  copyright: PropTypes.element,
37
44
  links: PropTypes.element,
38
45
  }).isRequired,
46
+ data: PropTypes.object.isRequired,
39
47
  };
40
48
 
41
49
  const Root = styled('div')`
42
- padding: 48px 0;
50
+ padding: 32px 0 24px 0;
51
+ .footer-brand-desc {
52
+ display: none;
53
+ }
43
54
  `;
44
55
 
45
- export default Footer;
56
+ export default StandardLayout;
@@ -3,6 +3,8 @@ import { useState } from 'react';
3
3
  import PropTypes from 'prop-types';
4
4
  import { useTheme, styled } from '@arcblock/ux/lib/Theme';
5
5
  import clsx from 'clsx';
6
+ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
7
+ import Icon from '../Icon';
6
8
 
7
9
  /**
8
10
  * footer 中的 links (支持分组, 最多支持 2 级)
@@ -16,7 +18,7 @@ export default function Links({ links, flowLayout, ...rest }) {
16
18
  }
17
19
  // 只要发现一项元素有子元素, 就认为是分组 (大字号突出 group title)
18
20
  const isGroupMode = links.some((item) => item.items?.length);
19
- const renderItem = ({ label, link, render, props }) => {
21
+ const renderItem = ({ label, link, icon, render, props }) => {
20
22
  let result = label;
21
23
  if (render) {
22
24
  result = render({ label, link, props });
@@ -27,9 +29,13 @@ export default function Links({ links, flowLayout, ...rest }) {
27
29
  </a>
28
30
  );
29
31
  }
30
- return result;
32
+ return (
33
+ <>
34
+ <Icon icon={icon} size={20} sx={{ mr: 0.5 }} />
35
+ {result}
36
+ </>
37
+ );
31
38
  };
32
-
33
39
  return (
34
40
  <Root
35
41
  {...rest}
@@ -48,14 +54,26 @@ export default function Links({ links, flowLayout, ...rest }) {
48
54
  {!flowLayout &&
49
55
  links.map((item, i) => {
50
56
  const { items } = item;
57
+ const isActive = i === activeIndex;
51
58
  return (
52
59
  <div
53
60
  key={i}
54
61
  className={clsx('footer-links-group', {
55
- 'footer-links-group--active': i === activeIndex,
62
+ 'footer-links-group--active': isActive,
56
63
  })}
57
64
  onClick={() => setActiveIndex(activeIndex === i ? -1 : i)}>
58
- <span className="footer-links-item">{renderItem(item)}</span>
65
+ <span className="footer-links-item">
66
+ {renderItem(item)}
67
+ {!!items?.length && (
68
+ <span className="footer-links-group-expand-icon">
69
+ <ExpandMoreIcon
70
+ style={{
71
+ transform: `rotate(${isActive ? 180 : 0}deg)`,
72
+ }}
73
+ />
74
+ </span>
75
+ )}
76
+ </span>
59
77
  {!!items?.length && (
60
78
  <div className="footer-links-sub">
61
79
  {items.map((child, j) => (
@@ -104,15 +122,25 @@ const Root = styled('div')`
104
122
  display: flex;
105
123
  flex-direction: column;
106
124
  }
125
+ .footer-links-group-expand-icon {
126
+ display: none;
127
+ position: absolute;
128
+ right: 16px;
129
+ top: 50%;
130
+ transform: translate(0, -50%);
131
+ svg {
132
+ width: auto;
133
+ height: 0.75em;
134
+ }
135
+ }
107
136
  .footer-links-item {
108
- display: inline-block;
137
+ display: inline-flex;
138
+ align-items: center;
139
+ position: relative;
109
140
  padding: 0 8px;
110
141
  font-size: 14px;
111
142
  line-height: 1.6;
112
143
  }
113
- .footer-links-group + .footer-links-group {
114
- margin-left: 128px;
115
- }
116
144
  &.footer-links--grouped {
117
145
  .footer-links-group {
118
146
  > .footer-links-item {
@@ -124,6 +152,8 @@ const Root = styled('div')`
124
152
  }
125
153
  }
126
154
  a {
155
+ display: inline-block;
156
+ max-width: 150px;
127
157
  color: inherit;
128
158
  text-decoration: none;
129
159
  &:hover {
@@ -140,22 +170,19 @@ const Root = styled('div')`
140
170
  .footer-links-item {
141
171
  padding: 0 8px;
142
172
  }
173
+ .footer-links-item + .footer-links-item::before {
174
+ content: '';
175
+ position: absolute;
176
+ left: 0;
177
+ top: 50%;
178
+ transform: translate(0, -50%);
179
+ height: 1em;
180
+ border-left: 1px solid ${(props) => props.theme.palette.grey[400]};
181
+ }
143
182
  }
144
183
  }
145
184
 
146
- ${(props) => props.$theme.breakpoints.down('lg')} {
147
- .footer-links-group + .footer-links-group {
148
- margin-left: 56px;
149
- }
150
- }
151
-
152
- ${(props) => props.$theme.breakpoints.down('lg')} {
153
- .footer-links-group + .footer-links-group {
154
- margin-left: 40px;
155
- }
156
- }
157
-
158
- ${(props) => props.$theme.breakpoints.down('sm')} {
185
+ ${(props) => props.$theme.breakpoints.down('md')} {
159
186
  .footer-links-inner {
160
187
  flex-direction: column;
161
188
  margin: 0;
@@ -164,11 +191,14 @@ const Root = styled('div')`
164
191
  display: none;
165
192
  }
166
193
  .footer-links-group {
194
+ position: relative;
167
195
  padding: 12px 0;
168
- border-top: 1px solid ${(props) => props.$theme.palette.grey[200]};
196
+ .footer-links-item .footer-links-group-expand-icon {
197
+ display: inline-block;
198
+ }
169
199
  }
170
200
  .footer-links-group + .footer-links-group {
171
- margin-left: 0;
201
+ border-top: 1px solid ${(props) => props.$theme.palette.grey[200]};
172
202
  }
173
203
  .footer-links-group--active {
174
204
  .footer-links-sub {
@@ -16,8 +16,7 @@ export default function SocialMedia({ items, ...rest }) {
16
16
  <Icon
17
17
  icon={item.icon}
18
18
  sx={{ bgcolor: theme.palette.grey[600], color: '#fff' }}
19
- size={44}
20
- variant="rounded"
19
+ size={24}
21
20
  component="span"
22
21
  />
23
22
  </a>
@@ -44,7 +43,13 @@ SocialMedia.defaultProps = {
44
43
  const Root = styled('div')`
45
44
  display: inline-flex;
46
45
  align-items: center;
46
+ a {
47
+ color: ${(props) => props.theme.palette.grey[400]};
48
+ &:hover {
49
+ color: ${(props) => props.theme.palette.primary.light};
50
+ }
51
+ }
47
52
  a + a {
48
- margin-left: 12px;
53
+ margin-left: 20px;
49
54
  }
50
55
  `;
@@ -207,6 +207,11 @@ const StyledUxHeader = styled(ResponsiveHeader)`
207
207
  padding-top: 0;
208
208
  }
209
209
  }
210
+ .navmenu-item-icon > .MuiAvatar-root,
211
+ .navmenu-sub-icon > .MuiAvatar-root {
212
+ width: 20px;
213
+ height: 20px;
214
+ }
210
215
  }
211
216
  `;
212
217
 
package/src/Icon/index.js CHANGED
@@ -19,6 +19,11 @@ export default function Icon({ icon, size, sx, ...rest }) {
19
19
  backgroundColor: 'transparent',
20
20
  borderRadius: 0,
21
21
  },
22
+ // 无 icon 背景时, svg icon 尺寸与窗口尺寸一致
23
+ '&.MuiAvatar-root svg': {
24
+ width: '100%',
25
+ height: '100%',
26
+ },
22
27
  });
23
28
  }
24
29
  if (isUrl(icon)) {
package/src/blocklets.js CHANGED
@@ -1,4 +1,4 @@
1
- import { mapRecursive } from './utils';
1
+ import { mapRecursive, isUrl } from './utils';
2
2
 
3
3
  export const publicPath = window?.blocklet?.groupPrefix || window?.blocklet?.prefix || '/';
4
4
 
@@ -37,12 +37,31 @@ export const getLocalizedNavigation = (navigation, locale = 'en') => {
37
37
  }
38
38
  return title;
39
39
  };
40
+ // eslint-disable-next-line no-shadow
41
+ const getLink = (link, locale) => {
42
+ if (typeof link === 'string') {
43
+ // http[s] 开头的 url
44
+ if (isUrl(link)) {
45
+ const url = new URL(link);
46
+ url.searchParams.set('locale', locale);
47
+ return url.href;
48
+ }
49
+ const url = new URL(link, window.location.origin);
50
+ url.searchParams.set('locale', locale);
51
+ return url.pathname + url.search;
52
+ }
53
+ if (typeof link === 'object') {
54
+ return link[locale] || link?.en || link?.zh;
55
+ }
56
+ return link;
57
+ };
40
58
  return mapRecursive(
41
59
  navigation,
42
60
  (item) => {
43
61
  return {
44
62
  ...item,
45
63
  title: getTitle(item.title, locale),
64
+ link: getLink(item.link, locale),
46
65
  };
47
66
  },
48
67
  'items'
package/src/utils.js CHANGED
@@ -24,9 +24,9 @@ export const countRecursive = (array, childrenKey = 'children') => {
24
24
  return counter;
25
25
  };
26
26
 
27
- // "http://", "https://", "//" 开头的 3 种情况
27
+ // "http://", "https://" 2 种情况
28
28
  export const isUrl = (str) => {
29
- return /^(https?:)?\/\//.test(str);
29
+ return /^https?:\/\//.test(str);
30
30
  };
31
31
 
32
32
  /**