@blocklet/ui-react 3.2.17 → 3.2.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.
Files changed (39) hide show
  1. package/lib/Dashboard/app-shell/app-badge.d.ts +24 -0
  2. package/lib/Dashboard/app-shell/app-badge.js +48 -0
  3. package/lib/Dashboard/app-shell/app-header.d.ts +5 -0
  4. package/lib/Dashboard/app-shell/app-header.js +64 -0
  5. package/lib/Dashboard/app-shell/app-info-context.d.ts +38 -0
  6. package/lib/Dashboard/app-shell/app-info-context.js +69 -0
  7. package/lib/Dashboard/app-shell/badges/app-badge-default.d.ts +20 -0
  8. package/lib/Dashboard/app-shell/badges/app-badge-default.js +84 -0
  9. package/lib/Dashboard/app-shell/badges/app-badge-did.d.ts +5 -0
  10. package/lib/Dashboard/app-shell/badges/app-badge-did.js +16 -0
  11. package/lib/Dashboard/app-shell/badges/app-badge-state.d.ts +6 -0
  12. package/lib/Dashboard/app-shell/badges/app-badge-state.js +34 -0
  13. package/lib/Dashboard/app-shell/badges/app-badge-switch.d.ts +8 -0
  14. package/lib/Dashboard/app-shell/badges/app-badge-switch.js +66 -0
  15. package/lib/Dashboard/app-shell/badges/app-badge-version.d.ts +14 -0
  16. package/lib/Dashboard/app-shell/badges/app-badge-version.js +50 -0
  17. package/lib/Dashboard/app-shell/index.d.ts +4 -0
  18. package/lib/Dashboard/app-shell/index.js +9 -0
  19. package/lib/Dashboard/index.d.ts +11 -1
  20. package/lib/Dashboard/index.js +82 -62
  21. package/lib/Footer/internal-footer.js +11 -11
  22. package/lib/Footer/links.d.ts +5 -3
  23. package/lib/Footer/links.js +63 -61
  24. package/lib/utils.js +28 -28
  25. package/package.json +12 -6
  26. package/src/Dashboard/app-shell/app-badge.stories.tsx +64 -0
  27. package/src/Dashboard/app-shell/app-badge.tsx +94 -0
  28. package/src/Dashboard/app-shell/app-header.tsx +91 -0
  29. package/src/Dashboard/app-shell/app-info-context.tsx +157 -0
  30. package/src/Dashboard/app-shell/badges/app-badge-default.tsx +131 -0
  31. package/src/Dashboard/app-shell/badges/app-badge-did.tsx +28 -0
  32. package/src/Dashboard/app-shell/badges/app-badge-state.tsx +40 -0
  33. package/src/Dashboard/app-shell/badges/app-badge-switch.tsx +72 -0
  34. package/src/Dashboard/app-shell/badges/app-badge-version.tsx +60 -0
  35. package/src/Dashboard/app-shell/index.ts +5 -0
  36. package/src/Dashboard/index.jsx +16 -2
  37. package/src/Footer/internal-footer.jsx +1 -1
  38. package/src/Footer/links.jsx +11 -7
  39. package/src/utils.js +6 -3
