@blocklet/ui-react 3.2.18 → 3.2.19

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.
@@ -1,29 +1,37 @@
1
- import { jsxs as s, jsx as e, Fragment as f } from "react/jsx-runtime";
2
- import { isValidElement as g } from "react";
3
- import { Box as t, Stack as y, Typography as p, useMediaQuery as v } from "@mui/material";
4
- import M from "@arcblock/ux/lib/Tabs";
5
- import B from "./app-badge.js";
6
- import { useAppInfo as I } from "./app-info-context.js";
7
- function h() {
8
- return v((r) => r.breakpoints.down("md"));
1
+ import { jsxs as l, jsx as e, Fragment as y } from "react/jsx-runtime";
2
+ import { useEffect as I, isValidElement as M } from "react";
3
+ import { Box as r, Stack as A, Typography as f, useMediaQuery as B } from "@mui/material";
4
+ import j from "@arcblock/ux/lib/Tabs";
5
+ import w from "./app-badge.js";
6
+ import { useAppInfo as C } from "./app-info-context.js";
7
+ function b() {
8
+ return B((i) => i.breakpoints.down("md"));
9
9
  }
10
- function x({ sx: i = {} }) {
11
- const r = h();
12
- return /* @__PURE__ */ e(t, { sx: { mx: r ? -2 : -3, borderBottom: "1px solid", borderColor: "divider", mt: 1.5, mb: 1, ...i } });
10
+ function h({ sx: t = {} }) {
11
+ const i = b();
12
+ return /* @__PURE__ */ e(r, { sx: { mx: i ? -2 : -3, borderBottom: "1px solid", borderColor: "divider", mt: 1.5, mb: 1, ...t } });
13
13
  }
14
- function H({ onTabChange: i = void 0 }) {
15
- const r = h(), {
16
- icon: a = void 0,
17
- name: d = "",
18
- description: m = void 0,
19
- actions: n = void 0,
20
- badges: c = [],
21
- tabs: l = [],
22
- currentTab: u = ""
23
- } = I();
24
- return d ? /* @__PURE__ */ s(t, { className: "app-header", sx: { mt: 3, mb: 3 }, children: [
25
- /* @__PURE__ */ s(
26
- t,
14
+ function D({ onTabChange: t = void 0 }) {
15
+ const i = b(), {
16
+ inService: d,
17
+ navItem: n,
18
+ icon: m = void 0,
19
+ name: p = "",
20
+ description: c = void 0,
21
+ actions: o = void 0,
22
+ badges: x = [],
23
+ tabs: a = [],
24
+ currentTab: g = "",
25
+ updateAppInfo: u
26
+ } = C();
27
+ return I(() => {
28
+ d || u({
29
+ name: n?.title || "",
30
+ description: n?.description || ""
31
+ });
32
+ }, [n?.title, n?.description, d, u]), p ? /* @__PURE__ */ l(r, { className: "app-header", sx: { mt: 3, mb: 3 }, children: [
33
+ /* @__PURE__ */ l(
34
+ r,
27
35
  {
28
36
  sx: {
29
37
  display: "flex",
@@ -31,34 +39,34 @@ function H({ onTabChange: i = void 0 }) {
31
39
  gap: 1
32
40
  },
33
41
  children: [
34
- a && /* @__PURE__ */ e(t, { sx: { display: "flex", alignItems: "center", justifyContent: "center" }, children: a }),
35
- /* @__PURE__ */ s(y, { sx: { flexGrow: 1 }, children: [
36
- /* @__PURE__ */ e(p, { variant: "h1", sx: { mb: 0.5 }, children: d }),
37
- m && /* @__PURE__ */ e(
38
- p,
42
+ m && /* @__PURE__ */ e(r, { sx: { display: "flex", alignItems: "center", justifyContent: "center" }, children: m }),
43
+ /* @__PURE__ */ l(A, { sx: { flexGrow: 1 }, children: [
44
+ /* @__PURE__ */ e(f, { variant: "h1", sx: { mb: 0.5 }, children: p }),
45
+ c && /* @__PURE__ */ e(
46
+ f,
39
47
  {
40
48
  variant: "body2",
41
49
  color: "text.secondary",
42
50
  sx: { lineHeight: 1.6, "& a": { color: "primary.main" }, maxWidth: 980 },
43
- children: m
51
+ children: c
44
52
  }
45
53
  )
46
54
  ] }),
47
- !r && n && /* @__PURE__ */ e(t, { sx: { ml: 1 }, children: n })
55
+ !i && o && /* @__PURE__ */ e(r, { sx: { ml: 1 }, children: o })
48
56
  ]
49
57
  }
50
58
  ),
51
- c.length > 0 && /* @__PURE__ */ e(t, { sx: { display: "flex", flexWrap: "wrap", gap: 1, alignItems: "center", mt: 2 }, children: c.map(
52
- (o, b) => g(o) ? o : /* @__PURE__ */ e(B, { ...o }, o.label || b)
59
+ x.length > 0 && /* @__PURE__ */ e(r, { sx: { display: "flex", flexWrap: "wrap", gap: 1, alignItems: "center", mt: 2 }, children: x.map(
60
+ (s, v) => M(s) ? s : /* @__PURE__ */ e(w, { ...s }, s.label || v)
53
61
  ) }),
54
- r && n && /* @__PURE__ */ e(t, { sx: { mt: r ? 2 : 0 }, children: n }),
55
- l.length <= 1 && /* @__PURE__ */ e(x, {}),
56
- l.length > 1 && /* @__PURE__ */ s(f, { children: [
57
- /* @__PURE__ */ e(M, { tabs: l, current: u, onChange: i, scrollButtons: "auto", sx: { mt: 2.5 } }),
58
- /* @__PURE__ */ e(x, { sx: { mt: 0 } })
62
+ i && o && /* @__PURE__ */ e(r, { sx: { mt: i ? 2 : 0 }, children: o }),
63
+ a.length <= 1 && /* @__PURE__ */ e(h, {}),
64
+ a.length > 1 && /* @__PURE__ */ l(y, { children: [
65
+ /* @__PURE__ */ e(j, { tabs: a, current: g, onChange: t, scrollButtons: "auto", sx: { mt: 2.5 } }),
66
+ /* @__PURE__ */ e(h, { sx: { mt: 0 } })
59
67
  ] })
60
68
  ] }) : null;
61
69
  }
62
70
  export {
63
- H as default
71
+ D as default
64
72
  };
@@ -1,4 +1,5 @@
1
1
  import { default as React } from 'react';
2
+ import { Locale } from '@arcblock/ux/lib/type';
2
3
  import { AppBadgeProps } from './app-badge';
3
4
  export type NavItem = {
4
5
  id: string;
@@ -22,11 +23,14 @@ export interface AppInfo {
22
23
  currentTab?: string;
23
24
  }
24
25
  export interface AppInfoContextValue extends AppInfo {
26
+ inService: boolean;
25
27
  currentTab: string;
26
28
  TabComponent: React.ComponentType | React.ReactNode;
27
29
  navItem?: NavItem;
28
30
  updateAppInfo: (patch: Partial<AppInfo>) => void;
29
31
  }
32
+ export declare const getI18nVal: (obj: Record<string, string | Record<Locale, string>>, key: string, locale?: Locale) => any;
33
+ export declare const findNavItem: (items: NavItem[], targetLink?: string, locale?: Locale) => null;
30
34
  interface AppInfoProviderProps {
31
35
  path?: string;
32
36
  currentTab?: string;
@@ -1,69 +1,83 @@
1
- import { jsx as x } from "react/jsx-runtime";
2
- import { createContext as h, useState as L, useMemo as T, useEffect as w, useContext as M } from "react";
3
- import { useCreation as A, useMemoizedFn as O } from "ahooks";
4
- import { withoutTrailingSlash as P } from "ufo";
5
- import W from "lodash/isPlainObject";
6
- import { WELLKNOWN_BLOCKLET_ADMIN_PATH as j } from "@abtnode/constant";
7
- import { useLocaleContext as E } from "@arcblock/ux/lib/Locale/context";
8
- const C = h({
1
+ import { jsx as C } from "react/jsx-runtime";
2
+ import { createContext as k, useState as L, useMemo as M, useContext as P } from "react";
3
+ import { useCreation as b, useMemoizedFn as T } from "ahooks";
4
+ import { withoutTrailingSlash as w } from "ufo";
5
+ import N from "lodash/isPlainObject";
6
+ import { WELLKNOWN_BLOCKLET_ADMIN_PATH as O } from "@abtnode/constant";
7
+ import { useLocaleContext as S } from "@arcblock/ux/lib/Locale/context";
8
+ const A = k({
9
+ inService: !1,
9
10
  currentTab: "",
10
11
  TabComponent: null,
11
12
  navItem: void 0,
12
13
  updateAppInfo: () => {
13
14
  }
14
- }), u = (s, a, d = "en") => {
15
- const o = s?.[a];
16
- return W(o) ? o[d] || o.en : o;
15
+ }), h = (l, r, c = "en") => {
16
+ const n = l?.[r];
17
+ return N(n) ? n[c] || n.en : n;
18
+ }, j = (l, r = "", c = "en") => {
19
+ const n = r === "/" ? ["/"] : r.split("/").filter(Boolean);
20
+ let e = null, d = 0;
21
+ return l.forEach((s) => {
22
+ const m = h(s, "link", c) || "", i = m === "/" ? ["/"] : m.split("/").filter(Boolean);
23
+ if (i.length > n.length)
24
+ return;
25
+ let p = !0;
26
+ for (let u = 0; u < i.length; u++)
27
+ if (i[u] !== n[u]) {
28
+ p = !1;
29
+ break;
30
+ }
31
+ p && i.length > d && (e = s, d = i.length);
32
+ }), e;
17
33
  };
18
- function D({
19
- path: s = window?.location?.pathname || "",
20
- currentTab: a = "",
21
- meta: d = void 0,
22
- children: o = null
34
+ function z({
35
+ path: l = window?.location?.pathname || "",
36
+ currentTab: r = "",
37
+ meta: c = void 0,
38
+ children: n = null
23
39
  }) {
24
- const { locale: i = "en" } = E() || {}, I = P(s), f = I.startsWith(j), b = A(() => Object.assign({}, window.blocklet, d), [d]), r = A(() => {
25
- const n = b.navigation?.filter(
26
- (e) => Array.isArray(e.section) ? e.section.includes("dashboard") : e.section === "dashboard"
27
- ), t = n?.flatMap((e) => e.items || [])?.find((e) => (u(e, "link", i) || "").startsWith(I));
40
+ const { locale: e = "en" } = S() || {}, d = w(l), s = d.startsWith(O), m = b(() => Object.assign({}, window.blocklet, c), [c]), i = b(() => {
41
+ const o = m.navigation?.filter(
42
+ (a) => Array.isArray(a.section) ? a.section.includes("dashboard") : a.section === "dashboard"
43
+ ), f = o?.flatMap((a) => a.items || []), t = j(f, d, e);
28
44
  if (!t)
29
45
  return;
30
- const l = n.find((e) => e.id === t.parent);
31
- let p = u(t, "title", i) || "";
32
- return f || (p = `${u(l, "title", i) || ""} - ${p}`), {
46
+ const v = o.find((a) => a.id === t.parent);
47
+ let g = h(t, "title", e) || "";
48
+ return s || (g = `${h(v, "title", e) || ""} - ${g}`), {
33
49
  id: t.id,
34
50
  parent: t.parent,
35
- title: p,
36
- link: u(t, "link", i) || "",
37
- description: u(t, "description", i) || ""
51
+ title: g,
52
+ link: h(t, "link", e) || "",
53
+ description: h(t, "description", e) || ""
38
54
  };
39
- }, [s, f, i, b]), [v, g] = L({}), m = O((n) => {
40
- g((c) => ({ ...c, ...n }));
41
- }), k = T(
55
+ }, [l, s, e, m]), [p, u] = L({}), I = T((o) => {
56
+ u((f) => ({ ...f, ...o }));
57
+ }), x = M(
42
58
  () => {
43
- const { tabs: n = [] } = v, c = n.find((p) => p.value === a);
44
- let t = a, l = null;
45
- return !c && n.length > 0 ? (t = n[0].value, l = n[0].render || null) : l = c?.render || null, {
46
- ...v,
47
- navItem: r,
59
+ const { tabs: o = [] } = p, f = o.find((g) => g.value === r);
60
+ let t = r, v = null;
61
+ return !f && o.length > 0 ? (t = o[0].value, v = o[0].render || null) : v = f?.render || null, {
62
+ ...p,
63
+ inService: s,
64
+ navItem: i,
48
65
  currentTab: t,
49
- TabComponent: l,
50
- updateAppInfo: m
66
+ TabComponent: v,
67
+ updateAppInfo: I
51
68
  };
52
69
  },
53
70
  // eslint-disable-next-line react-hooks/exhaustive-deps
54
- [v, r, a, m]
71
+ [p, i, s, r, I]
55
72
  );
56
- return w(() => {
57
- f || m({
58
- name: r?.title || "",
59
- description: r?.description || ""
60
- });
61
- }, [r?.title, r?.description, f, m]), /* @__PURE__ */ x(C.Provider, { value: k, children: o });
73
+ return /* @__PURE__ */ C(A.Provider, { value: x, children: n });
62
74
  }
63
- function F() {
64
- return M(C);
75
+ function D() {
76
+ return P(A);
65
77
  }
66
78
  export {
67
- D as AppInfoProvider,
68
- F as useAppInfo
79
+ z as AppInfoProvider,
80
+ j as findNavItem,
81
+ h as getI18nVal,
82
+ D as useAppInfo
69
83
  };
@@ -7,7 +7,7 @@ export default Dashboard;
7
7
  */
8
8
  declare function Dashboard({ meta, fallbackUrl, invalidPathFallback, headerAddons, sessionManagerProps, links, showDomainWarningDialog, appPath, appTab, onAppTabChange, children, ...rest }: {
9
9
  [x: string]: any;
10
- meta?: {} | undefined;
10
+ meta?: undefined;
11
11
  fallbackUrl?: any;
12
12
  invalidPathFallback?: null | undefined;
13
13
  headerAddons?: undefined;
@@ -17,7 +17,7 @@ import { default as yo } from "./app-shell/app-badge.js";
17
17
  import { AppInfoProvider as Y } from "./app-shell/app-info-context.js";
18
18
  import { useAppInfo as Ao } from "./app-shell/app-info-context.js";
19
19
  function Z({
20
- meta: a = {},
20
+ meta: a = void 0,
21
21
  fallbackUrl: s = m,
22
22
  invalidPathFallback: l = null,
23
23
  headerAddons: L = void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "3.2.18",
3
+ "version": "3.2.19",
4
4
  "description": "Some useful front-end web components that can be used in Blocklets.",
5
5
  "keywords": [
6
6
  "react",
@@ -35,9 +35,9 @@
35
35
  "dependencies": {
36
36
  "@abtnode/constant": "^1.17.5",
37
37
  "@abtnode/util": "^1.17.5",
38
- "@arcblock/bridge": "3.2.18",
39
- "@arcblock/icons": "3.2.18",
40
- "@arcblock/react-hooks": "3.2.18",
38
+ "@arcblock/bridge": "3.2.19",
39
+ "@arcblock/icons": "3.2.19",
40
+ "@arcblock/react-hooks": "3.2.19",
41
41
  "@arcblock/ws": "^1.27.15",
42
42
  "@blocklet/did-space-react": "^1.2.10",
43
43
  "@iconify-icons/logos": "^1.2.36",
@@ -82,7 +82,7 @@
82
82
  "access": "public"
83
83
  },
84
84
  "devDependencies": {
85
- "@arcblock/did-connect-react": "3.2.18",
85
+ "@arcblock/did-connect-react": "3.2.19",
86
86
  "@babel/preset-env": "^7.28.0",
87
87
  "@babel/preset-react": "^7.27.1",
88
88
  "@babel/preset-typescript": "^7.27.1",
@@ -96,5 +96,5 @@
96
96
  "typescript": "~5.5.4",
97
97
  "unbuild": "^2.0.0"
98
98
  },
99
- "gitHead": "2637c095bf07f3fd8feb5f06ca6aff993a3be29b"
99
+ "gitHead": "8439c6a0c022c3fab219191ff035d737c422f3ff"
100
100
  }
@@ -1,4 +1,4 @@
1
- import { isValidElement } from 'react';
1
+ import { isValidElement, useEffect } from 'react';
2
2
  import { Box, Stack, Typography, useMediaQuery, SxProps, Theme } from '@mui/material';
3
3
  import Tabs from '@arcblock/ux/lib/Tabs';
4
4
  import AppBadge from './app-badge';
@@ -29,6 +29,8 @@ interface AppHeaderProps {
29
29
  function AppHeader({ onTabChange = undefined }: AppHeaderProps) {
30
30
  const isMobile = useMobile();
31
31
  const {
32
+ inService,
33
+ navItem,
32
34
  icon = undefined,
33
35
  name = '',
34
36
  description = undefined,
@@ -36,8 +38,19 @@ function AppHeader({ onTabChange = undefined }: AppHeaderProps) {
36
38
  badges = [],
37
39
  tabs = [],
38
40
  currentTab = '',
41
+ updateAppInfo,
39
42
  } = useAppInfo();
40
43
 
44
+ // 非 Service 应用,自动更新 name / description
45
+ useEffect(() => {
46
+ if (!inService) {
47
+ updateAppInfo({
48
+ name: navItem?.title || '',
49
+ description: navItem?.description || '',
50
+ });
51
+ }
52
+ }, [navItem?.title, navItem?.description, inService, updateAppInfo]);
53
+
41
54
  if (!name) {
42
55
  return null;
43
56
  }
@@ -1,4 +1,4 @@
1
- import React, { createContext, useState, useContext, useMemo, useEffect } from 'react';
1
+ import React, { createContext, useState, useContext, useMemo } from 'react';
2
2
  import { useCreation, useMemoizedFn } from 'ahooks';
3
3
  import { withoutTrailingSlash } from 'ufo';
4
4
  import isPlainObject from 'lodash/isPlainObject';
@@ -32,6 +32,7 @@ export interface AppInfo {
32
32
  }
33
33
 
34
34
  export interface AppInfoContextValue extends AppInfo {
35
+ inService: boolean;
35
36
  currentTab: string;
36
37
  TabComponent: React.ComponentType | React.ReactNode;
37
38
  navItem?: NavItem;
@@ -39,19 +40,56 @@ export interface AppInfoContextValue extends AppInfo {
39
40
  }
40
41
 
41
42
  const AppInfoContext = createContext<AppInfoContextValue>({
43
+ inService: false,
42
44
  currentTab: '',
43
45
  TabComponent: null,
44
46
  navItem: undefined,
45
47
  updateAppInfo: () => {},
46
48
  });
47
49
 
48
- const getI18nVal = (obj: Record<string, string | Record<Locale, string>>, key: string, locale: Locale = 'en') => {
50
+ export const getI18nVal = (
51
+ obj: Record<string, string | Record<Locale, string>>,
52
+ key: string,
53
+ locale: Locale = 'en'
54
+ ) => {
49
55
  const val = obj?.[key];
50
56
 
51
57
  // @ts-ignore
52
58
  return isPlainObject(val) ? val[locale] || val.en : val;
53
59
  };
54
60
 
61
+ // 基于前缀匹配算法查找 NavItem
62
+ export const findNavItem = (items: NavItem[], targetLink: string = '', locale: Locale = 'en') => {
63
+ const targetParts = targetLink === '/' ? ['/'] : targetLink.split('/').filter(Boolean);
64
+
65
+ let result: NavItem | null = null;
66
+ let maxLen = 0;
67
+
68
+ items.forEach((item) => {
69
+ const currentLink = getI18nVal(item, 'link', locale) || '';
70
+ const currentParts = currentLink === '/' ? ['/'] : currentLink.split('/').filter(Boolean);
71
+
72
+ if (currentParts.length > targetParts.length) {
73
+ return;
74
+ }
75
+
76
+ let isMatch = true;
77
+ for (let i = 0; i < currentParts.length; i++) {
78
+ if (currentParts[i] !== targetParts[i]) {
79
+ isMatch = false;
80
+ break;
81
+ }
82
+ }
83
+
84
+ if (isMatch && currentParts.length > maxLen) {
85
+ result = item;
86
+ maxLen = currentParts.length;
87
+ }
88
+ });
89
+
90
+ return result;
91
+ };
92
+
55
93
  interface AppInfoProviderProps {
56
94
  path?: string;
57
95
  currentTab?: string;
@@ -77,12 +115,8 @@ export function AppInfoProvider({
77
115
  Array.isArray(v.section) ? v.section.includes('dashboard') : v.section === 'dashboard'
78
116
  );
79
117
  const items: NavItem[] = navigations?.flatMap((v: { items?: NavItem[] }) => v.items || []);
80
-
81
- // 根据路由查找 navItem
82
- const item = items?.find((v) => {
83
- const link = getI18nVal(v, 'link', locale) || '';
84
- return link.startsWith(targetLink);
85
- });
118
+ // 前缀匹配 navItem
119
+ const item = findNavItem(items, targetLink, locale);
86
120
 
87
121
  if (!item) {
88
122
  return undefined;
@@ -128,6 +162,7 @@ export function AppInfoProvider({
128
162
 
129
163
  return {
130
164
  ...appInfo,
165
+ inService,
131
166
  navItem,
132
167
  currentTab: ctab,
133
168
  TabComponent,
@@ -135,19 +170,9 @@ export function AppInfoProvider({
135
170
  };
136
171
  },
137
172
  // eslint-disable-next-line react-hooks/exhaustive-deps
138
- [appInfo, navItem, currentTab, updateAppInfo]
173
+ [appInfo, navItem, inService, currentTab, updateAppInfo]
139
174
  );
140
175
 
141
- // 非 Service 应用,动态更新 name / description
142
- useEffect(() => {
143
- if (!inService) {
144
- updateAppInfo({
145
- name: navItem?.title || '',
146
- description: navItem?.description || '',
147
- });
148
- }
149
- }, [navItem?.title, navItem?.description, inService, updateAppInfo]);
150
-
151
176
  return <AppInfoContext.Provider value={value}>{children}</AppInfoContext.Provider>;
152
177
  }
153
178
 
@@ -19,7 +19,7 @@ import { AppHeader, AppBadge, AppInfoProvider, useAppInfo } from './app-shell';
19
19
  * 专门用于 (composable) blocklet 的 Dashboard 组件, 解析 blocklet meta 中 section 为 dashboard 的 navigation 数据, 渲染一个 UX Dashboard
20
20
  */
21
21
  function Dashboard({
22
- meta = {},
22
+ meta = undefined,
23
23
  fallbackUrl = publicPath,
24
24
  invalidPathFallback = null,
25
25
  headerAddons = undefined,