@blocklet/ui-react 3.0.37 → 3.0.39

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,17 +1,17 @@
1
1
  import { jsx as s, Fragment as v } from "react/jsx-runtime";
2
2
  import "iconify-icon";
3
- import { use as b, createElement as k } from "react";
4
3
  import r from "prop-types";
4
+ import { use as b, createElement as k } from "react";
5
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";
6
+ import L from "@arcblock/ux/lib/Config/theme-mode-toggle";
7
+ import { useLocaleContext as N } from "@arcblock/ux/lib/Locale/context";
8
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";
9
+ import C from "@arcblock/ux/lib/SessionBlocklet";
10
+ import R from "@arcblock/ux/lib/SessionUser";
11
+ import { getLocalizedNavigation as S, filterNavByRole as M } from "../blocklets.js";
12
+ import { SessionManagerProps as B } from "../types.js";
13
+ import D from "./domain-warning.js";
14
+ import P from "./notification-addon.js";
15
15
  const j = () => !!(window?.blocklet?.navigation ?? []).find((o) => o.id === "/userCenter/notification");
16
16
  function x({
17
17
  formattedBlocklet: l,
@@ -19,14 +19,14 @@ function x({
19
19
  showDomainWarningDialog: m = !0,
20
20
  sessionManagerProps: u = { showRole: !0 }
21
21
  }) {
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);
22
+ const n = b(w), { locale: i, languages: p } = N() || {}, { enableConnect: g = !0, enableLocale: d = !0 } = l, h = !!n?.session?.user;
23
+ let a = S(l?.navigation?.sessionManager, i) || [];
24
+ a = M(a, n?.session?.user?.role);
25
25
  const c = (() => {
26
26
  if (o && typeof o != "function")
27
27
  return Array.isArray(o) ? o : [o];
28
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) {
29
+ if (j() && e.push(/* @__PURE__ */ s(P, { session: n.session }, "notification-addon")), d && i && p.length > 1 && e.push(/* @__PURE__ */ s(T, { showText: !1 }, "locale-selector")), e.push(/* @__PURE__ */ s(L, {}, "theme-mode-toggle")), g && n) {
30
30
  const f = [];
31
31
  h && (a ? a.slice(0, 5) : []).forEach((t) => {
32
32
  f.push({
@@ -36,9 +36,9 @@ function x({
36
36
  href: t.link,
37
37
  key: t.link
38
38
  });
39
- }), e.push(/* @__PURE__ */ s(N, { session: n.session, locale: i }, "session-blocklet")), e.push(
39
+ }), e.push(/* @__PURE__ */ s(C, { session: n.session, locale: i }, "session-blocklet")), e.push(
40
40
  /* @__PURE__ */ s(
41
- L,
41
+ R,
42
42
  {
43
43
  session: n.session,
44
44
  locale: i,
@@ -52,7 +52,7 @@ function x({
52
52
  }
53
53
  return typeof o == "function" && (e = o(e) || []), e;
54
54
  })(), y = Array.isArray(c) ? c : [c], A = [
55
- m ? /* @__PURE__ */ s(P, { session: n?.session, locale: i }) : null,
55
+ m ? /* @__PURE__ */ s(D, { session: n?.session, locale: i }) : null,
56
56
  ...y
57
57
  ].filter(Boolean);
58
58
  return k(v, null, ...A);
@@ -63,7 +63,7 @@ x.propTypes = {
63
63
  // - PropTypes.func: 可以把自定义 addons 插在 session-manager 或 locale-selector (如果存在的话) 前/中/后
64
64
  // - PropTypes.node: 将 addons 原样传给 UX Header 组件
65
65
  addons: r.oneOfType([r.func, r.node]),
66
- sessionManagerProps: S,
66
+ sessionManagerProps: B,
67
67
  showDomainWarningDialog: r.bool
68
68
  };
69
69
  export {
@@ -0,0 +1,16 @@
1
+ import { default as PropTypes } from 'prop-types';
2
+ declare function WizardModal({ onFinished, show, onChangeVisible, loadingText, }: {
3
+ onFinished?: (() => void) | undefined;
4
+ show?: boolean | undefined;
5
+ onChangeVisible?: (() => void) | undefined;
6
+ loadingText?: string | undefined;
7
+ }): import("react/jsx-runtime").JSX.Element | null;
8
+ declare namespace WizardModal {
9
+ namespace propTypes {
10
+ let onFinished: PropTypes.Requireable<(...args: any[]) => any>;
11
+ let show: PropTypes.Requireable<boolean>;
12
+ let onChangeVisible: PropTypes.Requireable<(...args: any[]) => any>;
13
+ let loadingText: PropTypes.Requireable<PropTypes.ReactNodeLike>;
14
+ }
15
+ }
16
+ export default WizardModal;
@@ -0,0 +1,141 @@
1
+ import { jsxs as x, jsx as i } from "react/jsx-runtime";
2
+ import { useLocaleContext as P } from "@arcblock/ux/lib/Locale/context";
3
+ import { useTheme as T, useMediaQuery as j, Dialog as D, Box as R, CircularProgress as E, Typography as M } from "@mui/material";
4
+ import a from "prop-types";
5
+ import { useState as g, useRef as h, useEffect as s } from "react";
6
+ import { withQuery as A, joinURL as F } from "ufo";
7
+ const b = "/.well-known/service/wizard/bind-account";
8
+ function B({
9
+ onFinished: y = () => {
10
+ },
11
+ show: o = !1,
12
+ onChangeVisible: v = () => {
13
+ },
14
+ loadingText: l = ""
15
+ }) {
16
+ const [r, n] = g(o), [c, d] = g(!1), [C, u] = g(() => localStorage.getItem("wizard-current-url") || b), z = h(y), f = h(), m = h(null), { locale: I } = P(), L = T(), p = j(L.breakpoints.down("sm"));
17
+ if (z.current = y, f.current = () => {
18
+ if (m.current?.contentWindow)
19
+ try {
20
+ const t = new URL(m.current.contentWindow.location.href).pathname;
21
+ localStorage.setItem("wizard-current-url", t), u(t);
22
+ } catch (e) {
23
+ u(b), console.warn("Failed to save wizard URL:", e);
24
+ }
25
+ localStorage.setItem("wizard-completed", "true"), n(!1), v(!1);
26
+ }, s(() => {
27
+ o !== r && n(o);
28
+ }, [o]), s(() => {
29
+ !r && c && d(!1);
30
+ }, [r]), s(() => {
31
+ const e = (t) => {
32
+ if (t.origin !== window.location.origin)
33
+ return;
34
+ const { type: k, data: U } = t.data || {};
35
+ switch (k) {
36
+ case "wizard.loaded":
37
+ d(!0);
38
+ break;
39
+ case "wizard.finished": {
40
+ n(!1), u(b), localStorage.removeItem("wizard-current-url"), localStorage.setItem("wizard-completed", "true");
41
+ const w = z.current?.(U);
42
+ w instanceof Promise ? w.then((W) => {
43
+ W !== !1 && window.location.reload();
44
+ }) : w !== !1 && window.location.reload();
45
+ break;
46
+ }
47
+ case "wizard.close": {
48
+ f.current();
49
+ break;
50
+ }
51
+ }
52
+ };
53
+ return window.addEventListener("message", e), () => {
54
+ window.removeEventListener("message", e);
55
+ };
56
+ }, []), s(() => {
57
+ localStorage.getItem("wizard-completed") || n(!0);
58
+ }, []), !r)
59
+ return null;
60
+ const S = A(F(window.location.origin, C), {
61
+ locale: I
62
+ });
63
+ return /* @__PURE__ */ x(
64
+ D,
65
+ {
66
+ open: r,
67
+ onClose: () => f.current(),
68
+ fullWidth: !0,
69
+ maxWidth: p ? !1 : "md",
70
+ fullScreen: p,
71
+ slotProps: {
72
+ paper: {
73
+ sx: {
74
+ margin: 0,
75
+ borderRadius: 0,
76
+ position: "relative",
77
+ overflow: "hidden",
78
+ ...p ? { borderRadius: 0 } : {
79
+ borderRadius: 1,
80
+ height: "max(800px, 80vh)"
81
+ }
82
+ }
83
+ }
84
+ },
85
+ sx: {
86
+ "& .MuiBackdrop-root": {
87
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
88
+ }
89
+ },
90
+ children: [
91
+ /* @__PURE__ */ i(
92
+ "iframe",
93
+ {
94
+ ref: m,
95
+ src: S,
96
+ title: "Setup Wizard",
97
+ style: {
98
+ width: "100%",
99
+ height: "100%",
100
+ border: 0,
101
+ padding: 0,
102
+ margin: 0,
103
+ opacity: c ? 1 : 0,
104
+ transition: "opacity 0.3s ease-in-out"
105
+ },
106
+ onLoad: () => d(!0)
107
+ }
108
+ ),
109
+ c ? null : /* @__PURE__ */ i(
110
+ R,
111
+ {
112
+ sx: {
113
+ position: "absolute",
114
+ top: 0,
115
+ left: 0,
116
+ right: 0,
117
+ bottom: 0,
118
+ display: "flex",
119
+ justifyContent: "center",
120
+ alignItems: "center",
121
+ bgcolor: "background.paper"
122
+ },
123
+ children: /* @__PURE__ */ x(R, { sx: { display: "flex", alignItems: "center", flexDirection: "column", gap: 1 }, children: [
124
+ /* @__PURE__ */ i(E, {}),
125
+ typeof l == "string" ? /* @__PURE__ */ i(M, { variant: "body1", children: l }) : l
126
+ ] })
127
+ }
128
+ )
129
+ ]
130
+ }
131
+ );
132
+ }
133
+ B.propTypes = {
134
+ onFinished: a.func,
135
+ show: a.bool,
136
+ onChangeVisible: a.func,
137
+ loadingText: a.node
138
+ };
139
+ export {
140
+ B as default
141
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "3.0.37",
3
+ "version": "3.0.39",
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.16.46",
37
37
  "@abtnode/util": "^1.16.46",
38
- "@arcblock/bridge": "3.0.37",
39
- "@arcblock/icons": "3.0.37",
40
- "@arcblock/react-hooks": "3.0.37",
38
+ "@arcblock/bridge": "3.0.39",
39
+ "@arcblock/icons": "3.0.39",
40
+ "@arcblock/react-hooks": "3.0.39",
41
41
  "@arcblock/ws": "^1.21.0",
42
42
  "@blocklet/constant": "^1.16.46",
43
43
  "@blocklet/did-space-react": "^1.1.11",
@@ -91,5 +91,5 @@
91
91
  "jest": "^29.7.0",
92
92
  "unbuild": "^2.0.0"
93
93
  },
94
- "gitHead": "7b09f31c3bd00c1472469dc926f7ee9e74133085"
94
+ "gitHead": "0b0937b6a161c7332622ae0001849a7d3b2904f1"
95
95
  }
@@ -1,20 +1,21 @@
1
1
  import 'iconify-icon';
2
2
 
3
- import { use, createElement } from 'react';
4
3
  import PropTypes from 'prop-types';
4
+ import { createElement, use } from 'react';
5
+
5
6
  // FIXME: 直接从 react 中 import Fragment 可能会在 vite 下出错,先暂时从 react/jsx-runtime 导入 Fragment 来跳过这个问题
6
- import { Fragment } from 'react/jsx-runtime';
7
7
  import { SessionContext } from '@arcblock/did-connect/lib/Session';
8
- import SessionUser from '@arcblock/ux/lib/SessionUser';
9
- import SessionBlocklet from '@arcblock/ux/lib/SessionBlocklet';
10
- import LocaleSelector from '@arcblock/ux/lib/Locale/selector';
11
- import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
12
8
  import ThemeModeToggle from '@arcblock/ux/lib/Config/theme-mode-toggle';
9
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
10
+ import LocaleSelector from '@arcblock/ux/lib/Locale/selector';
11
+ import SessionBlocklet from '@arcblock/ux/lib/SessionBlocklet';
12
+ import SessionUser from '@arcblock/ux/lib/SessionUser';
13
+ import { Fragment } from 'react/jsx-runtime';
13
14
 
15
+ import { filterNavByRole, getLocalizedNavigation } from '../blocklets';
14
16
  import { SessionManagerProps } from '../types';
15
- import { getLocalizedNavigation, filterNavByRole } from '../blocklets';
16
- import NotificationAddon from './notification-addon';
17
17
  import DomainWarning from './domain-warning';
18
+ import NotificationAddon from './notification-addon';
18
19
 
19
20
  const hasNotification = () => {
20
21
  const navigations = window?.blocklet?.navigation ?? [];
@@ -0,0 +1,192 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import { Box, CircularProgress, Dialog, Typography, 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({
10
+ onFinished = () => {},
11
+ show = false,
12
+ onChangeVisible = () => {},
13
+ loadingText = '',
14
+ }) {
15
+ const [open, setOpen] = useState(show);
16
+ const [loaded, setLoaded] = useState(false);
17
+ const [currentUrl, setCurrentUrl] = useState(() => {
18
+ // 从 localStorage 恢复上次的 URL
19
+ return localStorage.getItem('wizard-current-url') || DEFAULT_WIZARD_PATH;
20
+ });
21
+ const onFinishedRef = useRef(onFinished);
22
+ const handleCloseRef = useRef();
23
+ const iframeRef = useRef(null);
24
+ const { locale } = useLocaleContext();
25
+ const theme = useTheme();
26
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
27
+ onFinishedRef.current = onFinished;
28
+
29
+ handleCloseRef.current = () => {
30
+ if (iframeRef.current?.contentWindow) {
31
+ try {
32
+ const url = new URL(iframeRef.current.contentWindow.location.href);
33
+ const savedUrl = url.pathname;
34
+ localStorage.setItem('wizard-current-url', savedUrl);
35
+ setCurrentUrl(savedUrl);
36
+ } catch (e) {
37
+ setCurrentUrl(DEFAULT_WIZARD_PATH);
38
+ console.warn('Failed to save wizard URL:', e);
39
+ }
40
+ }
41
+ localStorage.setItem('wizard-completed', 'true');
42
+ setOpen(false);
43
+ onChangeVisible(false);
44
+ };
45
+
46
+ useEffect(() => {
47
+ if (show !== open) {
48
+ setOpen(show);
49
+ }
50
+ }, [show]); // eslint-disable-line react-hooks/exhaustive-deps
51
+
52
+ useEffect(() => {
53
+ if (!open && loaded) {
54
+ setLoaded(false);
55
+ }
56
+ }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
57
+
58
+ // 处理 iframe 消息
59
+ useEffect(() => {
60
+ const listener = (event) => {
61
+ // 只处理来自同源的消息
62
+ if (event.origin !== window.location.origin) {
63
+ return;
64
+ }
65
+
66
+ const { type, data } = event.data || {};
67
+
68
+ switch (type) {
69
+ case 'wizard.loaded':
70
+ setLoaded(true);
71
+ break;
72
+ case 'wizard.finished': {
73
+ setOpen(false);
74
+ // 完成后重置为默认 URL
75
+ setCurrentUrl(DEFAULT_WIZARD_PATH);
76
+ localStorage.removeItem('wizard-current-url');
77
+ localStorage.setItem('wizard-completed', 'true');
78
+ const result = onFinishedRef.current?.(data);
79
+ if (result instanceof Promise) {
80
+ result.then((reload) => {
81
+ if (reload !== false) {
82
+ window.location.reload();
83
+ }
84
+ });
85
+ } else if (result !== false) {
86
+ window.location.reload();
87
+ }
88
+ break;
89
+ }
90
+ case 'wizard.close': {
91
+ handleCloseRef.current();
92
+ break;
93
+ }
94
+ default:
95
+ break;
96
+ }
97
+ };
98
+
99
+ window.addEventListener('message', listener);
100
+ return () => {
101
+ window.removeEventListener('message', listener);
102
+ };
103
+ }, []);
104
+
105
+ // 控制弹窗显示
106
+ useEffect(() => {
107
+ const wizardCompleted = localStorage.getItem('wizard-completed');
108
+ if (!wizardCompleted) {
109
+ setOpen(true);
110
+ }
111
+ }, []);
112
+
113
+ if (!open) {
114
+ return null;
115
+ }
116
+
117
+ const src = withQuery(joinURL(window.location.origin, currentUrl), {
118
+ locale,
119
+ });
120
+
121
+ return (
122
+ <Dialog
123
+ open={open}
124
+ onClose={() => handleCloseRef.current()}
125
+ fullWidth
126
+ maxWidth={isMobile ? false : 'md'}
127
+ fullScreen={isMobile}
128
+ slotProps={{
129
+ paper: {
130
+ sx: {
131
+ margin: 0,
132
+ borderRadius: 0,
133
+ position: 'relative',
134
+ overflow: 'hidden',
135
+ ...(isMobile
136
+ ? { borderRadius: 0 }
137
+ : {
138
+ borderRadius: 1,
139
+ height: 'max(800px, 80vh)',
140
+ }),
141
+ },
142
+ },
143
+ }}
144
+ sx={{
145
+ '& .MuiBackdrop-root': {
146
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
147
+ },
148
+ }}>
149
+ <iframe
150
+ ref={iframeRef}
151
+ src={src}
152
+ title="Setup Wizard"
153
+ style={{
154
+ width: '100%',
155
+ height: '100%',
156
+ border: 0,
157
+ padding: 0,
158
+ margin: 0,
159
+ opacity: loaded ? 1 : 0,
160
+ transition: 'opacity 0.3s ease-in-out',
161
+ }}
162
+ onLoad={() => setLoaded(true)}
163
+ />
164
+ {loaded ? null : (
165
+ <Box
166
+ sx={{
167
+ position: 'absolute',
168
+ top: 0,
169
+ left: 0,
170
+ right: 0,
171
+ bottom: 0,
172
+ display: 'flex',
173
+ justifyContent: 'center',
174
+ alignItems: 'center',
175
+ bgcolor: 'background.paper',
176
+ }}>
177
+ <Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column', gap: 1 }}>
178
+ <CircularProgress />
179
+ {typeof loadingText === 'string' ? <Typography variant="body1">{loadingText}</Typography> : loadingText}
180
+ </Box>
181
+ </Box>
182
+ )}
183
+ </Dialog>
184
+ );
185
+ }
186
+
187
+ WizardModal.propTypes = {
188
+ onFinished: PropTypes.func,
189
+ show: PropTypes.bool,
190
+ onChangeVisible: PropTypes.func,
191
+ loadingText: PropTypes.node,
192
+ };