@@ -0,0 +1,60 @@
1
+ import { LiteralUnion } from 'type-fest';
2
+ import { alpha, Theme, Typography, useTheme } from '@mui/material';
3
+ import { Icon } from '@iconify/react';
4
+ import { AppBadgeDefaultProps, BadgeContainer } from './app-badge-default';
5
+
6
+ export type ColorKey = 'primary' | 'info' | 'success' | 'error' | 'warning';
7
+ export type BadgeColor = LiteralUnion<ColorKey, string>;
8
+ export const colorMap: Record<ColorKey, (theme: Theme) => { main: string }> = {
9
+ primary: (theme) => theme.palette.primary,
10
+ info: (theme) => theme.palette.info,
11
+ success: (theme) => theme.palette.success,
12
+ error: (theme) => theme.palette.error,
13
+ warning: (theme) => theme.palette.warning,
14
+ };
15
+
16
+ export const getBgColor = (theme: Theme, color: BadgeColor): string => {
17
+ if (colorMap[color as ColorKey]) {
18
+ const c = colorMap[color as ColorKey](theme).main;
19
+ return alpha(c, 0.1);
20
+ }
21
+ return alpha(color, 0.1);
22
+ };
23
+
24
+ export const getTextColor = (theme: Theme, color: BadgeColor): string => {
25
+ if (colorMap[color as ColorKey]) {
26
+ return colorMap[color as ColorKey](theme).main;
27
+ }
28
+ return color;
29
+ };
30
+
31
+ export interface AppBadgeVersionProps extends AppBadgeDefaultProps {
32
+ color?: string | ColorKey;
33
+ }
34
+
35
+ export function AppBadgeVersion({
36
+ icon = '',
37
+ value = '',
38
+ color = 'info',
39
+ loading = false,
40
+ ...rest
41
+ }: AppBadgeVersionProps) {
42
+ const theme = useTheme();
43
+ const txtcolor = getTextColor(theme, color);
44
+ const bgcolor = getBgColor(theme, color);
45
+
46
+ return (
47
+ <BadgeContainer
48
+ loading={loading}
49
+ sx={{
50
+ borderColor: alpha(txtcolor, 0.2),
51
+ bgcolor,
52
+ }}
53
+ {...rest}>
54
+ <Icon icon={icon || 'lucide:orbit'} style={{ marginRight: 6 }} />
55
+ <Typography className="app-badge-value" style={{ color: txtcolor }}>
56
+ v {value}
57
+ </Typography>
58
+ </BadgeContainer>
59
+ );
60
+ }
@@ -0,0 +1,5 @@
1
+ import AppHeader from './app-header';
2
+ import AppBadge from './app-badge';
3
+ import { AppInfoProvider, useAppInfo } from './app-info-context';
4
+
5
+ export { AppHeader, AppBadge, AppInfoProvider, useAppInfo };
@@ -13,6 +13,7 @@ import { mapRecursive, flatRecursive, matchPaths } from '../utils';
13
13
  import { publicPath, formatBlockletInfo, getLocalizedNavigation, filterNavByRole } from '../blocklets';
14
14
  import HeaderAddons from '../common/header-addons';
15
15
  import { useWalletHiddenTopbar } from '../common/wallet-hidden-topbar';
16
+ import { AppHeader, AppBadge, AppInfoProvider, useAppInfo } from './app-shell';
16
17
 
17
18
  /**
18
19
  * 专门用于 (composable) blocklet 的 Dashboard 组件, 解析 blocklet meta 中 section 为 dashboard 的 navigation 数据, 渲染一个 UX Dashboard
@@ -32,6 +33,10 @@ function Dashboard({
32
33
  },
33
34
  links = [],
34
35
  showDomainWarningDialog = true,
36
+ appPath = undefined,
37
+ appTab = undefined,
38
+ onAppTabChange = undefined,
39
+ children = undefined,
35
40
  ...rest
36
41
  }) {
37
42
  useWalletHiddenTopbar();
@@ -150,8 +155,12 @@ function Dashboard({
150
155
  logo: <img src={appLogo} alt="logo" />,
151
156
  addons: _headerAddons,
152
157
  ...rest.headerProps,
153
- }}
154
- />
158
+ }}>
159
+ <AppInfoProvider path={appPath} currentTab={appTab} meta={meta}>
160
+ <AppHeader onTabChange={onAppTabChange} />
161
+ {children}
162
+ </AppInfoProvider>
163
+ </UxDashboard>
155
164
  );
156
165
  }
157
166
 
@@ -165,6 +174,11 @@ Dashboard.propTypes = {
165
174
  sessionManagerProps: SessionManagerProps,
166
175
  links: PropTypes.oneOfType([PropTypes.array, PropTypes.func]),
167
176
  showDomainWarningDialog: PropTypes.bool,
177
+ appPath: PropTypes.string,
178
+ appTab: PropTypes.string,
179
+ onAppTabChange: PropTypes.func,
180
+ children: PropTypes.node,
168
181
  };
169
182
 
183
+ export { AppHeader, AppBadge, AppInfoProvider, useAppInfo };
170
184
  export default Dashboard;
@@ -43,7 +43,7 @@ function InternalFooter({ ...props }) {
43
43
  return brand ? <Brand {...brand} /> : null;
44
44
  };
45
45
  const renderNavigation = () => {
46
- return navigation?.length ? <Links links={navigation} columns={3} /> : null;
46
+ return navigation?.length ? <Links links={navigation} minColumns={3} /> : null;
47
47
  };
48
48
  const renderSocialMedia = () => {
49
49
  return socialMedia?.length ? <SocialMedia items={socialMedia} /> : null;
@@ -14,13 +14,15 @@ import { splitNavColumns, isMailProtocol } from '../utils';
14
14
  /**
15
15
  * footer 中的 links (支持分组, 最多支持 2 级)
16
16
  */
