@blocklet/ui-react 2.4.21 → 2.4.24
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/lib/Dashboard/index.js +46 -20
- package/lib/Header/index.js +1 -0
- package/lib/blocklets.js +23 -2
- package/lib/utils.js +24 -2
- package/package.json +4 -4
- package/src/Dashboard/index.js +39 -12
- package/src/Header/index.js +1 -0
- package/src/blocklets.js +24 -1
- package/src/utils.js +16 -0
package/lib/Dashboard/index.js
CHANGED
|
@@ -7,12 +7,12 @@ exports.default = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _react = require("react");
|
|
9
9
|
|
|
10
|
+
var _Session = require("@arcblock/did-connect/lib/Session");
|
|
11
|
+
|
|
10
12
|
var _context = require("@arcblock/ux/lib/Locale/context");
|
|
11
13
|
|
|
12
14
|
var _dashboard = _interopRequireDefault(require("@arcblock/ux/lib/Layout/dashboard"));
|
|
13
15
|
|
|
14
|
-
var _ClickToCopy = _interopRequireDefault(require("@arcblock/ux/lib/ClickToCopy"));
|
|
15
|
-
|
|
16
16
|
var _Address = _interopRequireDefault(require("@arcblock/did-connect/lib/Address"));
|
|
17
17
|
|
|
18
18
|
var _Avatar = _interopRequireDefault(require("@arcblock/did-connect/lib/Avatar"));
|
|
@@ -39,17 +39,43 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
|
|
|
39
39
|
|
|
40
40
|
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; }
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* 根据 role 筛选 nav, 规则:
|
|
44
|
+
* - 如果是枝结点, 必须至少存在一个符合条件的子结点
|
|
45
|
+
* - role 未定义, 符合条件
|
|
46
|
+
* - role 定义且包括当前的 userRole, 符合条件
|
|
47
|
+
*
|
|
48
|
+
* @param {object[]} nav 导航菜单数据
|
|
49
|
+
* @param {string} userRole 当前用户 role
|
|
50
|
+
* @returns 符合 role 权限的导航菜单数据
|
|
51
|
+
*/
|
|
52
|
+
function filterNavByRole(nav, userRole) {
|
|
53
|
+
return (0, _utils.filterRecursive)(nav, (item, context) => {
|
|
54
|
+
const isRoleMatched = !item.role || userRole && item.role.includes(userRole);
|
|
55
|
+
|
|
56
|
+
if (!context.isLeaf) {
|
|
57
|
+
var _context$filteredChil;
|
|
58
|
+
|
|
59
|
+
return isRoleMatched && ((_context$filteredChil = context.filteredChildren) === null || _context$filteredChil === void 0 ? void 0 : _context$filteredChil.length);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return isRoleMatched;
|
|
63
|
+
}, 'items');
|
|
64
|
+
}
|
|
42
65
|
/**
|
|
43
66
|
* 专门用于 (composable) blocklet 的 Dashboard 组件, 解析 blocklet meta 中 section 为 dashboard 的 navigation 数据, 渲染一个 UX Dashboard
|
|
44
67
|
*/
|
|
68
|
+
|
|
69
|
+
|
|
45
70
|
function Dashboard(_ref) {
|
|
46
|
-
var _formattedBlocklet$na;
|
|
71
|
+
var _formattedBlocklet$na, _sessionCtx$session, _sessionCtx$session$u;
|
|
47
72
|
|
|
48
73
|
let {
|
|
49
74
|
meta
|
|
50
75
|
} = _ref,
|
|
51
76
|
rest = _objectWithoutProperties(_ref, _excluded);
|
|
52
77
|
|
|
78
|
+
const sessionCtx = (0, _react.useContext)(_Session.SessionContext);
|
|
53
79
|
const {
|
|
54
80
|
locale
|
|
55
81
|
} = (0, _context.useLocaleContext)() || {};
|
|
@@ -72,7 +98,10 @@ function Dashboard(_ref) {
|
|
|
72
98
|
appLogo,
|
|
73
99
|
appName
|
|
74
100
|
} = formattedBlocklet;
|
|
75
|
-
let localizedNav = (0, _blocklets.getLocalizedNavigation)(formattedBlocklet === null || formattedBlocklet === void 0 ? void 0 : (_formattedBlocklet$na = formattedBlocklet.navigation) === null || _formattedBlocklet$na === void 0 ? void 0 : _formattedBlocklet$na.dashboard, locale) || [];
|
|
101
|
+
let localizedNav = (0, _blocklets.getLocalizedNavigation)(formattedBlocklet === null || formattedBlocklet === void 0 ? void 0 : (_formattedBlocklet$na = formattedBlocklet.navigation) === null || _formattedBlocklet$na === void 0 ? void 0 : _formattedBlocklet$na.dashboard, locale) || []; // 根据 role 筛选 nav 数据
|
|
102
|
+
|
|
103
|
+
localizedNav = filterNavByRole(localizedNav, sessionCtx === null || sessionCtx === void 0 ? void 0 : (_sessionCtx$session = sessionCtx.session) === null || _sessionCtx$session === void 0 ? void 0 : (_sessionCtx$session$u = _sessionCtx$session.user) === null || _sessionCtx$session$u === void 0 ? void 0 : _sessionCtx$session$u.role); // 将 nav 数据处理成 ux dashboard 需要的格式
|
|
104
|
+
|
|
76
105
|
localizedNav = (0, _utils.mapRecursive)(localizedNav, item => ({
|
|
77
106
|
title: item.title,
|
|
78
107
|
url: item.link,
|
|
@@ -100,22 +129,19 @@ function Dashboard(_ref) {
|
|
|
100
129
|
}, rest), {}, {
|
|
101
130
|
headerProps: _objectSpread({
|
|
102
131
|
brand: appName,
|
|
103
|
-
description: /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}),
|
|
117
|
-
children: did
|
|
118
|
-
})
|
|
132
|
+
description: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Address.default, {
|
|
133
|
+
compact: true,
|
|
134
|
+
responsive: false,
|
|
135
|
+
copyable: false,
|
|
136
|
+
showCopyButtonInTooltip: true,
|
|
137
|
+
prepend: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Avatar.default, {
|
|
138
|
+
did: did,
|
|
139
|
+
size: 16,
|
|
140
|
+
style: {
|
|
141
|
+
marginRight: 4
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
children: did
|
|
119
145
|
}),
|
|
120
146
|
logo: /*#__PURE__*/(0, _jsxRuntime.jsx)("a", {
|
|
121
147
|
href: _blocklets.publicPath,
|
package/lib/Header/index.js
CHANGED
package/lib/blocklets.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.publicPath = exports.parseNavigation = exports.getLocalizedNavigation = exports.formatTheme = exports.formatBlockletInfo = void 0;
|
|
6
|
+
exports.publicPath = exports.parseNavigation = exports.getLocalizedNavigation = exports.formatTheme = exports.formatNavigation = exports.formatBlockletInfo = void 0;
|
|
7
7
|
|
|
8
8
|
var _utils = require("./utils");
|
|
9
9
|
|
|
@@ -99,14 +99,35 @@ const getLocalizedNavigation = function getLocalizedNavigation(navigation) {
|
|
|
99
99
|
});
|
|
100
100
|
}, 'items');
|
|
101
101
|
};
|
|
102
|
+
/**
|
|
103
|
+
* 格式化 navigation
|
|
104
|
+
*
|
|
105
|
+
* - role 统一为数组形式
|
|
106
|
+
*/
|
|
107
|
+
|
|
102
108
|
|
|
103
109
|
exports.getLocalizedNavigation = getLocalizedNavigation;
|
|
104
110
|
|
|
111
|
+
const formatNavigation = navigation => {
|
|
112
|
+
return (0, _utils.mapRecursive)(navigation, item => {
|
|
113
|
+
if (item.role) {
|
|
114
|
+
return _objectSpread(_objectSpread({}, item), {}, {
|
|
115
|
+
role: Array.isArray(item.role) ? item.role : [item.role]
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return item;
|
|
120
|
+
}, 'items');
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
exports.formatNavigation = formatNavigation;
|
|
124
|
+
|
|
105
125
|
const parseNavigation = navigation => {
|
|
106
126
|
if (!(navigation !== null && navigation !== void 0 && navigation.length)) {
|
|
107
127
|
return null;
|
|
108
128
|
}
|
|
109
129
|
|
|
130
|
+
const formattedNav = formatNavigation(navigation);
|
|
110
131
|
const sections = {
|
|
111
132
|
header: [],
|
|
112
133
|
footer: [],
|
|
@@ -118,7 +139,7 @@ const parseNavigation = navigation => {
|
|
|
118
139
|
dashboard: []
|
|
119
140
|
}; // 对 navigation 顶层元素按 section 分组
|
|
120
141
|
|
|
121
|
-
|
|
142
|
+
formattedNav.forEach(item => {
|
|
122
143
|
// item#section 为空时, 表示只存在于 header
|
|
123
144
|
if (!item.section) {
|
|
124
145
|
sections.header.push(item); // item 出现在指定几个 section 中 (array)
|
package/lib/utils.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.matchPaths = exports.matchPath = exports.mapRecursive = exports.isUrl = exports.flatRecursive = exports.countRecursive = void 0;
|
|
6
|
+
exports.matchPaths = exports.matchPath = exports.mapRecursive = exports.isUrl = exports.flatRecursive = exports.filterRecursive = exports.countRecursive = void 0;
|
|
7
7
|
|
|
8
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; }
|
|
9
9
|
|
|
@@ -42,11 +42,33 @@ const countRecursive = function countRecursive(array) {
|
|
|
42
42
|
let counter = 0;
|
|
43
43
|
mapRecursive(array, () => counter++, childrenKey);
|
|
44
44
|
return counter;
|
|
45
|
-
}; //
|
|
45
|
+
}; // 对有层级结构的 array 进行 filter 处理
|
|
46
|
+
// 因为是 DFS 遍历, 可以借助 context.filteredChildren 在过滤/保留子结的同时保持父子结构 (即使父结点不满足筛选条件)
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
exports.countRecursive = countRecursive;
|
|
49
50
|
|
|
51
|
+
const filterRecursive = function filterRecursive(array, predicate) {
|
|
52
|
+
let childrenKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'children';
|
|
53
|
+
return array.map(item => _objectSpread({}, item)).filter(item => {
|
|
54
|
+
const children = item[childrenKey];
|
|
55
|
+
|
|
56
|
+
if (Array.isArray(children)) {
|
|
57
|
+
const filtered = filterRecursive(children, predicate, childrenKey);
|
|
58
|
+
item[childrenKey] = filtered !== null && filtered !== void 0 && filtered.length ? filtered : undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const context = {
|
|
62
|
+
filteredChildren: item[childrenKey],
|
|
63
|
+
isLeaf: !(children !== null && children !== void 0 && children.length)
|
|
64
|
+
};
|
|
65
|
+
return predicate(item, context);
|
|
66
|
+
});
|
|
67
|
+
}; // "http://", "https://" 2 种情况
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
exports.filterRecursive = filterRecursive;
|
|
71
|
+
|
|
50
72
|
const isUrl = str => {
|
|
51
73
|
return /^https?:\/\//.test(str);
|
|
52
74
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/ui-react",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.24",
|
|
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.
|
|
34
|
-
"@arcblock/ux": "^2.4.
|
|
33
|
+
"@arcblock/did-connect": "^2.4.24",
|
|
34
|
+
"@arcblock/ux": "^2.4.24",
|
|
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": "^28.1.3"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "d82c2bdd64a3a3155312fd91e6e7b40293125ad3"
|
|
57
57
|
}
|
package/src/Dashboard/index.js
CHANGED
|
@@ -1,17 +1,42 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
1
|
+
import { useMemo, useContext } from 'react';
|
|
2
|
+
import { SessionContext } from '@arcblock/did-connect/lib/Session';
|
|
2
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
4
|
import UxDashboard from '@arcblock/ux/lib/Layout/dashboard';
|
|
4
|
-
import ClickToCopy from '@arcblock/ux/lib/ClickToCopy';
|
|
5
5
|
import DidAddress from '@arcblock/did-connect/lib/Address';
|
|
6
6
|
import DidAvatar from '@arcblock/did-connect/lib/Avatar';
|
|
7
7
|
import { blockletMetaProps } from '../types';
|
|
8
|
-
import { mapRecursive, flatRecursive, matchPaths } from '../utils';
|
|
8
|
+
import { mapRecursive, flatRecursive, matchPaths, filterRecursive } from '../utils';
|
|
9
9
|
import { publicPath, formatBlockletInfo, getLocalizedNavigation } from '../blocklets';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* 根据 role 筛选 nav, 规则:
|
|
13
|
+
* - 如果是枝结点, 必须至少存在一个符合条件的子结点
|
|
14
|
+
* - role 未定义, 符合条件
|
|
15
|
+
* - role 定义且包括当前的 userRole, 符合条件
|
|
16
|
+
*
|
|
17
|
+
* @param {object[]} nav 导航菜单数据
|
|
18
|
+
* @param {string} userRole 当前用户 role
|
|
19
|
+
* @returns 符合 role 权限的导航菜单数据
|
|
20
|
+
*/
|
|
21
|
+
function filterNavByRole(nav, userRole) {
|
|
22
|
+
return filterRecursive(
|
|
23
|
+
nav,
|
|
24
|
+
(item, context) => {
|
|
25
|
+
const isRoleMatched = !item.role || (userRole && item.role.includes(userRole));
|
|
26
|
+
if (!context.isLeaf) {
|
|
27
|
+
return isRoleMatched && context.filteredChildren?.length;
|
|
28
|
+
}
|
|
29
|
+
return isRoleMatched;
|
|
30
|
+
},
|
|
31
|
+
'items'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
11
35
|
/**
|
|
12
36
|
* 专门用于 (composable) blocklet 的 Dashboard 组件, 解析 blocklet meta 中 section 为 dashboard 的 navigation 数据, 渲染一个 UX Dashboard
|
|
13
37
|
*/
|
|
14
38
|
function Dashboard({ meta, ...rest }) {
|
|
39
|
+
const sessionCtx = useContext(SessionContext);
|
|
15
40
|
const { locale } = useLocaleContext() || {};
|
|
16
41
|
const blocklet = Object.assign({}, window.blocklet, meta);
|
|
17
42
|
const formattedBlocklet = useMemo(() => {
|
|
@@ -29,6 +54,9 @@ function Dashboard({ meta, ...rest }) {
|
|
|
29
54
|
|
|
30
55
|
const { did, appLogo, appName } = formattedBlocklet;
|
|
31
56
|
let localizedNav = getLocalizedNavigation(formattedBlocklet?.navigation?.dashboard, locale) || [];
|
|
57
|
+
// 根据 role 筛选 nav 数据
|
|
58
|
+
localizedNav = filterNavByRole(localizedNav, sessionCtx?.session?.user?.role);
|
|
59
|
+
// 将 nav 数据处理成 ux dashboard 需要的格式
|
|
32
60
|
localizedNav = mapRecursive(
|
|
33
61
|
localizedNav,
|
|
34
62
|
(item) => ({
|
|
@@ -58,15 +86,14 @@ function Dashboard({ meta, ...rest }) {
|
|
|
58
86
|
headerProps={{
|
|
59
87
|
brand: appName,
|
|
60
88
|
description: (
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
</ClickToCopy>
|
|
89
|
+
<DidAddress
|
|
90
|
+
compact
|
|
91
|
+
responsive={false}
|
|
92
|
+
copyable={false}
|
|
93
|
+
showCopyButtonInTooltip
|
|
94
|
+
prepend={<DidAvatar did={did} size={16} style={{ marginRight: 4 }} />}>
|
|
95
|
+
{did}
|
|
96
|
+
</DidAddress>
|
|
70
97
|
),
|
|
71
98
|
logo: (
|
|
72
99
|
<a href={publicPath}>
|
package/src/Header/index.js
CHANGED
|
@@ -132,6 +132,7 @@ function Header({ meta, addons, sessionManagerProps, homeLink, theme: themeOverr
|
|
|
132
132
|
compact
|
|
133
133
|
responsive={false}
|
|
134
134
|
copyable={false}
|
|
135
|
+
showCopyButtonInTooltip
|
|
135
136
|
prepend={<DidAvatar did={did} size={16} style={{ marginRight: 4 }} />}>
|
|
136
137
|
{did}
|
|
137
138
|
</DidAddress>
|
package/src/blocklets.js
CHANGED
|
@@ -68,11 +68,34 @@ export const getLocalizedNavigation = (navigation, locale = 'en') => {
|
|
|
68
68
|
);
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* 格式化 navigation
|
|
73
|
+
*
|
|
74
|
+
* - role 统一为数组形式
|
|
75
|
+
*/
|
|
76
|
+
export const formatNavigation = (navigation) => {
|
|
77
|
+
return mapRecursive(
|
|
78
|
+
navigation,
|
|
79
|
+
(item) => {
|
|
80
|
+
if (item.role) {
|
|
81
|
+
return {
|
|
82
|
+
...item,
|
|
83
|
+
role: Array.isArray(item.role) ? item.role : [item.role],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return item;
|
|
87
|
+
},
|
|
88
|
+
'items'
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
71
92
|
export const parseNavigation = (navigation) => {
|
|
72
93
|
if (!navigation?.length) {
|
|
73
94
|
return null;
|
|
74
95
|
}
|
|
75
96
|
|
|
97
|
+
const formattedNav = formatNavigation(navigation);
|
|
98
|
+
|
|
76
99
|
const sections = {
|
|
77
100
|
header: [],
|
|
78
101
|
footer: [],
|
|
@@ -85,7 +108,7 @@ export const parseNavigation = (navigation) => {
|
|
|
85
108
|
};
|
|
86
109
|
|
|
87
110
|
// 对 navigation 顶层元素按 section 分组
|
|
88
|
-
|
|
111
|
+
formattedNav.forEach((item) => {
|
|
89
112
|
// item#section 为空时, 表示只存在于 header
|
|
90
113
|
if (!item.section) {
|
|
91
114
|
sections.header.push(item);
|
package/src/utils.js
CHANGED
|
@@ -24,6 +24,22 @@ export const countRecursive = (array, childrenKey = 'children') => {
|
|
|
24
24
|
return counter;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
// 对有层级结构的 array 进行 filter 处理
|
|
28
|
+
// 因为是 DFS 遍历, 可以借助 context.filteredChildren 在过滤/保留子结的同时保持父子结构 (即使父结点不满足筛选条件)
|
|
29
|
+
export const filterRecursive = (array, predicate, childrenKey = 'children') => {
|
|
30
|
+
return array
|
|
31
|
+
.map((item) => ({ ...item }))
|
|
32
|
+
.filter((item) => {
|
|
33
|
+
const children = item[childrenKey];
|
|
34
|
+
if (Array.isArray(children)) {
|
|
35
|
+
const filtered = filterRecursive(children, predicate, childrenKey);
|
|
36
|
+
item[childrenKey] = filtered?.length ? filtered : undefined;
|
|
37
|
+
}
|
|
38
|
+
const context = { filteredChildren: item[childrenKey], isLeaf: !children?.length };
|
|
39
|
+
return predicate(item, context);
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
27
43
|
// "http://", "https://" 2 种情况
|
|
28
44
|
export const isUrl = (str) => {
|
|
29
45
|
return /^https?:\/\//.test(str);
|