@arcblock/ux 3.0.32 → 3.0.34

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.
@@ -21,6 +21,7 @@ export type AvatarProps = {
21
21
  shape?: "" | "rectangle" | "square" | "hexagon" | "circle";
22
22
  blockiesPadding?: true | false;
23
23
  responsive?: false | true;
24
+ useProxyFallback?: boolean;
24
25
  } & Omit<import('../Img').ImgProps, "size" | "src">;
25
26
  export { Avatar as AvatarProps };
26
27
  /**
@@ -34,6 +35,7 @@ export { Avatar as AvatarProps };
34
35
  * shape?: '' | 'rectangle' | 'square' | 'hexagon' | 'circle';
35
36
  * blockiesPadding?: true | false;
36
37
  * responsive?: false | true;
38
+ * useProxyFallback?: boolean;
37
39
  * } & Omit<import('../Img').ImgProps, 'size' | 'src'>} AvatarProps
38
40
  */
39
41
  /**
@@ -51,6 +53,7 @@ declare namespace Avatar {
51
53
  let shape: PropTypes.Requireable<string>;
52
54
  let blockiesPadding: PropTypes.Requireable<boolean>;
53
55
  let responsive: PropTypes.Requireable<boolean>;
56
+ let useProxyFallback: PropTypes.Requireable<boolean>;
54
57
  }
55
58
  export { propTypes_1 as propTypes };
56
59
  }
@@ -1,6 +1,6 @@
1
1
  import { jsx as o } from "react/jsx-runtime";
2
- import { useState as k, useMemo as P } from "react";
3
- import t from "prop-types";
2
+ import { useState as I, useMemo as P } from "react";
3
+ import a from "prop-types";
4
4
  import { ErrorBoundary as z } from "react-error-boundary";
5
5
  import { Shape as D } from "@arcblock/did-motif";
6
6
  import { Box as u } from "@mui/material";
@@ -13,12 +13,12 @@ import { DID_PREFIX as f } from "../Util/constant.js";
13
13
  function d({ ...s }) {
14
14
  const r = Object.assign({}, s);
15
15
  n(r.did) && (r.did = ""), n(r.size) && (r.size = 36), n(r.variant) && (r.variant = "default"), n(r.animation) && (r.animation = !1), n(r.shape) && (r.shape = ""), n(r.blockiesPadding) && (r.blockiesPadding = !0), n(r.responsive) && (r.responsive = !1);
16
- const [c, h] = k(!1), g = E(r, d, []), { did: i = "", size: e, src: p, variant: b, animation: v, shape: x, blockiesPadding: y, responsive: w, ...a } = g, l = P(() => R(i) ? T.createIcon({
16
+ const [l, h] = I(!1), g = E(r, d, []), { did: i = "", size: e, src: p, variant: b, animation: v, shape: x, blockiesPadding: y, responsive: k, ...t } = g, c = P(() => R(i) ? T.createIcon({
17
17
  seed: i.replace(f, "").toLowerCase(),
18
18
  size: 8,
19
19
  scale: 16
20
20
  }).toDataURL() : null, [i]);
21
- if (p && !c)
21
+ if (p && !l)
22
22
  return /* @__PURE__ */ o(
23
23
  m,
24
24
  {
@@ -26,8 +26,9 @@ function d({ ...s }) {
26
26
  src: p,
27
27
  alt: i,
28
28
  onError: () => h(!0),
29
- ...a,
30
- className: `avatar-img--${b} ${a?.className || ""}`,
29
+ ...t,
30
+ className: `avatar-img--${b} ${t?.className || ""}`,
31
+ useProxyFallback: !0,
31
32
  sx: {
32
33
  "& .avatar-img--rounded": {
33
34
  borderRadius: "4px",
@@ -37,17 +38,17 @@ function d({ ...s }) {
37
38
  borderRadius: "100%",
38
39
  overflow: "hidden"
39
40
  },
40
- ...a.sx
41
+ ...t.sx
41
42
  }
42
43
  }
43
44
  );
44
- if (l) {
45
+ if (c) {
45
46
  if (y) {
46
- const I = e > 24 ? 4 : 2;
47
+ const w = e > 24 ? 4 : 2;
47
48
  return /* @__PURE__ */ o(
48
49
  u,
49
50
  {
50
- ...a,
51
+ ...t,
51
52
  sx: {
52
53
  display: "flex",
53
54
  alignItems: "center",
@@ -63,13 +64,13 @@ function d({ ...s }) {
63
64
  borderRadius: `${Math.min(10, Math.floor(0.1 * e + 2))}px`,
64
65
  background: "#ddd"
65
66
  },
66
- ...a.sx
67
+ ...t.sx
67
68
  },
68
- children: /* @__PURE__ */ o("div", { className: "blocky-icon-inner", children: /* @__PURE__ */ o(m, { width: e * 0.7 - I * 2, src: l, alt: i }) })
69
+ children: /* @__PURE__ */ o("div", { className: "blocky-icon-inner", children: /* @__PURE__ */ o(m, { width: e * 0.7 - w * 2, src: c, alt: i }) })
69
70
  }
70
71
  );
71
72
  }
72
- return /* @__PURE__ */ o(m, { ...a, width: e, src: l, alt: i, style: { marginRight: 4 } });
73
+ return /* @__PURE__ */ o(m, { ...t, width: e, src: c, alt: i, style: { marginRight: 4 } });
73
74
  }
74
75
  if (i)
75
76
  return /* @__PURE__ */ o(
@@ -79,25 +80,26 @@ function d({ ...s }) {
79
80
  size: e,
80
81
  animation: v,
81
82
  shape: D[(x || "").toUpperCase()],
82
- responsive: w,
83
- ...a
83
+ responsive: k,
84
+ ...t
84
85
  }
85
86
  );
86
87
  throw new Error(`Invalid DID: ${i}`);
87
88
  }
88
89
  d.propTypes = {
89
- did: t.string,
90
- size: t.number,
91
- variant: t.oneOf(["circle", "rounded", "default"]),
90
+ did: a.string,
91
+ size: a.number,
92
+ variant: a.oneOf(["circle", "rounded", "default"]),
92
93
  // animation 仅对 did motif 有效
93
- animation: t.bool,
94
+ animation: a.bool,
94
95
  // shape 仅对 did motif 有效, 明确指定 motif shape, 而非由 did role type 自动推断 shape
95
- shape: t.oneOf(["", "rectangle", "square", "hexagon", "circle"]),
96
- blockiesPadding: t.bool,
97
- responsive: t.bool
96
+ shape: a.oneOf(["", "rectangle", "square", "hexagon", "circle"]),
97
+ blockiesPadding: a.bool,
98
+ responsive: a.bool,
99
+ useProxyFallback: a.bool
98
100
  };
99
101
  function $(s) {
100
- const r = s.size || 36, c = { rounded: "4px", circle: "100%" }[s.variant] || 0;
102
+ const r = s.size || 36, l = { rounded: "4px", circle: "100%" }[s.variant] || 0;
101
103
  return /* @__PURE__ */ o(
102
104
  z,
103
105
  {
@@ -108,7 +110,7 @@ function $(s) {
108
110
  width: r,
109
111
  height: r,
110
112
  bgcolor: "grey.300",
111
- borderRadius: c
113
+ borderRadius: l
112
114
  }
113
115
  }
114
116
  ),
@@ -1,21 +1,12 @@
1
- import { jsxs as a, jsx as o } from "react/jsx-runtime";
2
- import { Box as r, Typography as n } from "@mui/material";
1
+ import { jsxs as r, jsx as t } from "react/jsx-runtime";
2
+ import { Box as e, Typography as l } from "@mui/material";
3
3
  import i from "../../Img/index.js";
4
- import { BLOCKLET_SERVICE_PATH_PREFIX as s } from "../../Util/constant.js";
5
- const l = (e) => {
6
- if (!e) return "";
7
- try {
8
- return new URL(e).protocol !== "https:" ? (console.warn("Image URL must use HTTPS protocol:", e), "") : `${s}/proxy?url=${e}`;
9
- } catch (t) {
10
- return console.warn("Invalid image URL format:", e, t), "";
11
- }
12
- };
13
- function g({
14
- requestAppInfo: e,
15
- currentAppInfo: t
4
+ function m({
5
+ requestAppInfo: a,
6
+ currentAppInfo: o
16
7
  }) {
17
- return /* @__PURE__ */ a(
18
- r,
8
+ return /* @__PURE__ */ r(
9
+ e,
19
10
  {
20
11
  sx: {
21
12
  textAlign: "center",
@@ -25,8 +16,8 @@ function g({
25
16
  gap: 2
26
17
  },
27
18
  children: [
28
- /* @__PURE__ */ a(
29
- r,
19
+ /* @__PURE__ */ r(
20
+ e,
30
21
  {
31
22
  sx: {
32
23
  display: "flex",
@@ -34,18 +25,9 @@ function g({
34
25
  justifyContent: "center"
35
26
  },
36
27
  children: [
37
- /* @__PURE__ */ o(
38
- i,
39
- {
40
- src: t.appLogo,
41
- alt: "Server",
42
- width: 48,
43
- height: 48,
44
- fallback: l(t.appLogo)
45
- }
46
- ),
47
- /* @__PURE__ */ a(
48
- r,
28
+ /* @__PURE__ */ t(i, { src: o.appLogo, alt: "Server", width: 48, height: 48, useProxyFallback: !0 }),
29
+ /* @__PURE__ */ r(
30
+ e,
49
31
  {
50
32
  sx: {
51
33
  mx: 2,
@@ -60,27 +42,18 @@ function g({
60
42
  }
61
43
  },
62
44
  children: [
63
- /* @__PURE__ */ o(r, { className: "dot" }),
64
- /* @__PURE__ */ o(r, { className: "dot" }),
65
- /* @__PURE__ */ o(r, { className: "dot" })
45
+ /* @__PURE__ */ t(e, { className: "dot" }),
46
+ /* @__PURE__ */ t(e, { className: "dot" }),
47
+ /* @__PURE__ */ t(e, { className: "dot" })
66
48
  ]
67
49
  }
68
50
  ),
69
- /* @__PURE__ */ o(
70
- i,
71
- {
72
- src: e.appLogo,
73
- alt: e.appName,
74
- width: 48,
75
- height: 48,
76
- fallback: l(e.appLogo)
77
- }
78
- )
51
+ /* @__PURE__ */ t(i, { src: a.appLogo, alt: a.appName, width: 48, height: 48, useProxyFallback: !0 })
79
52
  ]
80
53
  }
81
54
  ),
82
- /* @__PURE__ */ a(
83
- n,
55
+ /* @__PURE__ */ r(
56
+ l,
84
57
  {
85
58
  sx: {
86
59
  mb: 1,
@@ -89,7 +62,7 @@ function g({
89
62
  children: [
90
63
  "Authorize",
91
64
  " ",
92
- /* @__PURE__ */ o(r, { component: "span", sx: { color: "primary.main" }, children: e.appName })
65
+ /* @__PURE__ */ t(e, { component: "span", sx: { color: "primary.main" }, children: a.appName })
93
66
  ]
94
67
  }
95
68
  )
@@ -98,5 +71,5 @@ function g({
98
71
  );
99
72
  }
100
73
  export {
101
- g as default
74
+ m as default
102
75
  };
@@ -19,6 +19,13 @@ export type ImgExProps = {
19
19
  lazy?: boolean | undefined;
20
20
  placeholder?: string | undefined;
21
21
  fallback?: string | undefined;
22
+ /**
23
+ * - 是否使用代理 fallback, 用于解决 CSP 的问题
24
+ */
25
+ /**
26
+ * - 是否使用代理 fallback, 用于解决 CSP 的问题
27
+ */
28
+ useProxyFallback?: boolean | undefined;
22
29
  className?: string | undefined;
23
30
  onError?: Function | undefined;
24
31
  onSuccess?: Function | undefined;
@@ -29,7 +36,7 @@ export type ImgProps = ImgExProps & import('@mui/material').BoxProps;
29
36
  * @param {ImgProps} props
30
37
  * @returns {React.ReactComponentElement}
31
38
  */
32
- declare function Img({ lazy, width, height, repeat, ratio, alt, size, position, src, placeholder, fallback, style, className, onError, onSuccess, ...rest }: ImgProps): import('react').ReactComponentElement<any, Pick<any, string | number | symbol>>;
39
+ declare function Img({ lazy, width, height, repeat, ratio, alt, size, position, src, useProxyFallback, placeholder, fallback, style, className, onError, onSuccess, ...rest }: ImgProps): import('react').ReactComponentElement<any, Pick<any, string | number | symbol>>;
33
40
  declare namespace Img {
34
41
  namespace propTypes {
35
42
  let src: PropTypes.Validator<string>;
@@ -47,5 +54,6 @@ declare namespace Img {
47
54
  let className: PropTypes.Requireable<string>;
48
55
  let onError: PropTypes.Requireable<(...args: any[]) => any>;
49
56
  let onSuccess: PropTypes.Requireable<(...args: any[]) => any>;
57
+ let useProxyFallback: PropTypes.Requireable<boolean>;
50
58
  }
51
59
  }
package/lib/Img/index.js CHANGED
@@ -1,83 +1,101 @@
1
- import { jsx as t, jsxs as W } from "react/jsx-runtime";
2
- import { useState as H, useMemo as f, useEffect as V } from "react";
1
+ import { jsx as s, jsxs as F } from "react/jsx-runtime";
2
+ import { useState as R, useMemo as d, useEffect as w } from "react";
3
3
  import e from "prop-types";
4
- import { useInView as X } from "react-intersection-observer";
5
- import _ from "@iconify-icons/material-symbols/warning-rounded";
6
- import q from "@iconify-icons/material-symbols/image-rounded";
7
- import { Icon as h } from "@iconify/react";
4
+ import { useInView as V } from "react-intersection-observer";
5
+ import W from "@iconify-icons/material-symbols/warning-rounded";
6
+ import X from "@iconify-icons/material-symbols/image-rounded";
7
+ import { Icon as k } from "@iconify/react";
8
8
  import { Box as B } from "@mui/material";
9
- import I from "lodash/noop";
10
- import { mergeSx as C } from "../Util/style.js";
11
- const D = "Img", b = {
12
- root: `${D}-root`
9
+ import $ from "lodash/noop";
10
+ import { mergeSx as q } from "../Util/style.js";
11
+ import { BLOCKLET_SERVICE_PATH_PREFIX as A } from "../Util/constant.js";
12
+ const D = (r) => {
13
+ if (!r) return "";
14
+ try {
15
+ return new URL(r).protocol !== "https:" ? (console.warn("Image URL must use HTTPS protocol:", r), "") : `${A}/proxy?url=${encodeURIComponent(r)}`;
16
+ } catch (t) {
17
+ return console.warn("Invalid image URL format:", r, t), "";
18
+ }
19
+ }, K = "Img", v = {
20
+ root: `${K}-root`
13
21
  };
14
- function F({
15
- lazy: x = !0,
16
- width: i = null,
17
- height: c = null,
18
- repeat: y = "no-repeat",
19
- ratio: S = 1,
20
- alt: N = null,
21
- size: g = "cover",
22
- position: u = "top center",
23
- src: r,
24
- placeholder: n = null,
25
- fallback: a = null,
26
- style: v = null,
27
- className: $ = "",
28
- onError: k = I,
29
- onSuccess: w = I,
30
- ...d
22
+ function M({
23
+ lazy: r = !0,
24
+ width: t = null,
25
+ height: f = null,
26
+ repeat: E = "no-repeat",
27
+ ratio: N = 1,
28
+ alt: P = null,
29
+ size: p = "cover",
30
+ position: I = "top center",
31
+ src: n,
32
+ useProxyFallback: b = !1,
33
+ // 是否使用代理 fallback, 用于解决 CSP 的问题
34
+ placeholder: c = null,
35
+ fallback: m = null,
36
+ style: T = null,
37
+ className: j = "",
38
+ onError: h = $,
39
+ onSuccess: L = $,
40
+ ...y
31
41
  }) {
32
- const [R, p] = x ? X({ threshold: 0, triggerOnce: !0 }) : [null, !0], [o, s] = H("init"), m = f(() => {
42
+ const [U, x] = r ? V({ threshold: 0, triggerOnce: !0 }) : [null, !0], [o, g] = R("init"), [l, S] = R(!1), a = d(() => m || (b && n ? D(n) : m), [n, b, m]), u = d(() => {
33
43
  switch (o) {
34
44
  case "init":
35
45
  case "loading":
36
- return n;
46
+ return c;
37
47
  case "error":
38
- return a;
48
+ return l ? null : a;
39
49
  case "loaded":
40
- return r;
50
+ return n;
41
51
  default:
42
52
  return null;
43
53
  }
44
- }, [n, a, r, o]), j = i && c ? 100 * c / i : S * 100, z = f(
54
+ }, [c, a, n, o, l]), _ = t && f ? 100 * f / t : N * 100, C = d(
45
55
  () => ({
46
- backgroundImage: m ? `url(${m})` : "",
47
- backgroundPosition: u,
48
- backgroundSize: g,
49
- backgroundRepeat: y,
50
- paddingTop: `${j}%`
56
+ backgroundImage: u ? `url(${u})` : "",
57
+ backgroundPosition: I,
58
+ backgroundSize: p,
59
+ backgroundRepeat: E,
60
+ paddingTop: `${_}%`
51
61
  }),
52
62
  // eslint-disable-next-line react-hooks/exhaustive-deps
53
- [m, u, g, o]
54
- ), E = {
55
- ...v,
63
+ [u, I, p, o]
64
+ ), H = {
65
+ ...T,
56
66
  display: "inline-block",
57
- width: i ? `${i}px` : "100%"
67
+ width: t ? `${t}px` : "100%"
58
68
  };
59
- function P() {
60
- const l = new Image();
61
- l.src = r, s("loading"), l.onload = () => {
62
- s("loaded"), w();
63
- }, l.onerror = (T) => {
64
- s("error"), k(T);
69
+ function O() {
70
+ const i = new Image();
71
+ i.src = n, g("loading"), S(!1), i.onload = () => {
72
+ g("loaded"), L();
73
+ }, i.onerror = () => {
74
+ g("error");
65
75
  };
66
76
  }
67
- return V(() => {
68
- p && P();
69
- }, [p]), // paddingTop 要求元素本身的宽度为 100%,所以只能加一个外层元素去限制宽度
70
- /* @__PURE__ */ t(
77
+ return w(() => {
78
+ x && O();
79
+ }, [x]), w(() => {
80
+ if (o === "error" && a && !l) {
81
+ const i = new Image();
82
+ i.src = a, i.onload = () => {
83
+ }, i.onerror = (z) => {
84
+ S(!0), o === "error" && h(z);
85
+ };
86
+ }
87
+ }, [o, a, l, h]), // paddingTop 要求元素本身的宽度为 100%,所以只能加一个外层元素去限制宽度
88
+ /* @__PURE__ */ s(
71
89
  B,
72
90
  {
73
91
  "data-id": "2",
74
- ref: R,
75
- style: E,
92
+ ref: U,
93
+ style: H,
76
94
  className: "arcblock_ux_img-wrapper",
77
- ...d,
78
- sx: C(
95
+ ...y,
96
+ sx: q(
79
97
  {
80
- [`& .${b.root}`]: {
98
+ [`& .${v.root}`]: {
81
99
  position: "relative",
82
100
  overflow: "hidden",
83
101
  "& .image--state, & .image--img": {
@@ -109,17 +127,17 @@ function F({
109
127
  }
110
128
  }
111
129
  },
112
- d.sx
130
+ y.sx
113
131
  ),
114
- children: /* @__PURE__ */ W("div", { className: `image ${$} ${b.root}`, style: z, children: [
115
- !a && o === "error" && /* @__PURE__ */ t("div", { className: "image--state", title: "loading image", children: /* @__PURE__ */ t(h, { icon: _, className: "image--icon" }) }),
116
- !n && o === "loading" && /* @__PURE__ */ t("div", { className: "image--state", title: "Image load error", children: /* @__PURE__ */ t(h, { icon: q, className: "image--icon" }) }),
117
- o === "loaded" && /* @__PURE__ */ t("img", { className: "image--img", src: r, alt: N })
132
+ children: /* @__PURE__ */ F("div", { className: `image ${j} ${v.root}`, style: C, children: [
133
+ (l || !a && o === "error") && /* @__PURE__ */ s("div", { className: "image--state", title: "Image load error", children: /* @__PURE__ */ s(k, { icon: W, className: "image--icon" }) }),
134
+ !c && o === "loading" && /* @__PURE__ */ s("div", { className: "image--state", title: "loading image", children: /* @__PURE__ */ s(k, { icon: X, className: "image--icon" }) }),
135
+ o === "loaded" && /* @__PURE__ */ s("img", { className: "image--img", src: n, alt: P })
118
136
  ] })
119
137
  }
120
138
  );
121
139
  }
122
- F.propTypes = {
140
+ M.propTypes = {
123
141
  src: e.string.isRequired,
124
142
  alt: e.string,
125
143
  size: e.string,
@@ -134,8 +152,9 @@ F.propTypes = {
134
152
  fallback: e.string,
135
153
  className: e.string,
136
154
  onError: e.func,
137
- onSuccess: e.func
155
+ onSuccess: e.func,
156
+ useProxyFallback: e.bool
138
157
  };
139
158
  export {
140
- F as default
159
+ M as default
141
160
  };
@@ -1,15 +1,15 @@
1
- import { jsxs as h, jsx as a } from "react/jsx-runtime";
1
+ import { jsxs as g, jsx as i } from "react/jsx-runtime";
2
2
  import { Box as n } from "@mui/material";
3
- function b({
3
+ function x({
4
4
  size: s = 64,
5
- padding: d = void 0,
6
- borderWidth: l = void 0,
7
- borderRadius: f = void 0,
5
+ padding: l = void 0,
6
+ borderWidth: f = void 0,
7
+ borderRadius: d = void 0,
8
8
  duration: c = void 0,
9
- children: g
9
+ children: m
10
10
  }) {
11
- const t = s, i = f ?? t / 8, e = l ?? t / 32, o = d ?? t * 0.25 - e, p = c ?? 2.5;
12
- return /* @__PURE__ */ h(
11
+ const t = s, a = d ?? t / 8, e = f ?? t / 32, r = l ?? t * 0.35 - e, p = c ?? 2.5;
12
+ return /* @__PURE__ */ g(
13
13
  n,
14
14
  {
15
15
  sx: {
@@ -21,7 +21,7 @@ function b({
21
21
  position: "relative"
22
22
  },
23
23
  children: [
24
- /* @__PURE__ */ a(
24
+ /* @__PURE__ */ i(
25
25
  n,
26
26
  {
27
27
  sx: {
@@ -37,7 +37,7 @@ function b({
37
37
  width: t,
38
38
  height: t,
39
39
  overflow: "hidden",
40
- borderRadius: `${i}px`,
40
+ borderRadius: `${a}px`,
41
41
  backgroundColor: "grey.200",
42
42
  "&::before,&::after": {
43
43
  content: '""',
@@ -50,7 +50,7 @@ function b({
50
50
  top: e,
51
51
  bottom: e,
52
52
  backgroundColor: "background.default",
53
- borderRadius: `${i - e}px`
53
+ borderRadius: `${a - e}px`
54
54
  },
55
55
  "&::before": {
56
56
  width: t * 5,
@@ -58,7 +58,7 @@ function b({
58
58
  top: "50%",
59
59
  left: "50%",
60
60
  transform: "translate(-50%, -50%)",
61
- background: ({ palette: r }) => `conic-gradient(from 45deg, transparent 0%, transparent 50%, ${r.secondary.main} 90%, ${r.secondary.main} 100%)`,
61
+ background: ({ palette: o }) => `conic-gradient(from 45deg, transparent 0%, transparent 50%, ${o.secondary.main} 90%, ${o.secondary.main} 100%)`,
62
62
  animation: `rotate ${p}s linear infinite`,
63
63
  "@keyframes rotate": {
64
64
  "0%": {
@@ -72,7 +72,7 @@ function b({
72
72
  }
73
73
  }
74
74
  ),
75
- /* @__PURE__ */ a(
75
+ /* @__PURE__ */ i(
76
76
  n,
77
77
  {
78
78
  sx: {
@@ -80,10 +80,20 @@ function b({
80
80
  display: "flex",
81
81
  justifyContent: "center",
82
82
  alignItems: "center",
83
- width: t - o - e,
84
- height: t - o - e
83
+ width: t - r - e,
84
+ height: t - r - e,
85
+ animation: "wait-bounce 1.6s infinite",
86
+ transformOrigin: "center bottom",
87
+ "@keyframes wait-bounce": {
88
+ "0%": { transform: "scale(1) translateY(0)" },
89
+ "20%": { transform: "scale(1.03) translateY(-4px)" },
90
+ "40%": { transform: "scale(0.98) translateY(2px)" },
91
+ "60%": { transform: "scale(1.01) translateY(-2px)" },
92
+ "80%": { transform: "scale(0.99) translateY(1px)" },
93
+ "100%": { transform: "scale(1) translateY(0)" }
94
+ }
85
95
  },
86
- children: g
96
+ children: m
87
97
  }
88
98
  )
89
99
  ]
@@ -91,5 +101,5 @@ function b({
91
101
  );
92
102
  }
93
103
  export {
94
- b as default
104
+ x as default
95
105
  };
@@ -1,17 +1,17 @@
1
1
  import { jsx as a } from "react/jsx-runtime";
2
- import { Box as l, Avatar as f } from "@mui/material";
3
- import p from "../Avatar/index.js";
4
- import { createNameOnlyAvatar as u } from "./utils.js";
5
- const N = (r, t = 48, n = void 0, e = void 0, c = !1) => {
6
- const o = (i) => {
2
+ import { Box as o, Avatar as f } from "@mui/material";
3
+ import u from "../Avatar/index.js";
4
+ import { createNameOnlyAvatar as x } from "./utils.js";
5
+ const N = (r, t = 48, c = void 0, e = void 0, l = !1) => {
6
+ const n = (i) => {
7
7
  e?.(r, i);
8
8
  };
9
9
  if (!r.avatar) {
10
- const i = u(r), { className: s, style: d, alt: m } = n || {};
10
+ const i = x(r), { className: s, style: d, alt: m } = c || {};
11
11
  return /* @__PURE__ */ a(
12
12
  f,
13
13
  {
14
- onClick: o,
14
+ onClick: n,
15
15
  className: s,
16
16
  style: d,
17
17
  alt: m,
@@ -19,7 +19,7 @@ const N = (r, t = 48, n = void 0, e = void 0, c = !1) => {
19
19
  width: t,
20
20
  height: t,
21
21
  fontSize: t * 0.4,
22
- cursor: c || e ? "pointer" : "default"
22
+ cursor: l || e ? "pointer" : "default"
23
23
  },
24
24
  variant: "circular",
25
25
  children: i
@@ -27,30 +27,31 @@ const N = (r, t = 48, n = void 0, e = void 0, c = !1) => {
27
27
  );
28
28
  }
29
29
  return /* @__PURE__ */ a(
30
- l,
30
+ o,
31
31
  {
32
32
  className: "user-card__avatar",
33
33
  sx: {
34
34
  display: "flex"
35
35
  },
36
36
  children: /* @__PURE__ */ a(
37
- p,
37
+ u,
38
38
  {
39
39
  size: t,
40
40
  did: r.did,
41
41
  variant: "circle",
42
+ useProxyFallback: !0,
42
43
  style: {
43
- cursor: c || e ? "pointer" : "default"
44
+ cursor: l || e ? "pointer" : "default"
44
45
  },
45
- onClick: o,
46
+ onClick: n,
46
47
  src: r.avatar,
47
48
  alt: r.fullName || "",
48
- ...n || {}
49
+ ...c || {}
49
50
  }
50
51
  )
51
52
  }
52
53
  );
53
- }, _ = (r, t = 120) => r ? /* @__PURE__ */ a(l, { sx: { maxWidth: t }, className: "user-card__top-right-content", children: r() }) : null;
54
+ }, _ = (r, t = 120) => r ? /* @__PURE__ */ a(o, { sx: { maxWidth: t }, className: "user-card__top-right-content", children: r() }) : null;
54
55
  export {
55
56
  N as renderAvatar,
56
57
  _ as renderTopRight
@@ -1,34 +1,44 @@
1
1
  import { jsx as t } from "react/jsx-runtime";
2
- import { useRef as c, useState as h, useEffect as C } from "react";
3
- import { CardType as i } from "./types.js";
4
- import v from "./Cards/avatar-only.js";
5
- import f from "./Cards/index.js";
6
- import y from "./Container/dialog.js";
7
- import A from "./Container/card.js";
8
- import D from "../Avatar/index.js";
9
- import { isUserDid as P, getUserByDid as S } from "./utils.js";
2
+ import { useRef as v, useState as C, useEffect as y, useMemo as P } from "react";
3
+ import { CardType as o } from "./types.js";
4
+ import A from "./Cards/avatar-only.js";
5
+ import m from "./Cards/index.js";
6
+ import D from "./Container/dialog.js";
7
+ import S from "./Container/card.js";
8
+ import w from "../Avatar/index.js";
9
+ import { isUserDid as x, getUserByDid as U } from "./utils.js";
10
10
  function L(e) {
11
11
  if (!e) return null;
12
12
  let r = "";
13
13
  return e.fullName ? r = e.fullName.charAt(0).toUpperCase() : e.email ? r = e.email.split("@")[0].charAt(0).toUpperCase() : r = e.did ? e.did.charAt(0).toUpperCase() : "?", r;
14
14
  }
15
- function g(e) {
16
- const { cardType: r = i.Detailed, showHoverCard: d } = e, n = d !== void 0 ? d : r === i.AvatarOnly, m = c(null), [a, o] = h(() => e.user || null);
17
- if (C(() => {
18
- let u = !0;
19
- return e.user ? o(e.user) : e.did && P(e.did) && !e.user && S(e.did).then((s) => {
20
- u && o(s || { fullName: "Anonymous", did: e.did, avatar: "" });
15
+ function B(e) {
16
+ const { cardType: r = o.Detailed, showHoverCard: l } = e, u = l !== void 0 ? l : r === o.AvatarOnly, c = v(null), [i, f] = C(() => e.user || null);
17
+ y(() => {
18
+ let a = !0;
19
+ return e.user ? f(e.user) : e.did && x(e.did) && !e.user && U(e.did).then((d) => {
20
+ a && f(d || { fullName: "Anonymous", did: e.did, avatar: "" });
21
21
  }), () => {
22
- u = !1;
22
+ a = !1;
23
23
  };
24
- }, [e.did, e.user]), !a)
25
- return /* @__PURE__ */ t(D, { did: e.did, size: e.avatarSize, ...e.avatarProps });
26
- const l = () => /* @__PURE__ */ t(y, { sx: e.popupSx, children: /* @__PURE__ */ t(
27
- f,
24
+ }, [e.did, e.user]);
25
+ const n = P(() => {
26
+ if (!i)
27
+ return null;
28
+ const { avatar: a = "", ...d } = i, h = a.split("?")[1];
29
+ return a && !h ? {
30
+ ...d,
31
+ avatar: `${a}?imageFilter=resize&w=48&h=48`
32
+ } : i;
33
+ }, [i]);
34
+ if (!n)
35
+ return /* @__PURE__ */ t(w, { did: e.did, size: e.avatarSize, useProxyFallback: !0, ...e.avatarProps });
36
+ const s = () => /* @__PURE__ */ t(D, { sx: e.popupSx, children: /* @__PURE__ */ t(
37
+ m,
28
38
  {
29
39
  ...e,
30
40
  shouldShowHoverCard: !1,
31
- user: a,
41
+ user: n,
32
42
  avatarProps: e.popupAvatarProps,
33
43
  shortenLabelProps: e.popupShortenLabelProps || e.shortenLabelProps,
34
44
  renderFields: e.popupRenderFields,
@@ -38,25 +48,25 @@ function g(e) {
38
48
  showDid: e.popupShowDid || e.showDid
39
49
  }
40
50
  ) });
41
- return r === i.AvatarOnly ? /* @__PURE__ */ t(
42
- v,
51
+ return r === o.AvatarOnly ? /* @__PURE__ */ t(
52
+ A,
43
53
  {
44
54
  ...e,
45
- shouldShowHoverCard: n,
46
- renderCardContent: l,
47
- user: a
55
+ shouldShowHoverCard: u,
56
+ renderCardContent: s,
57
+ user: n
48
58
  }
49
- ) : /* @__PURE__ */ t(A, { containerRef: m, cardType: r, sx: e.sx, children: /* @__PURE__ */ t(
50
- f,
59
+ ) : /* @__PURE__ */ t(S, { containerRef: c, cardType: r, sx: e.sx, children: /* @__PURE__ */ t(
60
+ m,
51
61
  {
52
62
  ...e,
53
- shouldShowHoverCard: n,
54
- renderCardContent: l,
55
- user: a
63
+ shouldShowHoverCard: u,
64
+ renderCardContent: s,
65
+ user: n
56
66
  }
57
67
  ) });
58
68
  }
59
69
  export {
60
70
  L as createNameOnlyAvatar,
61
- g as default
71
+ B as default
62
72
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/ux",
3
- "version": "3.0.32",
3
+ "version": "3.0.34",
4
4
  "description": "Common used react components for arcblock products",
5
5
  "keywords": [
6
6
  "react",
@@ -60,16 +60,16 @@
60
60
  "react": "^19.0.0",
61
61
  "react-router-dom": "^6.22.3"
62
62
  },
63
- "gitHead": "c81f41bdecc9ffb41a2f8158fb7a557fae62666b",
63
+ "gitHead": "e2822eb021fb9aebfcf80f71dd4da4ca76b2372b",
64
64
  "dependencies": {
65
- "@arcblock/bridge": "3.0.32",
65
+ "@arcblock/bridge": "3.0.34",
66
66
  "@arcblock/did": "^1.21.0",
67
67
  "@arcblock/did-motif": "^1.1.14",
68
- "@arcblock/icons": "3.0.32",
69
- "@arcblock/nft-display": "3.0.32",
70
- "@arcblock/react-hooks": "3.0.32",
68
+ "@arcblock/icons": "3.0.34",
69
+ "@arcblock/nft-display": "3.0.34",
70
+ "@arcblock/react-hooks": "3.0.34",
71
71
  "@blocklet/js-sdk": "^1.16.46",
72
- "@blocklet/theme": "3.0.32",
72
+ "@blocklet/theme": "3.0.34",
73
73
  "@fontsource/roboto": "~5.1.1",
74
74
  "@fontsource/ubuntu-mono": "^5.2.6",
75
75
  "@iconify-icons/logos": "^1.2.36",
@@ -23,6 +23,7 @@ import { DID_PREFIX } from '../Util/constant';
23
23
  * shape?: '' | 'rectangle' | 'square' | 'hexagon' | 'circle';
24
24
  * blockiesPadding?: true | false;
25
25
  * responsive?: false | true;
26
+ * useProxyFallback?: boolean;
26
27
  * } & Omit<import('../Img').ImgProps, 'size' | 'src'>} AvatarProps
27
28
  */
28
29
 
@@ -84,6 +85,7 @@ function Avatar({ ...rawProps }) {
84
85
  {...rest}
85
86
  // HACK: 这个 className 是传递给子元素的,所以下面的 sx 需要通过子元素选择器来写样式
86
87
  className={`avatar-img--${variant} ${rest?.className || ''}`}
88
+ useProxyFallback
87
89
  sx={{
88
90
  '& .avatar-img--rounded': {
89
91
  borderRadius: '4px',
@@ -159,6 +161,7 @@ Avatar.propTypes = {
159
161
  shape: PropTypes.oneOf(['', 'rectangle', 'square', 'hexagon', 'circle']),
160
162
  blockiesPadding: PropTypes.bool,
161
163
  responsive: PropTypes.bool,
164
+ useProxyFallback: PropTypes.bool,
162
165
  };
163
166
 
164
167
  /**
@@ -1,28 +1,5 @@
1
1
  import { Box, Typography } from '@mui/material';
2
2
  import Img from '../../Img';
3
- import { BLOCKLET_SERVICE_PATH_PREFIX } from '../../Util/constant';
4
-
5
- const getProxyImageUrl = (url: string) => {
6
- if (!url) return '';
7
-
8
- try {
9
- // 检查是否是一个有效的URL
10
- const urlObj = new URL(url);
11
-
12
- // 检查协议是否是https
13
- if (urlObj.protocol !== 'https:') {
14
- console.warn('Image URL must use HTTPS protocol:', url);
15
- return '';
16
- }
17
-
18
- // 返回代理URL
19
- return `${BLOCKLET_SERVICE_PATH_PREFIX}/proxy?url=${url}`;
20
- } catch (error) {
21
- // URL construction failed, indicating invalid URL format
22
- console.warn('Invalid image URL format:', url, error);
23
- return '';
24
- }
25
- };
26
3
 
27
4
  export type RequestAppInfo = {
28
5
  appLogo: string;
@@ -59,13 +36,7 @@ export default function AuthAppsInfo({
59
36
  justifyContent: 'center',
60
37
  }}>
61
38
  {/* FIXME: @zhanghan 增加 hover 的效果 */}
62
- <Img
63
- src={currentAppInfo.appLogo}
64
- alt="Server"
65
- width={48}
66
- height={48}
67
- fallback={getProxyImageUrl(currentAppInfo.appLogo)}
68
- />
39
+ <Img src={currentAppInfo.appLogo} alt="Server" width={48} height={48} useProxyFallback />
69
40
 
70
41
  <Box
71
42
  sx={{
@@ -86,13 +57,7 @@ export default function AuthAppsInfo({
86
57
  </Box>
87
58
 
88
59
  {/* FIXME: @zhanghan 增加 hover 的效果 */}
89
- <Img
90
- src={requestAppInfo.appLogo}
91
- alt={requestAppInfo.appName}
92
- width={48}
93
- height={48}
94
- fallback={getProxyImageUrl(requestAppInfo.appLogo)}
95
- />
60
+ <Img src={requestAppInfo.appLogo} alt={requestAppInfo.appName} width={48} height={48} useProxyFallback />
96
61
  </Box>
97
62
 
98
63
  <Typography
package/src/Img/index.jsx CHANGED
@@ -7,6 +7,29 @@ import { Icon } from '@iconify/react';
7
7
  import { Box } from '@mui/material';
8
8
  import noop from 'lodash/noop';
9
9
  import { mergeSx } from '../Util/style';
10
+ import { BLOCKLET_SERVICE_PATH_PREFIX } from '../Util/constant';
11
+
12
+ const getProxyImageUrl = (url) => {
13
+ if (!url) return '';
14
+
15
+ try {
16
+ // 检查是否是一个有效的URL
17
+ const urlObj = new URL(url);
18
+
19
+ // 检查协议是否是https
20
+ if (urlObj.protocol !== 'https:') {
21
+ console.warn('Image URL must use HTTPS protocol:', url);
22
+ return '';
23
+ }
24
+
25
+ // 返回代理URL
26
+ return `${BLOCKLET_SERVICE_PATH_PREFIX}/proxy?url=${encodeURIComponent(url)}`;
27
+ } catch (error) {
28
+ // URL construction failed, indicating invalid URL format
29
+ console.warn('Invalid image URL format:', url, error);
30
+ return '';
31
+ }
32
+ };
10
33
 
11
34
  /**
12
35
  * @typedef {Object} ImgExProps
@@ -22,6 +45,7 @@ import { mergeSx } from '../Util/style';
22
45
  * @property {boolean} [lazy=true]
23
46
  * @property {string} [placeholder]
24
47
  * @property {string} [fallback]
48
+ * @property {boolean} [useProxyFallback=false] - 是否使用代理 fallback, 用于解决 CSP 的问题
25
49
  * @property {string} [className='']
26
50
  * @property {function} [onError=() => {}]
27
51
  * @property {function} [onSuccess=() => {}]
@@ -52,6 +76,7 @@ function Img({
52
76
  size = 'cover',
53
77
  position = 'top center',
54
78
  src,
79
+ useProxyFallback = false, // 是否使用代理 fallback, 用于解决 CSP 的问题
55
80
  placeholder = null,
56
81
  fallback = null,
57
82
  style = null,
@@ -64,6 +89,17 @@ function Img({
64
89
  const [ref, inView] = lazy ? useInView({ threshold: 0, triggerOnce: true }) : [null, true];
65
90
 
66
91
  const [imgState, setImgState] = useState('init');
92
+ const [fallbackError, setFallbackError] = useState(false);
93
+
94
+ const fallbackSrc = useMemo(() => {
95
+ if (fallback) {
96
+ return fallback;
97
+ }
98
+ if (useProxyFallback && src) {
99
+ return getProxyImageUrl(src);
100
+ }
101
+ return fallback;
102
+ }, [src, useProxyFallback, fallback]);
67
103
 
68
104
  const actualSrc = useMemo(() => {
69
105
  switch (imgState) {
@@ -71,13 +107,13 @@ function Img({
71
107
  case 'loading':
72
108
  return placeholder;
73
109
  case 'error':
74
- return fallback;
110
+ return fallbackError ? null : fallbackSrc;
75
111
  case 'loaded':
76
112
  return src;
77
113
  default:
78
114
  return null;
79
115
  }
80
- }, [placeholder, fallback, src, imgState]);
116
+ }, [placeholder, fallbackSrc, src, imgState, fallbackError]);
81
117
 
82
118
  const actualRatio = width && height ? (100 * height) / width : ratio * 100;
83
119
 
@@ -106,13 +142,13 @@ function Img({
106
142
  const img = new Image();
107
143
  img.src = src;
108
144
  setImgState('loading');
145
+ setFallbackError(false);
109
146
  img.onload = () => {
110
147
  setImgState('loaded');
111
148
  onSuccess();
112
149
  };
113
- img.onerror = (err) => {
150
+ img.onerror = () => {
114
151
  setImgState('error');
115
- onError(err);
116
152
  };
117
153
  }
118
154
 
@@ -121,6 +157,24 @@ function Img({
121
157
  // eslint-disable-next-line react-hooks/exhaustive-deps
122
158
  }, [inView]);
123
159
 
160
+ // 处理 fallback 加载
161
+ useEffect(() => {
162
+ if (imgState === 'error' && fallbackSrc && !fallbackError) {
163
+ const fallbackImg = new Image();
164
+ fallbackImg.src = fallbackSrc;
165
+ fallbackImg.onload = () => {
166
+ // fallback 加载成功,保持在 error 状态但显示 fallback
167
+ };
168
+ fallbackImg.onerror = (err) => {
169
+ // fallback 也加载失败
170
+ setFallbackError(true);
171
+ if (imgState === 'error') {
172
+ onError(err);
173
+ }
174
+ };
175
+ }
176
+ }, [imgState, fallbackSrc, fallbackError, onError]);
177
+
124
178
  return (
125
179
  // paddingTop 要求元素本身的宽度为 100%,所以只能加一个外层元素去限制宽度
126
180
  <Box
@@ -166,13 +220,13 @@ function Img({
166
220
  rest.sx
167
221
  )}>
168
222
  <div className={`image ${className} ${classes.root}`} style={mergedStyle}>
169
- {!fallback && imgState === 'error' && (
170
- <div className="image--state" title="loading image">
223
+ {(fallbackError || (!fallbackSrc && imgState === 'error')) && (
224
+ <div className="image--state" title="Image load error">
171
225
  <Icon icon={WarningRoundedIcon} className="image--icon" />
172
226
  </div>
173
227
  )}
174
228
  {!placeholder && imgState === 'loading' && (
175
- <div className="image--state" title="Image load error">
229
+ <div className="image--state" title="loading image">
176
230
  <Icon icon={ImageRoundedIcon} className="image--icon" />
177
231
  </div>
178
232
  )}
@@ -198,6 +252,7 @@ Img.propTypes = {
198
252
  className: PropTypes.string,
199
253
  onError: PropTypes.func,
200
254
  onSuccess: PropTypes.func,
255
+ useProxyFallback: PropTypes.bool,
201
256
  };
202
257
 
203
258
  export default Img;
@@ -20,7 +20,7 @@ export default function LoadingMask({
20
20
  const finialSize = size;
21
21
  const finialRadius = borderRadius ?? finialSize / 8;
22
22
  const finialBorderWidth = borderWidth ?? finialSize / 32;
23
- const finialPadding = padding ?? finialSize * 0.25 - finialBorderWidth;
23
+ const finialPadding = padding ?? finialSize * 0.35 - finialBorderWidth;
24
24
  const finialDuration = duration ?? 2.5;
25
25
  return (
26
26
  <Box
@@ -89,6 +89,17 @@ export default function LoadingMask({
89
89
  alignItems: 'center',
90
90
  width: finialSize - finialPadding - finialBorderWidth,
91
91
  height: finialSize - finialPadding - finialBorderWidth,
92
+
93
+ animation: 'wait-bounce 1.6s infinite',
94
+ transformOrigin: 'center bottom',
95
+ '@keyframes wait-bounce': {
96
+ '0%': { transform: 'scale(1) translateY(0)' },
97
+ '20%': { transform: 'scale(1.03) translateY(-4px)' },
98
+ '40%': { transform: 'scale(0.98) translateY(2px)' },
99
+ '60%': { transform: 'scale(1.01) translateY(-2px)' },
100
+ '80%': { transform: 'scale(0.99) translateY(1px)' },
101
+ '100%': { transform: 'scale(1) translateY(0)' },
102
+ },
92
103
  }}>
93
104
  {children}
94
105
  </Box>
@@ -51,6 +51,7 @@ export const renderAvatar = (
51
51
  size={avatarSize}
52
52
  did={user.did}
53
53
  variant="circle"
54
+ useProxyFallback
54
55
  style={{
55
56
  cursor: shouldShowHoverCard || onAvatarClick ? 'pointer' : 'default',
56
57
  }}
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, useState } from 'react';
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
2
  import type { User } from './types';
3
3
  import { UserCardProps, CardType } from './types';
4
4
  import AvatarOnlyCard from './Cards/avatar-only';
@@ -55,9 +55,26 @@ function UserCard(props: UserCardProps) {
55
55
  };
56
56
  }, [props.did, props.user]);
57
57
 
58
+ // 补全 avatar 的 imageFilter 信息,以提高性能
59
+ const userInfo = useMemo(() => {
60
+ if (!user) {
61
+ return null;
62
+ }
63
+
64
+ const { avatar = '', ...rest } = user;
65
+ const avatarParam = avatar.split('?')[1];
66
+ if (avatar && !avatarParam) {
67
+ return {
68
+ ...rest,
69
+ avatar: `${avatar}?imageFilter=resize&w=48&h=48`,
70
+ };
71
+ }
72
+ return user;
73
+ }, [user]);
74
+
58
75
  // 如果不存在,则使用 did 渲染头像
59
- if (!user) {
60
- return <Avatar did={props.did} size={props.avatarSize} {...props.avatarProps} />;
76
+ if (!userInfo) {
77
+ return <Avatar did={props.did} size={props.avatarSize} useProxyFallback {...props.avatarProps} />;
61
78
  }
62
79
 
63
80
  // user 存在,则使用 user 渲染头像
@@ -68,7 +85,7 @@ function UserCard(props: UserCardProps) {
68
85
  <DetailedCard
69
86
  {...props}
70
87
  shouldShowHoverCard={false}
71
- user={user!}
88
+ user={userInfo!}
72
89
  avatarProps={props.popupAvatarProps}
73
90
  shortenLabelProps={props.popupShortenLabelProps || props.shortenLabelProps}
74
91
  renderFields={props.popupRenderFields}
@@ -88,7 +105,7 @@ function UserCard(props: UserCardProps) {
88
105
  {...props}
89
106
  shouldShowHoverCard={shouldShowHoverCard}
90
107
  renderCardContent={renderCardContent}
91
- user={user!}
108
+ user={userInfo!}
92
109
  />
93
110
  );
94
111
  }
@@ -100,7 +117,7 @@ function UserCard(props: UserCardProps) {
100
117
  {...props}
101
118
  shouldShowHoverCard={shouldShowHoverCard}
102
119
  renderCardContent={renderCardContent}
103
- user={user!}
120
+ user={userInfo!}
104
121
  />
105
122
  </CardContainer>
106
123
  );