@blocklet/ui-react 3.0.36 → 3.0.38

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,52 +1,52 @@
1
1
  import { jsx as i } from "react/jsx-runtime";
2
2
  import "iconify-icon";
3
- import { use as I, useMemo as v, useLayoutEffect as w } from "react";
3
+ import { use as I, useMemo as b, useLayoutEffect as v } from "react";
4
4
  import t from "prop-types";
5
5
  import { SessionContext as R } from "@arcblock/did-connect/lib/Session";
6
6
  import { useLocaleContext as T } from "@arcblock/ux/lib/Locale/context";
7
- import z from "@arcblock/ux/lib/Img";
8
- import A from "@arcblock/ux/lib/Layout/dashboard";
9
- import C from "@arcblock/ux/lib/hooks/use-blocklet-logo";
10
- import { SessionManagerProps as D, BlockletMetaProps as M } from "../types.js";
11
- import { mapRecursive as O, flatRecursive as j, matchPaths as H } from "../utils.js";
12
- import { formatBlockletInfo as S, getLocalizedNavigation as _, filterNavByRole as E, publicPath as m } from "../blocklets.js";
13
- import U from "../common/header-addons.js";
14
- import { useWalletHiddenTopbar as q } from "../common/wallet-hidden-topbar.js";
15
- function F({
7
+ import C from "@arcblock/ux/lib/Img";
8
+ import D from "@arcblock/ux/lib/Layout/dashboard";
9
+ import M from "@arcblock/ux/lib/hooks/use-blocklet-logo";
10
+ import { SessionManagerProps as A, BlockletMetaProps as O } from "../types.js";
11
+ import { mapRecursive as j, flatRecursive as H, matchPaths as S } from "../utils.js";
12
+ import { formatBlockletInfo as E, getLocalizedNavigation as U, filterNavByRole as _, publicPath as h } from "../blocklets.js";
13
+ import q from "../common/header-addons.js";
14
+ import { useWalletHiddenTopbar as F } from "../common/wallet-hidden-topbar.js";
15
+ function G({
16
16
  meta: s = {},
17
- fallbackUrl: a = m,
17
+ fallbackUrl: a = h,
18
18
  invalidPathFallback: l = null,
19
19
  headerAddons: k = void 0,
20
20
  sessionManagerProps: L = {
21
21
  showRole: !0,
22
22
  // dashboard 默认退出登录行为: 跳转到 (root) blocklet 首页
23
23
  onLogout: () => {
24
- window.location.href = m;
24
+ window.location.href = h;
25
25
  }
26
26
  },
27
27
  links: c = [],
28
- showDomainWarningDialog: N = !0,
29
- ...h
28
+ showDomainWarningDialog: P = !0,
29
+ ...d
30
30
  }) {
31
- q();
32
- const d = I(R)?.session?.user, g = d?.role, { locale: x } = T() || {}, r = v(() => {
31
+ F();
32
+ const f = I(R)?.session?.user, g = f?.role, { locale: x } = T() || {}, r = b(() => {
33
33
  const e = Object.assign({}, window.blocklet, s);
34
34
  try {
35
- return S(e);
35
+ return E(e);
36
36
  } catch (n) {
37
37
  return console.error("Failed to format blocklet info", n, e), e;
38
38
  }
39
- }, [s]), { localizedNav: y, flattened: f, matchedIndex: b } = v(() => {
40
- let e = _(r?.navigation?.dashboard, x) || [];
41
- e = E(e, g), e = O(
39
+ }, [s]), { localizedNav: w, flattened: p, matchedIndex: y } = b(() => {
40
+ let e = U(r?.navigation?.dashboard, x) || [];
41
+ e = _(e, g), e = j(
42
42
  e,
43
43
  (o) => {
44
- let u = null;
45
- return o.icon && (o.icon.startsWith("http") || o.icon.startsWith("data:") ? u = /* @__PURE__ */ i(z, { src: o.icon }) : u = /* @__PURE__ */ i("iconify-icon", { height: "100%", width: "100%", icon: o.icon })), {
44
+ let m = null;
45
+ return o.icon && (o.icon.startsWith("http") || o.icon.startsWith("data:") ? m = /* @__PURE__ */ i(C, { src: o.icon }) : m = /* @__PURE__ */ i("iconify-icon", { height: "100%", width: "100%", icon: o.icon })), {
46
46
  id: o.id,
47
47
  title: o.title,
48
48
  url: o.link,
49
- icon: u,
49
+ icon: m,
50
50
  // https://github.com/ArcBlock/ux/issues/755#issuecomment-1208692620
51
51
  external: !0,
52
52
  children: o.items
@@ -54,55 +54,56 @@ function F({
54
54
  },
55
55
  "items"
56
56
  );
57
- const n = j(e).filter((o) => !!o.url), p = H(n.map((o) => o.url));
58
- return p !== -1 && (n[p].active = !0), { localizedNav: e, flattened: n, matchedIndex: p };
59
- }, [r, x, g]), P = typeof c == "function" ? c(y) : [...y, ...c], W = C({
57
+ const n = H(e).filter((o) => !!o.url), u = S(n.map((o) => o.url));
58
+ return u !== -1 && (n[u].active = !0), { localizedNav: e, flattened: n, matchedIndex: u };
59
+ }, [r, x, g]), W = typeof c == "function" ? c(w) : [...w, ...c], N = M({
60
60
  meta: s
61
61
  });
62
- if (w(() => {
63
- d && !f?.length && a && (window.location.href = a);
64
- }, [a]), w(() => {
65
- d && f?.length && b === -1 && l && l();
66
- }, [l, f, b]), !r.appName)
62
+ if (v(() => {
63
+ f && !p?.length && a && (window.location.href = a);
64
+ }, [a]), v(() => {
65
+ f && p?.length && y === -1 && l && l();
66
+ }, [l, p, y]), !r.appName)
67
67
  return null;
68
- const { appName: B } = r;
68
+ const { appName: z } = r, B = /* @__PURE__ */ i(
69
+ q,
70
+ {
71
+ formattedBlocklet: r,
72
+ addons: k,
73
+ sessionManagerProps: L,
74
+ showDomainWarningDialog: P,
75
+ showWizard: d.headerProps?.showWizard
76
+ }
77
+ );
69
78
  return /* @__PURE__ */ i(
70
- A,
79
+ D,
71
80
  {
72
- title: B,
81
+ title: z,
73
82
  fullWidth: !0,
74
83
  sidebarWidth: 128,
75
84
  legacy: !1,
76
- links: P,
77
- ...h,
85
+ links: W,
86
+ ...d,
78
87
  headerProps: {
79
- homeLink: m,
80
- logo: /* @__PURE__ */ i("img", { src: W, alt: "logo" }),
81
- addons: /* @__PURE__ */ i(
82
- U,
83
- {
84
- formattedBlocklet: r,
85
- addons: k,
86
- sessionManagerProps: L,
87
- showDomainWarningDialog: N
88
- }
89
- ),
90
- ...h.headerProps
88
+ homeLink: h,
89
+ logo: /* @__PURE__ */ i("img", { src: N, alt: "logo" }),
90
+ addons: B,
91
+ ...d.headerProps
91
92
  }
92
93
  }
93
94
  );
94
95
  }
95
- F.propTypes = {
96
- meta: M,
96
+ G.propTypes = {
97
+ meta: O,
97
98
  // 如果当前用户没有权限访问任何导航菜单, 则自动跳转到 fallbackUrl, 默认值为 publicPath, 设置为 null 表示禁用自动跳转
98
99
  fallbackUrl: t.string,
99
100
  // 当前路径未匹配任何 nav links 时的 fallback, 默认行为跳转到首个可用的 nav link
100
101
  invalidPathFallback: t.func,
101
102
  headerAddons: t.oneOfType([t.func, t.node]),
102
- sessionManagerProps: D,
103
+ sessionManagerProps: A,
103
104
  links: t.oneOfType([t.array, t.func]),
104
105
  showDomainWarningDialog: t.bool
105
106
  };
106
107
  export {
107
- F as default
108
+ G as default
108
109
  };
@@ -18,6 +18,7 @@ type HeaderProps = {
18
18
  align?: 'left' | 'right';
19
19
  maxWidth?: false | Breakpoint;
20
20
  showDomainWarningDialog?: boolean;
21
+ showWizard?: boolean;
21
22
  };
22
23
  declare const _default: import('react').ComponentType<HeaderProps & Omit<BoxProps, keyof HeaderProps>>;
23
24
  export default _default;
@@ -1,41 +1,41 @@
1
1
  import { jsx as n } from "react/jsx-runtime";
2
- import { useMemo as k } from "react";
3
- import { useMemoizedFn as $ } from "ahooks";
4
- import { withErrorBoundary as N } from "react-error-boundary";
5
- import { ErrorFallback as H } from "@arcblock/ux/lib/ErrorBoundary";
6
- import { styled as y, useTheme as C, deepmerge as T, ThemeProvider as _ } from "@arcblock/ux/lib/Theme";
7
- import { ResponsiveHeader as E } from "@arcblock/ux/lib/Header";
8
- import B, { Products as F } from "@arcblock/ux/lib/NavMenu";
9
- import { useLocaleContext as L } from "@arcblock/ux/lib/Locale/context";
10
- import { translate as P } from "@arcblock/ux/lib/Locale/util";
11
- import R from "@arcblock/ux/lib/hooks/use-blocklet-logo";
12
- import z from "lodash/omit";
13
- import A from "lodash/isFinite";
14
- import M from "clsx";
15
- import W from "../Icon/index.js";
16
- import { mapRecursive as j, flatRecursive as S, matchPaths as U } from "../utils.js";
17
- import { formatBlockletInfo as K, getLocalizedNavigation as O, publicPath as q } from "../blocklets.js";
18
- import D from "../common/header-addons.js";
19
- import { useWalletHiddenTopbar as G } from "../common/wallet-hidden-topbar.js";
20
- import J from "../libs/with-hide-when-embed.js";
21
- const Q = {
2
+ import { useMemo as v } from "react";
3
+ import { useMemoizedFn as N } from "ahooks";
4
+ import { withErrorBoundary as H } from "react-error-boundary";
5
+ import { ErrorFallback as y } from "@arcblock/ux/lib/ErrorBoundary";
6
+ import { styled as C, useTheme as T, deepmerge as _, ThemeProvider as E } from "@arcblock/ux/lib/Theme";
7
+ import { ResponsiveHeader as B } from "@arcblock/ux/lib/Header";
8
+ import F, { Products as L } from "@arcblock/ux/lib/NavMenu";
9
+ import { useLocaleContext as P } from "@arcblock/ux/lib/Locale/context";
10
+ import { translate as R } from "@arcblock/ux/lib/Locale/util";
11
+ import z from "@arcblock/ux/lib/hooks/use-blocklet-logo";
12
+ import A from "lodash/omit";
13
+ import M from "lodash/isFinite";
14
+ import W from "clsx";
15
+ import j from "../Icon/index.js";
16
+ import { mapRecursive as S, flatRecursive as U, matchPaths as K } from "../utils.js";
17
+ import { formatBlockletInfo as O, getLocalizedNavigation as q, publicPath as D } from "../blocklets.js";
18
+ import G from "../common/header-addons.js";
19
+ import { useWalletHiddenTopbar as J } from "../common/wallet-hidden-topbar.js";
20
+ import Q from "../libs/with-hide-when-embed.js";
21
+ const V = {
22
22
  en: {
23
23
  products: "Products"
24
24
  },
25
25
  zh: {
26
26
  products: "产品"
27
27
  }
28
- }, V = (e) => {
28
+ }, X = (e) => {
29
29
  if (!e?.length)
30
30
  return { navItems: [], activeId: null };
31
31
  let r = 1;
32
- const s = j(e, (o) => {
33
- const i = o.icon ? /* @__PURE__ */ n(W, { icon: o.icon }) : null;
32
+ const i = S(e, (o) => {
33
+ const d = o.icon ? /* @__PURE__ */ n(j, { icon: o.icon }) : null;
34
34
  if (o.items)
35
35
  return {
36
36
  id: `${r++}`,
37
37
  label: o.title,
38
- icon: i,
38
+ icon: d,
39
39
  children: o.items
40
40
  };
41
41
  let l = {};
@@ -45,72 +45,74 @@ const Q = {
45
45
  }), {
46
46
  id: `${r++}`,
47
47
  label: /* @__PURE__ */ n("a", { href: o.link, ...l, children: o.title }),
48
- icon: i,
48
+ icon: d,
49
49
  description: o.description,
50
50
  link: o.link
51
51
  };
52
- }, "items"), a = S(s), d = U(a.map((o) => o.link));
53
- return { navItems: s, activeId: d >= 0 ? a[d].id : null };
52
+ }, "items"), a = U(i), s = K(a.map((o) => o.link));
53
+ return { navItems: i, activeId: s >= 0 ? a[s].id : null };
54
54
  };
55
- function X({
55
+ function Y({
56
56
  meta: e = {},
57
57
  addons: r = void 0,
58
58
  sessionManagerProps: u = {
59
59
  showRole: !0
60
60
  },
61
- homeLink: s = q,
61
+ homeLink: i = D,
62
62
  theme: a = void 0,
63
- hideNavMenu: d = !1,
63
+ hideNavMenu: s = !1,
64
64
  showDomainWarningDialog: o = !0,
65
- ...i
65
+ showWizard: d = !1,
66
+ ...l
66
67
  }) {
67
- G();
68
- const l = C(), { locale: h } = L() || {}, v = $((t, f = {}) => P(Q, t, h, "en", f)), m = k(() => {
68
+ J();
69
+ const h = T(), { locale: b } = P() || {}, x = N((t, f = {}) => R(V, t, b, "en", f)), m = v(() => {
69
70
  const t = Object.assign({}, window.blocklet, e);
70
71
  try {
71
- return K(t);
72
+ return O(t);
72
73
  } catch (f) {
73
74
  return console.error("Failed to format blocklet info", f, t), t;
74
75
  }
75
- }, [e]), c = k(() => T(l, a), [l, a]), x = R({
76
+ }, [e]), c = v(() => _(h, a), [h, a]), I = z({
76
77
  meta: e,
77
78
  theme: a
78
79
  });
79
80
  if (!m.appName)
80
81
  return null;
81
- const I = O(m?.navigation?.header, h), b = V(I), { navItems: p, activeId: w } = b, g = parseInt(window.blocklet?.USE_ARCBLOCK_THEME, 10);
82
- return A(g) && p.splice(g, 0, {
83
- label: v("products"),
82
+ const w = q(m?.navigation?.header, b), g = X(w), { navItems: p, activeId: $ } = g, k = parseInt(window.blocklet?.USE_ARCBLOCK_THEME, 10);
83
+ return M(k) && p.splice(k, 0, {
84
+ label: x("products"),
84
85
  // eslint-disable-next-line react/no-unstable-nested-components
85
- children: ({ isOpen: t }) => /* @__PURE__ */ n(F, { isOpen: t })
86
- }), /* @__PURE__ */ n(_, { theme: c, children: /* @__PURE__ */ n(
87
- Y,
86
+ children: ({ isOpen: t }) => /* @__PURE__ */ n(L, { isOpen: t })
87
+ }), /* @__PURE__ */ n(E, { theme: c, children: /* @__PURE__ */ n(
88
+ Z,
88
89
  {
89
- homeLink: s,
90
- logo: /* @__PURE__ */ n("img", { src: x, alt: "logo" }),
90
+ homeLink: i,
91
+ logo: /* @__PURE__ */ n("img", { src: I, alt: "logo" }),
91
92
  addons: (
92
93
  // @ts-ignore
93
94
  /* @__PURE__ */ n(
94
- D,
95
+ G,
95
96
  {
96
97
  formattedBlocklet: m,
97
- addons: typeof r == "function" ? (t) => r(t, { navigation: b }) : r,
98
+ addons: typeof r == "function" ? (t) => r(t, { navigation: g }) : r,
98
99
  sessionManagerProps: u,
99
- showDomainWarningDialog: o
100
+ showDomainWarningDialog: o,
101
+ showWizard: d
100
102
  }
101
103
  )
102
104
  ),
103
- ...z(i, ["bordered"]),
104
- $bordered: i?.bordered,
105
+ ...A(l, ["bordered"]),
106
+ $bordered: l?.bordered,
105
107
  $bgcolor: c.palette.background.default,
106
- className: M("blocklet__header", i.className),
107
- children: d || !p?.length ? null : ({ isMobile: t }) => (
108
+ className: W("blocklet__header", l.className),
109
+ children: s || !p?.length ? null : ({ isMobile: t }) => (
108
110
  // @ts-ignore
109
111
  /* @__PURE__ */ n(
110
- B,
112
+ F,
111
113
  {
112
114
  mode: t ? "inline" : "horizontal",
113
- activeId: w,
115
+ activeId: $,
114
116
  items: p,
115
117
  className: "header-nav",
116
118
  bgColor: "transparent",
@@ -122,7 +124,7 @@ function X({
122
124
  }
123
125
  ) });
124
126
  }
125
- const Y = y(E)`
127
+ const Z = C(B)`
126
128
  ${({ $bgcolor: e }) => `background-color: ${e || "#fff"};`}
127
129
  .header-logo {
128
130
  min-width: 44px;
@@ -133,9 +135,9 @@ const Y = y(E)`
133
135
  min-width: 32px;
134
136
  }
135
137
  }
136
- `, wo = N(J(X), {
137
- FallbackComponent: H
138
+ `, $o = H(Q(Y), {
139
+ FallbackComponent: y
138
140
  });
139
141
  export {
140
- wo as default
142
+ $o as default
141
143
  };
@@ -1,12 +1,13 @@
1
1
  import { default as PropTypes } from 'prop-types';
2
2
  import { SessionManagerProps } from '../types';
3
- declare function HeaderAddons({ formattedBlocklet, addons, showDomainWarningDialog, sessionManagerProps, }: {
3
+ declare function HeaderAddons({ formattedBlocklet, addons, showDomainWarningDialog, sessionManagerProps, showWizard, }: {
4
4
  formattedBlocklet: any;
5
5
  addons?: null | undefined;
6
6
  showDomainWarningDialog?: boolean | undefined;
7
7
  sessionManagerProps?: {
8
8
  showRole: boolean;
9
9
  } | undefined;
10
+ showWizard?: boolean | undefined;
10
11
  }): import('react').FunctionComponentElement<import('react').FragmentProps>;
11
12
  declare namespace HeaderAddons {
12
13
  namespace propTypes {
@@ -14,6 +15,7 @@ declare namespace HeaderAddons {
14
15
  export let addons: PropTypes.Requireable<NonNullable<((...args: any[]) => any) | PropTypes.ReactNodeLike>>;
15
16
  export { SessionManagerProps as sessionManagerProps };
16
17
  export let showDomainWarningDialog: PropTypes.Requireable<boolean>;
18
+ export let showWizard: PropTypes.Requireable<boolean>;
17
19
  }
18
20
  }
19
21
  export default HeaderAddons;
@@ -1,71 +1,132 @@
1
- import { jsx as s, Fragment as v } from "react/jsx-runtime";
1
+ import { jsx as e, Fragment as x } from "react/jsx-runtime";
2
2
  import "iconify-icon";
3
- import { use as b, createElement as k } from "react";
4
- import r from "prop-types";
5
- import { SessionContext as w } from "@arcblock/did-connect/lib/Session";
6
- import L from "@arcblock/ux/lib/SessionUser";
7
- import N from "@arcblock/ux/lib/SessionBlocklet";
8
- import T from "@arcblock/ux/lib/Locale/selector";
9
- import { useLocaleContext as C } from "@arcblock/ux/lib/Locale/context";
10
- import R from "@arcblock/ux/lib/Config/theme-mode-toggle";
11
- import { SessionManagerProps as S } from "../types.js";
12
- import { getLocalizedNavigation as M, filterNavByRole as B } from "../blocklets.js";
13
- import D from "./notification-addon.js";
14
- import P from "./domain-warning.js";
15
- const j = () => !!(window?.blocklet?.navigation ?? []).find((o) => o.id === "/userCenter/notification");
16
- function x({
17
- formattedBlocklet: l,
18
- addons: o = null,
19
- showDomainWarningDialog: m = !0,
20
- sessionManagerProps: u = { showRole: !0 }
3
+ import { useState as h, useEffect as L, use as N, createElement as W } from "react";
4
+ import s from "prop-types";
5
+ import { Tooltip as M, IconButton as R } from "@mui/material";
6
+ import B from "@iconify-icons/tabler/wand";
7
+ import { SessionContext as D } from "@arcblock/did-connect/lib/Session";
8
+ import E from "@arcblock/ux/lib/SessionUser";
9
+ import O from "@arcblock/ux/lib/SessionBlocklet";
10
+ import P from "@arcblock/ux/lib/Locale/selector";
11
+ import { useLocaleContext as g } from "@arcblock/ux/lib/Locale/context";
12
+ import j from "@arcblock/ux/lib/Config/theme-mode-toggle";
13
+ import { SessionManagerProps as F } from "../types.js";
14
+ import { getLocalizedNavigation as I, filterNavByRole as q } from "../blocklets.js";
15
+ import H from "./notification-addon.js";
16
+ import U from "./domain-warning.js";
17
+ import V from "./wizard-modal.js";
18
+ const $ = () => !!(window?.blocklet?.navigation ?? []).find((i) => i.id === "/userCenter/notification");
19
+ function G({
20
+ formattedBlocklet: m,
21
+ addons: i = null,
22
+ showDomainWarningDialog: y = !0,
23
+ sessionManagerProps: w = { showRole: !0 },
24
+ showWizard: T = !1
21
25
  }) {
22
- const n = b(w), { locale: i, languages: p } = C() || {}, { enableConnect: g = !0, enableLocale: d = !0 } = l, h = !!n?.session?.user;
23
- let a = M(l?.navigation?.sessionManager, i) || [];
24
- a = B(a, n?.session?.user?.role);
25
- const c = (() => {
26
- if (o && typeof o != "function")
27
- return Array.isArray(o) ? o : [o];
28
- let e = [];
29
- if (j() && e.push(/* @__PURE__ */ s(D, { session: n.session }, "notification-addon")), d && i && p.length > 1 && e.push(/* @__PURE__ */ s(T, { showText: !1 }, "locale-selector")), e.push(/* @__PURE__ */ s(R, {}, "theme-mode-toggle")), g && n) {
30
- const f = [];
31
- h && (a ? a.slice(0, 5) : []).forEach((t) => {
32
- f.push({
33
- label: t.title,
34
- icon: t.icon ? /* @__PURE__ */ s("iconify-icon", { icon: t.icon, height: 24, style: { marginRight: 8 } }) : null,
26
+ const [d, p] = h(!1), [t, c] = h(!1), { t: b } = g();
27
+ L(() => {
28
+ let o;
29
+ return t && (o = setTimeout(() => {
30
+ c(!1);
31
+ }, 3e3)), () => {
32
+ o && clearTimeout(o);
33
+ };
34
+ }, [t]);
35
+ const n = N(D), { locale: r, languages: z } = g() || {}, { enableConnect: A = !0, enableLocale: k = !0 } = m, v = !!n?.session?.user;
36
+ let f = I(m?.navigation?.sessionManager, r) || [];
37
+ f = q(f, n?.session?.user?.role);
38
+ const u = (() => {
39
+ if (i && typeof i != "function")
40
+ return Array.isArray(i) ? i : [i];
41
+ let o = [];
42
+ if ($() && o.push(/* @__PURE__ */ e(H, { session: n.session }, "notification-addon")), k && r && z.length > 1 && o.push(/* @__PURE__ */ e(P, { showText: !1 }, "locale-selector")), o.push(/* @__PURE__ */ e(j, {}, "theme-mode-toggle")), T && o.push(
43
+ /* @__PURE__ */ e(
44
+ M,
45
+ {
46
+ title: b("setup.wizardTooltip"),
47
+ open: t,
48
+ placement: "bottom",
49
+ arrow: !0,
50
+ children: /* @__PURE__ */ e(
51
+ R,
52
+ {
53
+ size: "small",
54
+ onClick: () => {
55
+ p(!0), c(!1);
56
+ },
57
+ sx: {
58
+ display: { xs: "none", md: "flex" },
59
+ color: "text.secondary",
60
+ ...t && {
61
+ backgroundColor: "action.hover",
62
+ "@keyframes pulse": {
63
+ "0%": { opacity: 0.8 },
64
+ "50%": { opacity: 0.5 },
65
+ "100%": { opacity: 0.8 }
66
+ },
67
+ animation: "pulse 2s infinite"
68
+ }
69
+ },
70
+ children: /* @__PURE__ */ e("iconify-icon", { icon: B, height: 20 })
71
+ }
72
+ )
73
+ },
74
+ `wizard-toggle-${t}`
75
+ ),
76
+ /* @__PURE__ */ e(
77
+ V,
78
+ {
79
+ show: d,
80
+ onChangeVisible: (a) => {
81
+ p(a), !a && d && c(!0);
82
+ },
83
+ onFinished: () => {
84
+ p(!1), c(!1);
85
+ }
86
+ },
87
+ "wizard-modal"
88
+ )
89
+ ), A && n) {
90
+ const a = [];
91
+ v && (f ? f.slice(0, 5) : []).forEach((l) => {
92
+ a.push({
93
+ label: l.title,
94
+ icon: l.icon ? /* @__PURE__ */ e("iconify-icon", { icon: l.icon, height: 24, style: { marginRight: 8 } }) : null,
35
95
  component: "a",
36
- href: t.link,
37
- key: t.link
96
+ href: l.link,
97
+ key: l.link
38
98
  });
39
- }), e.push(/* @__PURE__ */ s(N, { session: n.session, locale: i }, "session-blocklet")), e.push(
40
- /* @__PURE__ */ s(
41
- L,
99
+ }), o.push(/* @__PURE__ */ e(O, { session: n.session, locale: r }, "session-blocklet")), o.push(
100
+ /* @__PURE__ */ e(
101
+ E,
42
102
  {
43
103
  session: n.session,
44
- locale: i,
45
- menu: f,
104
+ locale: r,
105
+ menu: a,
46
106
  showRole: !0,
47
- ...u
107
+ ...w
48
108
  },
49
109
  "session-user"
50
110
  )
51
111
  );
52
112
  }
53
- return typeof o == "function" && (e = o(e) || []), e;
54
- })(), y = Array.isArray(c) ? c : [c], A = [
55
- m ? /* @__PURE__ */ s(P, { session: n?.session, locale: i }) : null,
56
- ...y
113
+ return typeof i == "function" && (o = i(o) || []), o;
114
+ })(), C = Array.isArray(u) ? u : [u], S = [
115
+ y ? /* @__PURE__ */ e(U, { session: n?.session, locale: r }) : null,
116
+ ...C
57
117
  ].filter(Boolean);
58
- return k(v, null, ...A);
118
+ return W(x, null, ...S);
59
119
  }
60
- x.propTypes = {
61
- formattedBlocklet: r.object.isRequired,
120
+ G.propTypes = {
121
+ formattedBlocklet: s.object.isRequired,
62
122
  // 需要考虑 定制的 addons 与内置的 连接钱包/选择语言 addons 共存的情况
63
123
  // - PropTypes.func: 可以把自定义 addons 插在 session-manager 或 locale-selector (如果存在的话) 前/中/后
64
124
  // - PropTypes.node: 将 addons 原样传给 UX Header 组件
65
- addons: r.oneOfType([r.func, r.node]),
66
- sessionManagerProps: S,
67
- showDomainWarningDialog: r.bool
125
+ addons: s.oneOfType([s.func, s.node]),
126
+ sessionManagerProps: F,
127
+ showDomainWarningDialog: s.bool,
128
+ showWizard: s.bool
68
129
  };
69
130
  export {
70
- x as default
131
+ G as default
71
132
  };
@@ -0,0 +1,14 @@
1
+ import { default as PropTypes } from 'prop-types';
2
+ declare function WizardModal({ onFinished, show, onChangeVisible }: {
3
+ onFinished?: (() => void) | undefined;
4
+ show?: boolean | undefined;
5
+ onChangeVisible?: (() => void) | undefined;
6
+ }): import("react/jsx-runtime").JSX.Element | null;
7
+ declare namespace WizardModal {
8
+ namespace propTypes {
9
+ let onFinished: PropTypes.Requireable<(...args: any[]) => any>;
10
+ let show: PropTypes.Requireable<boolean>;
11
+ let onChangeVisible: PropTypes.Requireable<(...args: any[]) => any>;
12
+ }
13
+ }
14
+ export default WizardModal;
@@ -0,0 +1,132 @@
1
+ import { jsxs as I, jsx as m } from "react/jsx-runtime";
2
+ import { useLocaleContext as U } from "@arcblock/ux/lib/Locale/context";
3
+ import { useTheme as W, useMediaQuery as P, Dialog as T, Box as j, CircularProgress as E } from "@mui/material";
4
+ import p from "prop-types";
5
+ import { useState as w, useRef as g, useEffect as a } from "react";
6
+ import { withQuery as M, joinURL as A } from "ufo";
7
+ const h = "/.well-known/service/wizard/bind-account";
8
+ function D({ onFinished: b = () => {
9
+ }, show: o = !1, onChangeVisible: x = () => {
10
+ } }) {
11
+ const [r, n] = w(o), [i, s] = w(!1), [y, l] = w(() => localStorage.getItem("wizard-current-url") || h), z = g(b), c = g(), d = g(null), { locale: R } = U(), C = W(), u = P(C.breakpoints.down("sm"));
12
+ if (z.current = b, c.current = () => {
13
+ if (d.current?.contentWindow)
14
+ try {
15
+ const t = new URL(d.current.contentWindow.location.href).pathname;
16
+ localStorage.setItem("wizard-current-url", t), l(t);
17
+ } catch (e) {
18
+ l(h), console.warn("Failed to save wizard URL:", e);
19
+ }
20
+ localStorage.setItem("wizard-completed", "true"), n(!1), x(!1);
21
+ }, a(() => {
22
+ o !== r && n(o);
23
+ }, [o]), a(() => {
24
+ !r && i && s(!1);
25
+ }, [r]), a(() => {
26
+ const e = (t) => {
27
+ if (t.origin !== window.location.origin)
28
+ return;
29
+ const { type: L, data: S } = t.data || {};
30
+ switch (L) {
31
+ case "wizard.loaded":
32
+ s(!0);
33
+ break;
34
+ case "wizard.finished": {
35
+ n(!1), l(h), localStorage.removeItem("wizard-current-url"), localStorage.setItem("wizard-completed", "true");
36
+ const f = z.current?.(S);
37
+ f instanceof Promise ? f.then((k) => {
38
+ k !== !1 && window.location.reload();
39
+ }) : f !== !1 && window.location.reload();
40
+ break;
41
+ }
42
+ case "wizard.close": {
43
+ c.current();
44
+ break;
45
+ }
46
+ }
47
+ };
48
+ return window.addEventListener("message", e), () => {
49
+ window.removeEventListener("message", e);
50
+ };
51
+ }, []), a(() => {
52
+ localStorage.getItem("wizard-completed") || n(!0);
53
+ }, []), !r)
54
+ return null;
55
+ const v = M(A(window.location.origin, y), {
56
+ locale: R
57
+ });
58
+ return /* @__PURE__ */ I(
59
+ T,
60
+ {
61
+ open: r,
62
+ onClose: () => c.current(),
63
+ fullWidth: !0,
64
+ maxWidth: u ? !1 : "md",
65
+ fullScreen: u,
66
+ slotProps: {
67
+ paper: {
68
+ sx: {
69
+ margin: 0,
70
+ borderRadius: 0,
71
+ position: "relative",
72
+ overflow: "hidden",
73
+ ...u ? { borderRadius: 0 } : {
74
+ borderRadius: 1,
75
+ height: "max(800px, 80vh)"
76
+ }
77
+ }
78
+ }
79
+ },
80
+ sx: {
81
+ "& .MuiBackdrop-root": {
82
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
83
+ }
84
+ },
85
+ children: [
86
+ /* @__PURE__ */ m(
87
+ "iframe",
88
+ {
89
+ ref: d,
90
+ src: v,
91
+ title: "Setup Wizard",
92
+ style: {
93
+ width: "100%",
94
+ height: "100%",
95
+ border: 0,
96
+ padding: 0,
97
+ margin: 0,
98
+ opacity: i ? 1 : 0,
99
+ transition: "opacity 0.3s ease-in-out"
100
+ },
101
+ onLoad: () => s(!0)
102
+ }
103
+ ),
104
+ i ? null : /* @__PURE__ */ m(
105
+ j,
106
+ {
107
+ sx: {
108
+ position: "absolute",
109
+ top: 0,
110
+ left: 0,
111
+ right: 0,
112
+ bottom: 0,
113
+ display: "flex",
114
+ justifyContent: "center",
115
+ alignItems: "center",
116
+ bgcolor: "background.paper"
117
+ },
118
+ children: /* @__PURE__ */ m(E, {})
119
+ }
120
+ )
121
+ ]
122
+ }
123
+ );
124
+ }
125
+ D.propTypes = {
126
+ onFinished: p.func,
127
+ show: p.bool,
128
+ onChangeVisible: p.func
129
+ };
130
+ export {
131
+ D as default
132
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "3.0.36",
3
+ "version": "3.0.38",
4
4
  "description": "Some useful front-end web components that can be used in Blocklets.",
5
5
  "keywords": [
6
6
  "react",
@@ -35,12 +35,12 @@
35
35
  "dependencies": {
36
36
  "@abtnode/constant": "^1.16.46",
37
37
  "@abtnode/util": "^1.16.46",
38
- "@arcblock/bridge": "3.0.36",
39
- "@arcblock/icons": "3.0.36",
40
- "@arcblock/react-hooks": "3.0.36",
38
+ "@arcblock/bridge": "3.0.38",
39
+ "@arcblock/icons": "3.0.38",
40
+ "@arcblock/react-hooks": "3.0.38",
41
41
  "@arcblock/ws": "^1.21.0",
42
42
  "@blocklet/constant": "^1.16.46",
43
- "@blocklet/did-space-react": "^1.1.10",
43
+ "@blocklet/did-space-react": "^1.1.11",
44
44
  "@iconify-icons/logos": "^1.2.36",
45
45
  "@iconify-icons/material-symbols": "^1.2.58",
46
46
  "@iconify-icons/tabler": "^1.2.95",
@@ -91,5 +91,5 @@
91
91
  "jest": "^29.7.0",
92
92
  "unbuild": "^2.0.0"
93
93
  },
94
- "gitHead": "8d2ee041615e72c57199253d5425b50872642e6e"
94
+ "gitHead": "643c8e01d5f5cb421b3315d4625177d1ccfa1ea1"
95
95
  }
@@ -129,6 +129,7 @@ function Dashboard({
129
129
  addons={headerAddons}
130
130
  sessionManagerProps={sessionManagerProps}
131
131
  showDomainWarningDialog={showDomainWarningDialog}
132
+ showWizard={rest.headerProps?.showWizard}
132
133
  />
133
134
  );
134
135
 
@@ -93,6 +93,7 @@ type HeaderProps = {
93
93
  align?: 'left' | 'right';
94
94
  maxWidth?: false | Breakpoint;
95
95
  showDomainWarningDialog?: boolean;
96
+ showWizard?: boolean;
96
97
  };
97
98
 
98
99
  /**
@@ -110,6 +111,7 @@ function Header({
110
111
  theme: themeOverrides = undefined,
111
112
  hideNavMenu = false,
112
113
  showDomainWarningDialog = true,
114
+ showWizard = false,
113
115
  ...rest
114
116
  }: HeaderProps & Omit<BoxProps, keyof HeaderProps>) {
115
117
  useWalletHiddenTopbar();
@@ -164,6 +166,7 @@ function Header({
164
166
  // @ts-ignore
165
167
  sessionManagerProps={sessionManagerProps}
166
168
  showDomainWarningDialog={showDomainWarningDialog}
169
+ showWizard={showWizard}
167
170
  />
168
171
  );
169
172
 
@@ -1,7 +1,10 @@
1
1
  import 'iconify-icon';
2
2
 
3
- import { use, createElement } from 'react';
3
+ import { use, createElement, useState, useEffect } from 'react';
4
4
  import PropTypes from 'prop-types';
5
+ import { IconButton, Tooltip } from '@mui/material';
6
+ import WandIcon from '@iconify-icons/tabler/wand';
7
+
5
8
  // FIXME: 直接从 react 中 import Fragment 可能会在 vite 下出错,先暂时从 react/jsx-runtime 导入 Fragment 来跳过这个问题
6
9
  import { Fragment } from 'react/jsx-runtime';
7
10
  import { SessionContext } from '@arcblock/did-connect/lib/Session';
@@ -15,6 +18,7 @@ import { SessionManagerProps } from '../types';
15
18
  import { getLocalizedNavigation, filterNavByRole } from '../blocklets';
16
19
  import NotificationAddon from './notification-addon';
17
20
  import DomainWarning from './domain-warning';
21
+ import WizardModal from './wizard-modal';
18
22
 
19
23
  const hasNotification = () => {
20
24
  const navigations = window?.blocklet?.navigation ?? [];
@@ -27,7 +31,25 @@ export default function HeaderAddons({
27
31
  addons = null,
28
32
  showDomainWarningDialog = true,
29
33
  sessionManagerProps = { showRole: true },
34
+ showWizard = false,
30
35
  }) {
36
+ const [wizardOpen, setWizardOpen] = useState(false);
37
+ const [showTooltip, setShowTooltip] = useState(false);
38
+ const { t } = useLocaleContext();
39
+
40
+ // 处理 tooltip 倒计时
41
+ useEffect(() => {
42
+ let timer;
43
+ if (showTooltip) {
44
+ timer = setTimeout(() => {
45
+ setShowTooltip(false);
46
+ }, 3000); // 3秒后自动隐藏
47
+ }
48
+ return () => {
49
+ if (timer) clearTimeout(timer);
50
+ };
51
+ }, [showTooltip]);
52
+
31
53
  const sessionCtx = use(SessionContext);
32
54
  const { locale, languages } = useLocaleContext() || {};
33
55
  const { enableConnect = true, enableLocale = true } = formattedBlocklet;
@@ -55,6 +77,54 @@ export default function HeaderAddons({
55
77
  // 切换明暗主题
56
78
  addonsArray.push(<ThemeModeToggle key="theme-mode-toggle" />);
57
79
 
80
+ // Wizard 开关
81
+ if (showWizard) {
82
+ addonsArray.push(
83
+ <Tooltip
84
+ key={`wizard-toggle-${showTooltip}`}
85
+ title={t('setup.wizardTooltip')}
86
+ open={showTooltip}
87
+ placement="bottom"
88
+ arrow>
89
+ <IconButton
90
+ size="small"
91
+ onClick={() => {
92
+ setWizardOpen(true);
93
+ setShowTooltip(false); // 点击后隐藏 tooltip
94
+ }}
95
+ sx={{
96
+ display: { xs: 'none', md: 'flex' },
97
+ color: 'text.secondary',
98
+ ...(showTooltip && {
99
+ backgroundColor: 'action.hover',
100
+ '@keyframes pulse': {
101
+ '0%': { opacity: 0.8 },
102
+ '50%': { opacity: 0.5 },
103
+ '100%': { opacity: 0.8 },
104
+ },
105
+ animation: 'pulse 2s infinite',
106
+ }),
107
+ }}>
108
+ <iconify-icon icon={WandIcon} height={20} />
109
+ </IconButton>
110
+ </Tooltip>,
111
+ <WizardModal
112
+ key="wizard-modal"
113
+ show={wizardOpen}
114
+ onChangeVisible={(visible) => {
115
+ setWizardOpen(visible);
116
+ if (!visible && wizardOpen) {
117
+ setShowTooltip(true);
118
+ }
119
+ }}
120
+ onFinished={() => {
121
+ setWizardOpen(false);
122
+ setShowTooltip(false);
123
+ }}
124
+ />
125
+ );
126
+ }
127
+
58
128
  // 启用了连接钱包并且检测到了 session context
59
129
  if (enableConnect && sessionCtx) {
60
130
  const menu = [];
@@ -110,4 +180,5 @@ HeaderAddons.propTypes = {
110
180
  addons: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
111
181
  sessionManagerProps: SessionManagerProps,
112
182
  showDomainWarningDialog: PropTypes.bool,
183
+ showWizard: PropTypes.bool,
113
184
  };
@@ -0,0 +1,168 @@
1
+ # WizardModal 使用示例
2
+
3
+ WizardModal 是一个自动弹窗的 iframe 组件,用于在页面加载时显示向导界面。
4
+
5
+ ## 基本用法
6
+
7
+ ### 在 Header 组件中使用
8
+
9
+ ```jsx
10
+ import { Header } from '@arcblock/blocklet-ui-react';
11
+
12
+ function App() {
13
+ return (
14
+ <Header
15
+ meta={blockletMeta}
16
+ // 默认不显示 wizard 按钮,设置为 true 启用
17
+ showWizard={true}
18
+ />
19
+ );
20
+ }
21
+ ```
22
+
23
+ ### 默认行为
24
+
25
+ ```jsx
26
+ // 默认不显示 wizard 按钮
27
+ <Header meta={blockletMeta} />
28
+
29
+ // 显示 wizard 按钮
30
+ <Header meta={blockletMeta} showWizard={true} />
31
+ ```
32
+
33
+ ### 重置向导状态
34
+
35
+ ```javascript
36
+ // 清除已完成标记,下次刷新页面时会重新显示向导
37
+ localStorage.removeItem('wizard-completed');
38
+ window.location.reload();
39
+ ```
40
+
41
+ ### 启用向导
42
+
43
+ ```jsx
44
+ <Header
45
+ meta={blockletMeta}
46
+ showWizard={true} // 设置为 true 显示向导按钮
47
+ />
48
+ ```
49
+
50
+ ### 自定义向导 URL
51
+
52
+ ```jsx
53
+ <Header
54
+ meta={blockletMeta}
55
+ wizardUrl="/.well-known/service/wizard/custom-setup"
56
+ onWizardFinished={(data) => {
57
+ console.log('自定义向导完成:', data);
58
+ }}
59
+ />
60
+ ```
61
+
62
+ ### 使用外部 URL
63
+
64
+ ```jsx
65
+ <Header
66
+ meta={blockletMeta}
67
+ wizardUrl="https://external-wizard.example.com/setup"
68
+ onWizardFinished={(data) => {
69
+ console.log('外部向导完成:', data);
70
+ }}
71
+ />
72
+ ```
73
+
74
+ ## 直接使用 WizardModal 组件
75
+
76
+ ```jsx
77
+ import { WizardModal } from '@arcblock/blocklet-ui-react/lib/common/wizard-modal';
78
+
79
+ function MyComponent() {
80
+ const [showWizard, setShowWizard] = useState(false);
81
+
82
+ return (
83
+ <div>
84
+ <button onClick={() => setShowWizard(true)}>打开向导</button>
85
+
86
+ <WizardModal
87
+ open={showWizard}
88
+ wizardUrl="/.well-known/service/wizard/bind-account"
89
+ onFinished={(data) => {
90
+ console.log('向导完成:', data);
91
+ setShowWizard(false);
92
+ }}
93
+ />
94
+ </div>
95
+ );
96
+ }
97
+ ```
98
+
99
+ ## 向导页面消息通信
100
+
101
+ 向导页面可以通过 postMessage 与父页面通信:
102
+
103
+ ### 向导页面发送消息
104
+
105
+ ```javascript
106
+ // 通知父页面向导已加载完成
107
+ window.parent.postMessage(
108
+ {
109
+ type: 'wizard.loaded',
110
+ },
111
+ window.location.origin
112
+ );
113
+
114
+ // 通知父页面向导已完成
115
+ window.parent.postMessage(
116
+ {
117
+ type: 'wizard.finished',
118
+ data: { success: true, result: 'setup-complete' },
119
+ },
120
+ window.location.origin
121
+ );
122
+
123
+ // 通知父页面关闭向导
124
+ window.parent.postMessage(
125
+ {
126
+ type: 'wizard.close',
127
+ },
128
+ window.location.origin
129
+ );
130
+ ```
131
+
132
+ ## Props 说明
133
+
134
+ ### Header 组件新增 Props
135
+
136
+ | 属性 | 类型 | 默认值 | 说明 |
137
+ | ---------- | ------- | ------ | ---------------- |
138
+ | showWizard | boolean | false | 是否显示向导按钮 |
139
+
140
+ ### WizardModal 组件 Props
141
+
142
+ | 属性 | 类型 | 默认值 | 说明 |
143
+ | --------------- | -------- | --------- | ------------------------------------ |
144
+ | show | boolean | false | 控制弹窗显示状态 |
145
+ | onFinished | function | undefined | 向导完成回调函数 |
146
+ | onChangeVisible | function | undefined | 弹窗显示状态变化回调 |
147
+ | onClose | function | undefined | 弹窗关闭回调(isCompleted: boolean) |
148
+
149
+ ## 新增功能特性
150
+
151
+ ### 1. 智能提示系统
152
+
153
+ - 点击遮罩关闭时显示 tooltip 提示:"想要再次打开 wizard 可以点击这里"
154
+ - Tooltip 显示 5 秒后自动消失
155
+ - 完成向导后不显示 tooltip
156
+ - 按钮在提示期间有闪烁动画效果
157
+
158
+ ### 2. URL 状态保存
159
+
160
+ - 关闭 wizard 时自动保存当前 iframe 的 URL 到 localStorage
161
+ - 下次打开时直接加载上次的位置,继续之前的进度
162
+ - 完成向导后重置为默认 URL
163
+
164
+ ### 3. 交互优化
165
+
166
+ - 点击遮罩或 ESC 键可以关闭弹窗
167
+ - 关闭时会保存当前进度
168
+ - 完成后自动清理临时状态
@@ -0,0 +1,183 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import { Box, CircularProgress, Dialog, useMediaQuery, useTheme } from '@mui/material';
3
+ import PropTypes from 'prop-types';
4
+ import { useEffect, useRef, useState } from 'react';
5
+ import { joinURL, withQuery } from 'ufo';
6
+
7
+ const DEFAULT_WIZARD_PATH = '/.well-known/service/wizard/bind-account';
8
+
9
+ export default function WizardModal({ onFinished = () => {}, show = false, onChangeVisible = () => {} }) {
10
+ const [open, setOpen] = useState(show);
11
+ const [loaded, setLoaded] = useState(false);
12
+ const [currentUrl, setCurrentUrl] = useState(() => {
13
+ // 从 localStorage 恢复上次的 URL
14
+ return localStorage.getItem('wizard-current-url') || DEFAULT_WIZARD_PATH;
15
+ });
16
+ const onFinishedRef = useRef(onFinished);
17
+ const handleCloseRef = useRef();
18
+ const iframeRef = useRef(null);
19
+ const { locale } = useLocaleContext();
20
+ const theme = useTheme();
21
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
22
+ onFinishedRef.current = onFinished;
23
+
24
+ handleCloseRef.current = () => {
25
+ if (iframeRef.current?.contentWindow) {
26
+ try {
27
+ const url = new URL(iframeRef.current.contentWindow.location.href);
28
+ const savedUrl = url.pathname;
29
+ localStorage.setItem('wizard-current-url', savedUrl);
30
+ setCurrentUrl(savedUrl);
31
+ } catch (e) {
32
+ setCurrentUrl(DEFAULT_WIZARD_PATH);
33
+ console.warn('Failed to save wizard URL:', e);
34
+ }
35
+ }
36
+ localStorage.setItem('wizard-completed', 'true');
37
+ setOpen(false);
38
+ onChangeVisible(false);
39
+ };
40
+
41
+ useEffect(() => {
42
+ if (show !== open) {
43
+ setOpen(show);
44
+ }
45
+ }, [show]); // eslint-disable-line react-hooks/exhaustive-deps
46
+
47
+ useEffect(() => {
48
+ if (!open && loaded) {
49
+ setLoaded(false);
50
+ }
51
+ }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
52
+
53
+ // 处理 iframe 消息
54
+ useEffect(() => {
55
+ const listener = (event) => {
56
+ // 只处理来自同源的消息
57
+ if (event.origin !== window.location.origin) {
58
+ return;
59
+ }
60
+
61
+ const { type, data } = event.data || {};
62
+
63
+ switch (type) {
64
+ case 'wizard.loaded':
65
+ setLoaded(true);
66
+ break;
67
+ case 'wizard.finished': {
68
+ setOpen(false);
69
+ // 完成后重置为默认 URL
70
+ setCurrentUrl(DEFAULT_WIZARD_PATH);
71
+ localStorage.removeItem('wizard-current-url');
72
+ localStorage.setItem('wizard-completed', 'true');
73
+ const result = onFinishedRef.current?.(data);
74
+ if (result instanceof Promise) {
75
+ result.then((reload) => {
76
+ if (reload !== false) {
77
+ window.location.reload();
78
+ }
79
+ });
80
+ } else if (result !== false) {
81
+ window.location.reload();
82
+ }
83
+ break;
84
+ }
85
+ case 'wizard.close': {
86
+ handleCloseRef.current();
87
+ break;
88
+ }
89
+ default:
90
+ break;
91
+ }
92
+ };
93
+
94
+ window.addEventListener('message', listener);
95
+ return () => {
96
+ window.removeEventListener('message', listener);
97
+ };
98
+ }, []);
99
+
100
+ // 控制弹窗显示
101
+ useEffect(() => {
102
+ const wizardCompleted = localStorage.getItem('wizard-completed');
103
+ if (!wizardCompleted) {
104
+ setOpen(true);
105
+ }
106
+ }, []);
107
+
108
+ if (!open) {
109
+ return null;
110
+ }
111
+
112
+ const src = withQuery(joinURL(window.location.origin, currentUrl), {
113
+ locale,
114
+ });
115
+
116
+ return (
117
+ <Dialog
118
+ open={open}
119
+ onClose={() => handleCloseRef.current()}
120
+ fullWidth
121
+ maxWidth={isMobile ? false : 'md'}
122
+ fullScreen={isMobile}
123
+ slotProps={{
124
+ paper: {
125
+ sx: {
126
+ margin: 0,
127
+ borderRadius: 0,
128
+ position: 'relative',
129
+ overflow: 'hidden',
130
+ ...(isMobile
131
+ ? { borderRadius: 0 }
132
+ : {
133
+ borderRadius: 1,
134
+ height: 'max(800px, 80vh)',
135
+ }),
136
+ },
137
+ },
138
+ }}
139
+ sx={{
140
+ '& .MuiBackdrop-root': {
141
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
142
+ },
143
+ }}>
144
+ <iframe
145
+ ref={iframeRef}
146
+ src={src}
147
+ title="Setup Wizard"
148
+ style={{
149
+ width: '100%',
150
+ height: '100%',
151
+ border: 0,
152
+ padding: 0,
153
+ margin: 0,
154
+ opacity: loaded ? 1 : 0,
155
+ transition: 'opacity 0.3s ease-in-out',
156
+ }}
157
+ onLoad={() => setLoaded(true)}
158
+ />
159
+ {loaded ? null : (
160
+ <Box
161
+ sx={{
162
+ position: 'absolute',
163
+ top: 0,
164
+ left: 0,
165
+ right: 0,
166
+ bottom: 0,
167
+ display: 'flex',
168
+ justifyContent: 'center',
169
+ alignItems: 'center',
170
+ bgcolor: 'background.paper',
171
+ }}>
172
+ <CircularProgress />
173
+ </Box>
174
+ )}
175
+ </Dialog>
176
+ );
177
+ }
178
+
179
+ WizardModal.propTypes = {
180
+ onFinished: PropTypes.func,
181
+ show: PropTypes.bool,
182
+ onChangeVisible: PropTypes.func,
183
+ };