@arcblock/ux 2.4.17 → 2.4.18

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.
@@ -75,8 +75,13 @@ function NavMenuWrapper(_ref) {
75
75
 
76
76
  function formatLinks(links, location) {
77
77
  return links.map(link => {
78
- if (Array.isArray(link)) {
79
- return formatLinks(link, location);
78
+ var _link$children;
79
+
80
+ if ((_link$children = link.children) !== null && _link$children !== void 0 && _link$children.length) {
81
+ return _objectSpread(_objectSpread({}, link), {}, {
82
+ label: link.title,
83
+ children: formatLinks(link.children, location)
84
+ });
80
85
  }
81
86
 
82
87
  return _objectSpread(_objectSpread({}, link), {}, {
@@ -108,9 +113,13 @@ function Dashboard(_ref2) {
108
113
  const theme = (0, _Theme.useTheme)();
109
114
  const location = (0, _reactRouterDom.useLocation)();
110
115
  const navItems = (0, _react.useMemo)(() => formatLinks(links, location), [location, links]);
111
- const flattendNavItems = navItems.flat();
116
+ const isGroupedMode = navItems.some(item => {
117
+ var _item$children;
118
+
119
+ return !!((_item$children = item.children) !== null && _item$children !== void 0 && _item$children.length);
120
+ }); // 一级菜单数量 > 8 或都分组模式, 都启用 dense 布局
112
121
 
113
- const _dense = dense === 'auto' ? flattendNavItems.length >= 8 : dense;
122
+ const _dense = dense === 'auto' ? navItems.length >= 8 || isGroupedMode : dense;
114
123
 
115
124
  const classes = (0, _clsx.default)('dashboard', {
116
125
  'dashboard-dense': _dense
@@ -120,7 +129,7 @@ function Dashboard(_ref2) {
120
129
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactHelmet.default, {
121
130
  title: title
122
131
  }, title), /*#__PURE__*/(0, _jsxRuntime.jsx)(StyledUxHeader, _objectSpread(_objectSpread({}, headerProps), {}, {
123
- $theme: theme,
132
+ className: "dashboard-header",
124
133
  children: _ref3 => {
125
134
  let {
126
135
  isMobile,
@@ -130,7 +139,7 @@ function Dashboard(_ref2) {
130
139
  if (isMobile) {
131
140
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(NavMenuWrapper, {
132
141
  mode: "inline",
133
- items: flattendNavItems,
142
+ items: navItems,
134
143
  closeMenu: closeMenu,
135
144
  bgColor: "transparent",
136
145
  activeTextColor: theme.palette.primary.main
@@ -183,7 +192,7 @@ Dashboard.defaultProps = {
183
192
  sidebarWidth: 120,
184
193
  dense: 'auto'
185
194
  };
186
- const Wrapper = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n &.dashboard {\n display: flex;\n flex-direction: column;\n height: 100vh;\n }\n .dashboard-body {\n overflow: hidden;\n flex: 1;\n }\n .dashboard-sidebar {\n box-sizing: border-box;\n flex: 0 0 auto;\n width: ", "px;\n &:hover {\n overflow-y: auto;\n }\n }\n .dashboard-main {\n display: flex;\n flex-direction: column;\n overflow: auto;\n flex: 1;\n }\n .dashboard-content {\n flex: 1;\n }\n &.dashboard-dense {\n .dashboard-sidebar {\n width: auto;\n }\n }\n ", " {\n .header-logo {\n display: flex;\n justify-content: center;\n /* logo \u4E0E sidebar \u4E2D\u7684 icon \u5782\u76F4\u5BF9\u9F50, sidebarWidth - 24 * 2 */\n width: ", "px;\n }\n &.dashboard-dense {\n .header-logo {\n /* dense = true \u65F6 logo \u4E0E sidenav icons \u4E0D\u9700\u8981\u5BF9\u9F50 */\n width: auto;\n }\n }\n }\n"])), props => props.sidebarWidth, props => props.theme.breakpoints.up('md'), props => props.sidebarWidth - 24 * 2);
195
+ const Wrapper = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n &.dashboard {\n display: flex;\n flex-direction: column;\n height: 100vh;\n }\n .dashboard-body {\n overflow: hidden;\n flex: 1;\n }\n .dashboard-sidebar {\n box-sizing: border-box;\n flex: 0 0 auto;\n width: ", "px;\n overflow: hidden;\n &:hover {\n overflow-y: auto;\n }\n }\n .dashboard-main {\n display: flex;\n flex-direction: column;\n overflow: auto;\n flex: 1;\n }\n .dashboard-content {\n flex: 1;\n }\n &.dashboard-dense {\n .dashboard-header {\n border-bottom: 1px solid #eee;\n }\n .dashboard-sidebar {\n width: 256px;\n border-right: 1px solid #eee;\n }\n }\n ", " {\n .header-logo {\n display: flex;\n justify-content: center;\n /* logo \u4E0E sidebar \u4E2D\u7684 icon \u5782\u76F4\u5BF9\u9F50, sidebarWidth - 24 * 2 */\n width: ", "px;\n }\n &.dashboard-dense {\n .header-logo {\n /* dense = true \u65F6 logo \u4E0E sidenav icons \u4E0D\u9700\u8981\u5BF9\u9F50 */\n width: auto;\n }\n }\n }\n"])), props => props.sidebarWidth, props => props.theme.breakpoints.up('md'), props => props.sidebarWidth - 24 * 2);
187
196
  const StyledUxHeader = (0, _Theme.styled)(_Header.ResponsiveHeader)(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n .header-container {\n max-width: 100%;\n }\n"]))); // 兼容旧版 dashboard
188
197
 
189
198
  function DashboardWrapper(_ref4) {
@@ -37,26 +37,23 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
37
37
 
38
38
  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; }
39
39
 
40
- // 渲染 links, 为 group links 添加分隔线, 返回展平后的、包含分隔线元素的 links 数组
41
- function renderLinks(links) {
42
- const result = [];
43
- links.forEach((item, index) => {
44
- const prev = links[index - 1]; // 当当前元素和前一个元素仅有一个为 group (array) 时, 在当前元素前面位置添加一个分隔线元素
45
-
46
- if (index > 0 && (Array.isArray(prev) && !Array.isArray(item) || Array.isArray(item) && !Array.isArray(prev))) {
47
- result.push( /*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
48
- className: "layout-sidebar-divider"
49
- }, "divider-after-".concat(index)));
50
- }
51
-
52
- result.push(renderLinksItem(item));
53
- });
54
- return result.flat();
40
+ function renderGroup(item) {
41
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)("li", {
42
+ className: "layout-sidebar-group",
43
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
44
+ className: "layout-sidebar-group-title",
45
+ children: item.title
46
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("ul", {
47
+ children: item.children.map(renderItem)
48
+ })]
49
+ }, "group-".concat(item.title));
55
50
  }
56
51
 
57
- function renderLinksItem(item) {
58
- if (Array.isArray(item)) {
59
- return item.map(renderLinksItem);
52
+ function renderItem(item) {
53
+ var _item$children;
54
+
55
+ if ((_item$children = item.children) !== null && _item$children !== void 0 && _item$children.length) {
56
+ return renderGroup(item);
60
57
  }
61
58
 
62
59
  const {
@@ -69,6 +66,7 @@ function renderLinksItem(item) {
69
66
  } = item; // external = true 时 link active 状态由传入 links 的调用方决定 (适用于 blocklet ui dashboard 的情况)
70
67
 
71
68
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
69
+ className: "layout-sidebar-item",
72
70
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_externalLink.NavLink, {
73
71
  external: external,
74
72
  to: url,
@@ -80,7 +78,7 @@ function renderLinksItem(item) {
80
78
  'layout-sidebar-link--active': external ? active : isActive
81
79
  });
82
80
  },
83
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
81
+ children: [icon && /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
84
82
  className: "layout-sidebar-icon ".concat(showBadge ? 'layout-sidebar-badge' : ''),
85
83
  children: icon
86
84
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
@@ -105,7 +103,7 @@ function Sidebar(_ref2) {
105
103
  'layout-sidebar-dense': dense
106
104
  }),
107
105
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("ul", {
108
- children: renderLinks(links)
106
+ children: links.map(renderItem)
109
107
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
110
108
  style: {
111
109
  flex: 1
@@ -124,6 +122,6 @@ Sidebar.defaultProps = {
124
122
  dense: false
125
123
  };
126
124
  const gradient = 'linear-gradient(32deg, rgba(144, 255, 230, 0.1), rgba(144, 255, 230, 0))';
127
- const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n .layout-sidebar-link {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 22px 24px;\n color: ", ";\n text-decoration: none;\n\n &:hover,\n &.layout-sidebar-link--active {\n color: ", ";\n background: ", ";\n border-left-color: ", ";\n }\n }\n .layout-sidebar-icon {\n display: inline-block;\n width: 32px;\n height: 32px;\n > img,\n > svg {\n width: 32px;\n height: 32px;\n }\n }\n .layout-sidebar-badge {\n position: relative;\n &:after {\n content: '';\n position: absolute;\n width: 10px;\n height: 10px;\n border-radius: 10px;\n background-color: #fe4e44;\n right: -2px;\n top: 0;\n box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3), 0px 0px 20px rgba(0, 0, 0, 0.1);\n }\n }\n .layout-sidebar-link-text {\n margin-top: 8px;\n font-size: 12px;\n font-weight: 500;\n text-align: center;\n text-transform: capitalize;\n letter-spacing: normal;\n }\n .layout-sidebar-divider {\n display: flex;\n justify-content: center;\n align-items: center;\n &::after {\n content: '';\n display: inline-block;\n width: 80px;\n max-width: 100%;\n border-bottom: 1px solid #ddd;\n }\n }\n &.layout-sidebar-dense {\n box-sizing: border-box;\n height: 100%;\n padding-top: 28px;\n border-right: 1px solid #ddd;\n .layout-sidebar-link {\n flex-direction: row;\n align-items: center;\n padding: 10px 36px 10px 24px;\n }\n .layout-sidebar-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n margin-right: 12px;\n > img,\n > svg {\n width: 20px;\n height: 20px;\n }\n }\n .layout-sidebar-badge {\n &:after {\n width: 6px;\n height: 6px;\n border-radius: 6px;\n right: -2px;\n top: 0;\n }\n }\n .layout-sidebar-link-text {\n margin-top: 0;\n font-size: 14px;\n }\n .layout-sidebar-divider {\n &::after {\n content: '';\n display: inline-block;\n width: 100%;\n margin: 12px 0;\n }\n }\n }\n"])), props => props.theme.palette.text.secondary, props => props.theme.palette.primary.main, gradient, _colors.teal.A700);
125
+ const Root = (0, _Theme.styled)('div')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n .layout-sidebar-link {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 22px 24px;\n color: ", ";\n text-decoration: none;\n\n &:hover,\n &.layout-sidebar-link--active {\n color: ", ";\n background: ", ";\n border-left-color: ", ";\n }\n }\n .layout-sidebar-icon {\n display: inline-block;\n width: 32px;\n height: 32px;\n > img,\n > svg {\n width: 32px;\n height: 32px;\n }\n }\n .layout-sidebar-badge {\n position: relative;\n &:after {\n content: '';\n position: absolute;\n width: 10px;\n height: 10px;\n border-radius: 10px;\n background-color: #fe4e44;\n right: -2px;\n top: 0;\n box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.3), 0px 0px 20px rgba(0, 0, 0, 0.1);\n }\n }\n .layout-sidebar-link-text {\n margin-top: 8px;\n font-size: 12px;\n font-weight: 500;\n text-align: center;\n text-transform: capitalize;\n letter-spacing: normal;\n }\n &.layout-sidebar-dense {\n box-sizing: border-box;\n padding: 20px 0;\n font-weight: bold;\n .layout-sidebar-item {\n padding: 0 16px;\n }\n .layout-sidebar-item + .layout-sidebar-item {\n margin-top: 1px;\n }\n .layout-sidebar-link {\n box-sizing: border-box;\n flex-direction: row;\n align-items: center;\n width: 100%;\n padding: 8px;\n &:hover,\n &.layout-sidebar-link--active {\n color: ", ";\n background: ", ";\n border: 0;\n border-radius: 4px;\n }\n }\n .layout-sidebar-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n margin-right: 8px;\n > img,\n > svg {\n width: 20px;\n height: 20px;\n }\n }\n .layout-sidebar-badge {\n &:after {\n width: 6px;\n height: 6px;\n border-radius: 6px;\n right: -2px;\n top: 0;\n }\n }\n .layout-sidebar-link-text {\n margin-top: 0;\n font-size: 14px;\n }\n .layout-sidebar-group {\n color: ", ";\n .layout-sidebar-group-title {\n display: inline-block;\n padding: 8px 0 8px 24px;\n font-size: 13px;\n text-transform: uppercase;\n }\n }\n .layout-sidebar-group + .layout-sidebar-group,\n .layout-sidebar-group + .layout-sidebar-item,\n .layout-sidebar-item + .layout-sidebar-group {\n margin-top: 16px;\n }\n }\n"])), props => props.theme.palette.text.secondary, props => props.theme.palette.primary.main, gradient, _colors.teal.A700, props => props.theme.palette.grey[900], props => props.theme.palette.grey[100], props => props.theme.palette.text.hint);
128
126
  var _default = Sidebar;
129
127
  exports.default = _default;
@@ -11,7 +11,7 @@ var _templateObject, _templateObject2, _templateObject3;
11
11
 
12
12
  function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
13
13
 
14
- const NavMenuBase = (0, _Theme.styled)('nav')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n background-color: ", ";\n font-size: 16px;\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n .navmenu-item,\n .navmenu-sub {\n display: flex;\n align-items: center;\n }\n a {\n color: inherit;\n }\n /* active/hover */\n .navmenu-item,\n .navmenu-sub {\n color: ", ";\n }\n .navmenu-item--active,\n .navmenu-item:hover,\n .navmenu-sub--opened {\n color: ", ";\n }\n\n .navmenu-item {\n position: relative;\n cursor: pointer;\n transition: color 0.2s ease-in-out;\n a {\n text-decoration: none;\n white-space: nowrap;\n }\n a::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n background-color: transparent;\n content: '';\n }\n }\n\n .navmenu-sub {\n position: relative;\n cursor: pointer;\n }\n /* icon & expand icon */\n .navmenu-item-icon,\n .navmenu-sub-icon,\n .navmenu-sub-expand-icon {\n display: flex;\n line-height: 1;\n }\n .navmenu-item-icon,\n .navmenu-sub-icon {\n margin-right: 4px;\n }\n .navmenu-item-icon > *,\n .navmenu-sub-icon > * {\n width: auto;\n height: 22px;\n max-height: 22px;\n font-size: 1.5em;\n }\n .navmenu-sub-expand-icon {\n margin-left: 8px;\n > * {\n width: 0.8em;\n height: 0.8em;\n transition: transform 0.2s ease-in-out;\n }\n }\n"])), props => props.$bgColor, props => props.$textColor, props => props.$activeTextColor);
14
+ const NavMenuBase = (0, _Theme.styled)('nav')(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n background-color: ", ";\n font-size: 16px;\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n .navmenu-item,\n .navmenu-sub {\n display: flex;\n align-items: center;\n }\n a {\n color: inherit;\n text-decoration: none;\n }\n /* active/hover */\n .navmenu-item,\n .navmenu-sub {\n color: ", ";\n }\n .navmenu-item--active,\n .navmenu-item:hover,\n .navmenu-sub--opened {\n color: ", ";\n }\n\n .navmenu-item {\n position: relative;\n cursor: pointer;\n transition: color 0.2s ease-in-out;\n a {\n white-space: nowrap;\n }\n a::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n background-color: transparent;\n content: '';\n }\n }\n\n .navmenu-sub {\n position: relative;\n cursor: pointer;\n }\n /* icon & expand icon */\n .navmenu-item-icon,\n .navmenu-sub-icon,\n .navmenu-sub-expand-icon {\n display: flex;\n line-height: 1;\n }\n .navmenu-item-icon,\n .navmenu-sub-icon {\n margin-right: 4px;\n }\n .navmenu-item-icon > *,\n .navmenu-sub-icon > * {\n width: auto;\n height: 22px;\n max-height: 22px;\n font-size: 1.5em;\n }\n .navmenu-sub-expand-icon {\n margin-left: 8px;\n > * {\n width: 0.8em;\n height: 0.8em;\n transition: transform 0.2s ease-in-out;\n }\n }\n"])), props => props.$bgColor, props => props.$textColor, props => props.$activeTextColor);
15
15
  const HorizontalStyle = (0, _Theme.styled)(NavMenuBase)(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n padding: 8px 16px;\n .navmenu-root {\n display: flex;\n align-items: center;\n }\n /* \u9876\u7EA7\u83DC\u5355\u95F4\u9694 */\n .navmenu-root > .navmenu-item,\n .navmenu-root > .navmenu-sub {\n margin-left: 24px;\n }\n .navmenu-root > .navmenu-item:first-child,\n .navmenu-root > .navmenu-sub:first-child {\n margin-left: 0;\n }\n\n /* \u5B50\u7EA7\u5217\u8868 */\n .navmenu-sub-container {\n display: none;\n position: absolute;\n top: 100%;\n }\n .navmenu-sub-list {\n padding: 16px;\n border-radius: 4px;\n background: #fff;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n .navmenu-item + .navmenu-item {\n margin-top: 8px;\n }\n }\n /* \u4E8C\u7EA7 sub menu */\n .navmenu-root > .navmenu-sub {\n > .navmenu-sub-container {\n left: 50%;\n transform: translateX(-50%);\n padding-top: 16px;\n }\n &.navmenu-sub--opened > .navmenu-sub-container {\n display: block;\n }\n }\n"])));
16
16
  /* inline mode */
17
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/ux",
3
- "version": "2.4.17",
3
+ "version": "2.4.18",
4
4
  "description": "Common used react components for arcblock products",
5
5
  "keywords": [
6
6
  "react",
@@ -47,10 +47,10 @@
47
47
  "react": ">=18.1.0",
48
48
  "react-ga": "^2.7.0"
49
49
  },
50
- "gitHead": "074d03aa938b1a9f52d2d9ef2bb8d68bb08d061e",
50
+ "gitHead": "45fe2e3b1726e2172aad280f62b81dea7e353f1b",
51
51
  "dependencies": {
52
- "@arcblock/icons": "^2.4.17",
53
- "@arcblock/react-hooks": "^2.4.17",
52
+ "@arcblock/icons": "^2.4.18",
53
+ "@arcblock/react-hooks": "^2.4.18",
54
54
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
55
55
  "@emotion/react": "^11.10.0",
56
56
  "@emotion/styled": "^11.10.0",
@@ -28,8 +28,12 @@ function NavMenuWrapper({ closeMenu, ...rest }) {
28
28
 
29
29
  function formatLinks(links, location) {
30
30
  return links.map((link) => {
31
- if (Array.isArray(link)) {
32
- return formatLinks(link, location);
31
+ if (link.children?.length) {
32
+ return {
33
+ ...link,
34
+ label: link.title,
35
+ children: formatLinks(link.children, location),
36
+ };
33
37
  }
34
38
  return {
35
39
  ...link,
@@ -48,21 +52,22 @@ function Dashboard({ children, title, headerProps, links, fullWidth, dense, ...r
48
52
  const theme = useTheme();
49
53
  const location = useLocation();
50
54
  const navItems = useMemo(() => formatLinks(links, location), [location, links]);
51
- const flattendNavItems = navItems.flat();
52
- const _dense = dense === 'auto' ? flattendNavItems.length >= 8 : dense;
55
+ const isGroupedMode = navItems.some((item) => !!item.children?.length);
56
+ // 一级菜单数量 > 8 或都分组模式, 都启用 dense 布局
57
+ const _dense = dense === 'auto' ? navItems.length >= 8 || isGroupedMode : dense;
53
58
  const classes = clsx('dashboard', { 'dashboard-dense': _dense }, rest.className);
54
59
 
55
60
  return (
56
61
  <Wrapper {...rest} className={classes}>
57
62
  <Helmet title={title} key={title} />
58
63
 
59
- <StyledUxHeader {...headerProps} $theme={theme}>
64
+ <StyledUxHeader {...headerProps} className="dashboard-header">
60
65
  {({ isMobile, closeMenu }) => {
61
66
  if (isMobile) {
62
67
  return (
63
68
  <NavMenuWrapper
64
69
  mode="inline"
65
- items={flattendNavItems}
70
+ items={navItems}
66
71
  closeMenu={closeMenu}
67
72
  bgColor="transparent"
68
73
  activeTextColor={theme.palette.primary.main}
@@ -124,6 +129,7 @@ const Wrapper = styled('div')`
124
129
  box-sizing: border-box;
125
130
  flex: 0 0 auto;
126
131
  width: ${(props) => props.sidebarWidth}px;
132
+ overflow: hidden;
127
133
  &:hover {
128
134
  overflow-y: auto;
129
135
  }
@@ -138,8 +144,12 @@ const Wrapper = styled('div')`
138
144
  flex: 1;
139
145
  }
140
146
  &.dashboard-dense {
147
+ .dashboard-header {
148
+ border-bottom: 1px solid #eee;
149
+ }
141
150
  .dashboard-sidebar {
142
- width: auto;
151
+ width: 256px;
152
+ border-right: 1px solid #eee;
143
153
  }
144
154
  }
145
155
  ${(props) => props.theme.breakpoints.up('md')} {
@@ -6,35 +6,30 @@ import clsx from 'clsx';
6
6
  import { styled } from '../../Theme';
7
7
  import { NavLink } from './external-link';
8
8
 
9
- // 渲染 links, 为 group links 添加分隔线, 返回展平后的、包含分隔线元素的 links 数组
10
- function renderLinks(links) {
11
- const result = [];
12
- links.forEach((item, index) => {
13
- const prev = links[index - 1];
14
- // 当当前元素和前一个元素仅有一个为 group (array) 时, 在当前元素前面位置添加一个分隔线元素
15
- if (index > 0 && ((Array.isArray(prev) && !Array.isArray(item)) || (Array.isArray(item) && !Array.isArray(prev)))) {
16
- result.push(<li className="layout-sidebar-divider" key={`divider-after-${index}`} />);
17
- }
18
- result.push(renderLinksItem(item));
19
- });
20
- return result.flat();
9
+ function renderGroup(item) {
10
+ return (
11
+ <li key={`group-${item.title}`} className="layout-sidebar-group">
12
+ <span className="layout-sidebar-group-title">{item.title}</span>
13
+ <ul>{item.children.map(renderItem)}</ul>
14
+ </li>
15
+ );
21
16
  }
22
17
 
23
- function renderLinksItem(item) {
24
- if (Array.isArray(item)) {
25
- return item.map(renderLinksItem);
18
+ function renderItem(item) {
19
+ if (item.children?.length) {
20
+ return renderGroup(item);
26
21
  }
27
22
  const { url, icon, title, showBadge, external, active } = item;
28
23
  // external = true 时 link active 状态由传入 links 的调用方决定 (适用于 blocklet ui dashboard 的情况)
29
24
  return (
30
- <li key={url}>
25
+ <li key={url} className="layout-sidebar-item">
31
26
  <NavLink
32
27
  external={external}
33
28
  to={url}
34
29
  className={({ isActive }) =>
35
30
  clsx('layout-sidebar-link', { 'layout-sidebar-link--active': external ? active : isActive })
36
31
  }>
37
- <span className={`layout-sidebar-icon ${showBadge ? 'layout-sidebar-badge' : ''}`}>{icon}</span>
32
+ {icon && <span className={`layout-sidebar-icon ${showBadge ? 'layout-sidebar-badge' : ''}`}>{icon}</span>}
38
33
  <Typography component="span" className="layout-sidebar-link-text">
39
34
  {title}
40
35
  </Typography>
@@ -46,7 +41,7 @@ function renderLinksItem(item) {
46
41
  function Sidebar({ links, addons, dense, ...rest }) {
47
42
  return (
48
43
  <Root {...rest} className={clsx({ 'layout-sidebar-dense': dense })}>
49
- <ul>{renderLinks(links)}</ul>
44
+ <ul>{links.map(renderItem)}</ul>
50
45
  <div style={{ flex: 1 }} />
51
46
  {addons}
52
47
  </Root>
@@ -121,33 +116,35 @@ const Root = styled('div')`
121
116
  text-transform: capitalize;
122
117
  letter-spacing: normal;
123
118
  }
124
- .layout-sidebar-divider {
125
- display: flex;
126
- justify-content: center;
127
- align-items: center;
128
- &::after {
129
- content: '';
130
- display: inline-block;
131
- width: 80px;
132
- max-width: 100%;
133
- border-bottom: 1px solid #ddd;
134
- }
135
- }
136
119
  &.layout-sidebar-dense {
137
120
  box-sizing: border-box;
138
- height: 100%;
139
- padding-top: 28px;
140
- border-right: 1px solid #ddd;
121
+ padding: 20px 0;
122
+ font-weight: bold;
123
+ .layout-sidebar-item {
124
+ padding: 0 16px;
125
+ }
126
+ .layout-sidebar-item + .layout-sidebar-item {
127
+ margin-top: 1px;
128
+ }
141
129
  .layout-sidebar-link {
130
+ box-sizing: border-box;
142
131
  flex-direction: row;
143
132
  align-items: center;
144
- padding: 10px 36px 10px 24px;
133
+ width: 100%;
134
+ padding: 8px;
135
+ &:hover,
136
+ &.layout-sidebar-link--active {
137
+ color: ${(props) => props.theme.palette.grey[900]};
138
+ background: ${(props) => props.theme.palette.grey[100]};
139
+ border: 0;
140
+ border-radius: 4px;
141
+ }
145
142
  }
146
143
  .layout-sidebar-icon {
147
144
  display: inline-block;
148
145
  width: 20px;
149
146
  height: 20px;
150
- margin-right: 12px;
147
+ margin-right: 8px;
151
148
  > img,
152
149
  > svg {
153
150
  width: 20px;
@@ -167,14 +164,20 @@ const Root = styled('div')`
167
164
  margin-top: 0;
168
165
  font-size: 14px;
169
166
  }
170
- .layout-sidebar-divider {
171
- &::after {
172
- content: '';
167
+ .layout-sidebar-group {
168
+ color: ${(props) => props.theme.palette.text.hint};
169
+ .layout-sidebar-group-title {
173
170
  display: inline-block;
174
- width: 100%;
175
- margin: 12px 0;
171
+ padding: 8px 0 8px 24px;
172
+ font-size: 13px;
173
+ text-transform: uppercase;
176
174
  }
177
175
  }
176
+ .layout-sidebar-group + .layout-sidebar-group,
177
+ .layout-sidebar-group + .layout-sidebar-item,
178
+ .layout-sidebar-item + .layout-sidebar-group {
179
+ margin-top: 16px;
180
+ }
178
181
  }
179
182
  `;
180
183
 
@@ -15,6 +15,7 @@ const NavMenuBase = styled('nav')`
15
15
  }
16
16
  a {
17
17
  color: inherit;
18
+ text-decoration: none;
18
19
  }
19
20
  /* active/hover */
20
21
  .navmenu-item,
@@ -32,7 +33,6 @@ const NavMenuBase = styled('nav')`
32
33
  cursor: pointer;
33
34
  transition: color 0.2s ease-in-out;
34
35
  a {
35
- text-decoration: none;
36
36
  white-space: nowrap;
37
37
  }
38
38
  a::before {