17
- export default function Links({ links = [], flowLayout = false, columns, ...rest }) {
17
+ export default function Links({ links = [], flowLayout = false, minColumns = 1, maxColumns = 4, ...rest }) {
18
18
  const [activeIndex, setActiveIndex] = useState(-1);
19
19
  const isMobile = useMobile({ key: 'md' });
20
20
  // 只要发现一项元素有子元素, 就认为是分组 (大字号突出 group title)
21
21
  const isGroupMode = links.some((item) => item.items?.length);
22
- // 是否启用 columns 布局
23
- const columnsLayout = !isMobile && isGroupMode && isInteger(columns) && columns > 1;
22
+ // 是否启用分列布局
23
+ const columnsLayout = !isMobile && isGroupMode && isInteger(minColumns) && minColumns > 1 && maxColumns >= minColumns;
24
+ // Clamp columns
25
+ const columns = columnsLayout ? Math.min(Math.max(links.length, minColumns), maxColumns) : 1;
24
26
  const renderItem = ({ label, link, icon, render, props }) => {
25
27
  let result = label;
26
28
 
@@ -139,6 +141,7 @@ export default function Links({ links = [], flowLayout = false, columns, ...rest
139
141
 
140
142
  return (
141
143
  <Root
144
+ columns={columns}
142
145
  {...rest}
143
146
  className={clsx(rest.className, {
144
147
  'footer-links--grouped': isGroupMode,
@@ -161,11 +164,12 @@ Links.propTypes = {
161
164
  ),
162
165
  // 流动布局, 简单的从左到右排列
163
166
  flowLayout: PropTypes.bool,
164
- // 列布局
165
- columns: PropTypes.number.isRequired,
167
+ // 列布局,最小列数,最大列数
168
+ minColumns: PropTypes.number,
169
+ maxColumns: PropTypes.number,
166
170
  };
167
171
 
168
- const Root = styled('div')`
172
+ const Root = styled('div', { shouldForwardProp: (prop) => prop !== 'columns' })`
169
173
  overflow: hidden;
170
174
  color: ${({ theme }) => theme.palette.text.secondary};
171
175
  .footer-links-inner {
@@ -230,7 +234,7 @@ const Root = styled('div')`
230
234
  /* columns 布局 */
231
235
  &.footer-links--columns {
232
236
  .footer-links-inner {
233
- gap: 96px;
237
+ gap: ${({ columns }) => `${288 / columns}px`};
234
238
  }
235
239
  .footer-links-column {
236
240
  display: flex;
package/src/utils.js CHANGED
@@ -89,6 +89,8 @@ export const matchPaths = (paths = []) => {
89
89
  /** 导航列表分列 */
90
90
  export const splitNavColumns = (items, options = {}) => {
91
91
  const { columns = 1, breakInside = false, groupHeight = 48, itemHeight = 24, childrenKey = 'items' } = options;
92
+ // 分组数
93
+ const groups = items.length;
92
94
 
93
95
  // 高度预估
94
96
  const totalHeight = items.reduce((height, group) => {
@@ -114,16 +116,17 @@ export const splitNavColumns = (items, options = {}) => {
114
116
  };
115
117
 
116
118
  items.forEach((group) => {
119
+ // 当前分组的预估高度
117
120
  const groupTotalHeight = groupHeight + (group[childrenKey]?.length || 0) * itemHeight;
118
121
 
119
- // 允许截断分组时,可以在任何子项处换列
122
+ // 允许截断分组,可以在任何子项处换列,尽可能利用当前列的剩余空间
120
123
  if (breakInside && shouldBreakColumn(groupHeight)) {
121
124
  currentColumn++;
122
125
  currentHeight = 0;
123
126
  result[currentColumn] = [];
124
127
  }
125
- // 不允许截断分组时,只能在分组边界换列
126
- if (!breakInside && currentHeight > 0 && shouldBreakColumn(groupTotalHeight)) {
128
+ // 不允许截断分组,只能在分组边界换列,优先分列,列不够用再考虑充分利用剩余空间
129
+ if (!breakInside && currentHeight > 0 && (groups <= columns || shouldBreakColumn(groupTotalHeight))) {
127
130
  currentColumn++;
128
131
  currentHeight = 0;
129
132
  result[currentColumn] = [];