@blocklet/ui-react 3.0.20 → 3.0.22

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.
@@ -0,0 +1,11 @@
1
+ export interface NftInfo {
2
+ address: string;
3
+ data: Record<string, any>;
4
+ display: Record<string, any>;
5
+ issuer: string;
6
+ }
7
+ export default function NftPreview({ visible, onClose, nft, }: {
8
+ visible: boolean;
9
+ onClose: () => void;
10
+ nft: NftInfo | null;
11
+ }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,60 @@
1
+ import { jsx as t } from "react/jsx-runtime";
2
+ import n from "@mui/material/Modal";
3
+ import r from "@emotion/styled";
4
+ import a, { getNFTData as s } from "@arcblock/ux/lib/NFTDisplay";
5
+ function f({
6
+ visible: e,
7
+ onClose: o,
8
+ nft: i
9
+ }) {
10
+ return !e || !i ? null : /* @__PURE__ */ t(d, { children: /* @__PURE__ */ t(n, { open: e, onClose: o, children: /* @__PURE__ */ t(h, { onClick: o, children: /* @__PURE__ */ t(a, { data: s(i), address: i.address, inset: !0 }) }) }) });
11
+ }
12
+ const d = r.div`
13
+ position: relative;
14
+ width: 100%;
15
+ height: 100%;
16
+ cursor: pointer;
17
+
18
+ &:hover {
19
+ .mask {
20
+ opacity: 1;
21
+ }
22
+ }
23
+
24
+ .mask {
25
+ position: absolute;
26
+ top: 0;
27
+ left: 0;
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ width: 100%;
32
+ height: 100%;
33
+ background: rgba(0, 0, 0, 0.6);
34
+ opacity: 0;
35
+ transition: opacity 0.2s;
36
+ }
37
+ `, h = r.div`
38
+ display: flex;
39
+ justify-content: center;
40
+ align-items: center;
41
+ height: 100vh;
42
+ width: 100vw;
43
+
44
+ img,
45
+ object {
46
+ max-width: 90vw;
47
+ max-height: 75vh;
48
+ }
49
+
50
+ .nft-display--inset {
51
+ > .MuiBox-root,
52
+ .nft-display__loading {
53
+ width: 75vmin;
54
+ height: 75vmin;
55
+ }
56
+ }
57
+ `;
58
+ export {
59
+ f as default
60
+ };
@@ -1,35 +1,84 @@
1
- import { jsxs as c, jsx as e, Fragment as h } from "react/jsx-runtime";
2
- import { Box as a, Skeleton as f, Typography as u, Pagination as y } from "@mui/material";
3
- import { useMemoizedFn as w, useReactive as k, useRequest as C, useCreation as b } from "ahooks";
4
- import F from "axios";
5
- import z from "@arcblock/ux/lib/NFTDisplay";
6
- import N from "@arcblock/ux/lib/Empty";
7
- import { WELLKNOWN_SERVICE_PATH_PREFIX as P } from "@abtnode/constant";
8
- import { translate as R } from "@arcblock/ux/lib/Locale/util";
9
- import { useLocaleContext as j } from "@arcblock/ux/lib/Locale/context";
10
- import { translations as E } from "../libs/locales.js";
11
- function q({ user: l }) {
12
- const { locale: g } = j(), d = w((s, r = {}) => R(E, s, g, "en", r)), t = k({
1
+ import { jsxs as l, jsx as o, Fragment as R } from "react/jsx-runtime";
2
+ import { useState as y } from "react";
3
+ import { Box as s, Skeleton as w, Typography as F, alpha as C, Stack as j, IconButton as f, Pagination as T } from "@mui/material";
4
+ import { useMemoizedFn as L, useReactive as _, useRequest as A, useCreation as D } from "ahooks";
5
+ import H from "axios";
6
+ import { Icon as h } from "@iconify/react";
7
+ import W, { getNFTData as O } from "@arcblock/ux/lib/NFTDisplay";
8
+ import U from "@arcblock/ux/lib/Empty";
9
+ import { WELLKNOWN_SERVICE_PATH_PREFIX as B } from "@abtnode/constant";
10
+ import { translate as M } from "@arcblock/ux/lib/Locale/util";
11
+ import { useLocaleContext as V } from "@arcblock/ux/lib/Locale/context";
12
+ import { joinURL as v } from "ufo";
13
+ import X from "@arcblock/ux/lib/SocialShare";
14
+ import { translations as $ } from "../libs/locales.js";
15
+ import q from "./nft-preview.js";
16
+ const J = (a) => ({
17
+ position: "absolute",
18
+ top: 0,
19
+ left: 0,
20
+ right: 0,
21
+ bottom: 0,
22
+ backgroundColor: "rgba(0, 0, 0, 0.6)",
23
+ opacity: 0,
24
+ transition: "opacity 0.3s ease-in-out",
25
+ display: "flex",
26
+ alignItems: "center",
27
+ justifyContent: "center",
28
+ zIndex: 1,
29
+ "& .mask-item": {
30
+ backgroundColor: C(a.palette.background.paper, 0.9),
31
+ color: "text.primary",
32
+ "&:hover": {
33
+ backgroundColor: C(a.palette.background.paper, 1)
34
+ }
35
+ }
36
+ }), P = "https://main.abtnetwork.io/explorer";
37
+ function ce({ user: a }) {
38
+ const { locale: N } = V(), u = L((t, e = {}) => M($, t, N, "en", e)), [n, g] = y({
39
+ visible: !1,
40
+ nft: null
41
+ }), [p, x] = y({
42
+ anchorEl: null,
43
+ props: null
44
+ }), S = (t) => {
45
+ const e = v(P, "assets", t);
46
+ window.open(e, "_blank");
47
+ }, z = (t, e) => {
48
+ let i = "";
49
+ typeof e.data?.value == "string" ? i = JSON.parse(e.data?.value)?.domain : i = e.data?.value?.domain, x({
50
+ anchorEl: t,
51
+ props: {
52
+ title: i ? `Hey, I just won an NFT at the ${i} event!` : "Hey, I just won an NFT!",
53
+ url: v(P, "assets", e.address)
54
+ }
55
+ });
56
+ }, E = (t) => {
57
+ g({
58
+ visible: !0,
59
+ nft: t
60
+ });
61
+ }, r = _({
13
62
  page: 1,
14
63
  size: 20
15
- }), p = C(
16
- async (s = t) => (await F.get(`${P}/ocap/listAssets`, {
64
+ }), k = A(
65
+ async (t = r) => (await H.get(`${B}/ocap/listAssets`, {
17
66
  params: {
18
- ownerAddress: l.did,
19
- ...s
67
+ ownerAddress: a.did,
68
+ ...t
20
69
  }
21
70
  })).data,
22
71
  {
23
- defaultParams: [t],
24
- refreshDeps: [l.did, t]
72
+ defaultParams: [r],
73
+ refreshDeps: [a.did, r]
25
74
  }
26
- ), { loading: n, data: o } = p, i = o?.page ?? { cursor: 0, next: !1, total: 0 }, m = (s, r) => {
27
- t.page = r, p.run(t);
28
- }, x = b(() => {
29
- if (n) {
30
- const s = ["skeleton-1", "skeleton-2", "skeleton-3", "skeleton-4", "skeleton-5"].map((r) => /* @__PURE__ */ e(f, { variant: "rectangular", width: "15%", height: 166, sx: { borderRadius: 1, flexShrink: 0 } }, r));
31
- return /* @__PURE__ */ c(
32
- a,
75
+ ), { loading: c, data: d } = k, m = d?.page ?? { cursor: 0, next: !1, total: 0 }, b = (t, e) => {
76
+ r.page = e, k.run(r);
77
+ }, I = D(() => {
78
+ if (c) {
79
+ const t = ["skeleton-1", "skeleton-2", "skeleton-3", "skeleton-4", "skeleton-5"].map((e) => /* @__PURE__ */ o(w, { variant: "rectangular", width: "15%", height: 166, sx: { borderRadius: 1, flexShrink: 0 } }, e));
80
+ return /* @__PURE__ */ l(
81
+ s,
33
82
  {
34
83
  sx: {
35
84
  display: "flex",
@@ -37,9 +86,9 @@ function q({ user: l }) {
37
86
  gap: 2
38
87
  },
39
88
  children: [
40
- /* @__PURE__ */ e(f, { width: "20%" }),
41
- /* @__PURE__ */ e(
42
- a,
89
+ /* @__PURE__ */ o(w, { width: "20%" }),
90
+ /* @__PURE__ */ o(
91
+ s,
43
92
  {
44
93
  sx: {
45
94
  display: "flex",
@@ -47,28 +96,28 @@ function q({ user: l }) {
47
96
  gap: 2,
48
97
  flexWrap: "nowrap"
49
98
  },
50
- children: s
99
+ children: t
51
100
  }
52
101
  )
53
102
  ]
54
103
  }
55
104
  );
56
105
  }
57
- return /* @__PURE__ */ c(h, { children: [
58
- /* @__PURE__ */ e(
59
- u,
106
+ return /* @__PURE__ */ l(R, { children: [
107
+ /* @__PURE__ */ o(
108
+ F,
60
109
  {
61
110
  sx: {
62
111
  color: "grey.A700",
63
112
  fontWeight: 600,
64
113
  mb: 2.5
65
114
  },
66
- children: d("common.nft")
115
+ children: u("common.nft")
67
116
  }
68
117
  ),
69
- o?.assets?.length === 0 && !n && /* @__PURE__ */ e(a, { sx: { display: "flex", justifyContent: "center", alignItems: "center", width: "100%", height: "100%" }, children: /* @__PURE__ */ e(N, { children: d("common.noNFT") }) }),
70
- /* @__PURE__ */ e(
71
- a,
118
+ d?.assets?.length === 0 && !c && /* @__PURE__ */ o(s, { sx: { display: "flex", justifyContent: "center", alignItems: "center", width: "100%", height: "100%" }, children: /* @__PURE__ */ o(U, { children: u("common.noNFT") }) }),
119
+ /* @__PURE__ */ o(
120
+ s,
72
121
  {
73
122
  className: "nft-list-wrapper",
74
123
  sx: {
@@ -82,50 +131,114 @@ function q({ user: l }) {
82
131
  },
83
132
  gap: 2.5
84
133
  },
85
- children: o?.assets?.map((s) => /* @__PURE__ */ e(
86
- a,
134
+ children: d?.assets?.map((t) => /* @__PURE__ */ l(
135
+ s,
87
136
  {
88
137
  sx: {
89
138
  flexShrink: 0,
90
139
  width: { xs: 120, sm: 120, md: 120, lg: 166 },
91
- height: { xs: 120, sm: 120, md: 120, lg: 166 }
140
+ height: { xs: 120, sm: 120, md: 120, lg: 166 },
141
+ position: "relative",
142
+ borderRadius: 1,
143
+ overflow: "hidden",
144
+ cursor: "pointer",
145
+ "&:hover .mask": {
146
+ opacity: 1
147
+ }
92
148
  },
93
- children: /* @__PURE__ */ e(
94
- z,
95
- {
96
- data: s.display,
97
- address: s.address,
98
- inset: !0,
99
- imageFilter: {
100
- imageFilter: "resize",
101
- w: "500",
102
- f: "webp"
149
+ children: [
150
+ /* @__PURE__ */ o(
151
+ W,
152
+ {
153
+ data: O(t),
154
+ address: t.address,
155
+ inset: !0,
156
+ imageFilter: {
157
+ imageFilter: "resize",
158
+ w: "500",
159
+ f: "webp"
160
+ }
103
161
  }
104
- }
105
- )
162
+ ),
163
+ /* @__PURE__ */ o(s, { className: "mask", sx: J, children: /* @__PURE__ */ l(j, { direction: "row", spacing: 1, children: [
164
+ /* @__PURE__ */ o(
165
+ f,
166
+ {
167
+ size: "small",
168
+ className: "mask-item",
169
+ onClick: (e) => {
170
+ e.stopPropagation(), S(t.address);
171
+ },
172
+ children: /* @__PURE__ */ o(h, { icon: "tabler:link", fontSize: 18 })
173
+ }
174
+ ),
175
+ /* @__PURE__ */ o(
176
+ f,
177
+ {
178
+ size: "small",
179
+ onClick: (e) => {
180
+ e.stopPropagation(), z(e.currentTarget, t);
181
+ },
182
+ className: "mask-item",
183
+ children: /* @__PURE__ */ o(h, { icon: "tabler:share-2", fontSize: 18 })
184
+ }
185
+ ),
186
+ /* @__PURE__ */ o(
187
+ f,
188
+ {
189
+ size: "small",
190
+ onClick: (e) => {
191
+ e.stopPropagation(), E(t);
192
+ },
193
+ className: "mask-item",
194
+ children: /* @__PURE__ */ o(h, { icon: "tabler:eye", fontSize: 18 })
195
+ }
196
+ )
197
+ ] }) })
198
+ ]
106
199
  },
107
- s.address
200
+ t.address
108
201
  ))
109
202
  }
110
203
  ),
111
- i.next || t.page > 1 ? /* @__PURE__ */ e(
112
- y,
204
+ m.next || r.page > 1 ? /* @__PURE__ */ o(
205
+ T,
113
206
  {
114
207
  sx: {
115
208
  display: "flex",
116
209
  justifyContent: "center",
117
210
  mt: 2
118
211
  },
119
- page: t.page,
120
- onChange: m,
121
- count: Math.ceil(i.total / t.size),
212
+ page: r.page,
213
+ onChange: b,
214
+ count: Math.ceil(m.total / r.size),
122
215
  size: "small"
123
216
  }
124
- ) : null
217
+ ) : null,
218
+ /* @__PURE__ */ o(
219
+ q,
220
+ {
221
+ visible: n.visible && !!n.nft,
222
+ nft: n.nft,
223
+ onClose: () => {
224
+ g({ visible: !1, nft: null });
225
+ }
226
+ }
227
+ ),
228
+ /* @__PURE__ */ o(
229
+ X,
230
+ {
231
+ anchorEl: p.anchorEl,
232
+ onClose: () => {
233
+ x({ anchorEl: null, props: null });
234
+ },
235
+ sharedProps: p.props || { title: "", url: "" }
236
+ }
237
+ )
125
238
  ] });
126
- }, [n, i, t.page, t.size, m]);
127
- return /* @__PURE__ */ e(a, { sx: { border: "1px solid", borderColor: "divider", borderRadius: 1, p: 2, mb: 5 }, children: x });
239
+ }, [c, m, r.page, r.size, b, p, n]);
240
+ return /* @__PURE__ */ o(s, { sx: { border: "1px solid", borderColor: "divider", borderRadius: 1, p: 2, mb: 5 }, children: I });
128
241
  }
129
242
  export {
130
- q as default
243
+ ce as default
131
244
  };
@@ -1,23 +1,24 @@
1
- import { jsx as t, jsxs as m } from "react/jsx-runtime";
2
- import { useState as v, useMemo as b } from "react";
3
- import { styled as L, Box as d, Typography as w, IconButton as C, Button as I, useTheme as k, FormControl as D, TextField as F } from "@mui/material";
4
- import { Remove as S, Add as B } from "@mui/icons-material";
5
- import { translate as E } from "@arcblock/ux/lib/Locale/util";
6
- import { useMemoizedFn as T } from "ahooks";
7
- import { useLocaleContext as U } from "@arcblock/ux/lib/Locale/context";
8
- import j from "@arcblock/icons/lib/Link";
9
- import { mergeSx as z } from "@arcblock/ux/lib/Util/style";
10
- import { withoutProtocol as M } from "ufo";
11
- import { isValidUrl as f } from "./utils.js";
12
- import { translations as P } from "../../libs/locales.js";
13
- import { inputFieldStyle as R, commonInputStyle as W } from "../editable-field.js";
14
- function _(e) {
15
- return M(e);
1
+ import { jsx as t, jsxs as p } from "react/jsx-runtime";
2
+ import { useState as L, useMemo as w } from "react";
3
+ import { styled as C, Box as d, Typography as x, IconButton as I, Button as k, useTheme as S, FormControl as D, TextField as F } from "@mui/material";
4
+ import { Remove as B, Add as T } from "@mui/icons-material";
5
+ import { translate as U } from "@arcblock/ux/lib/Locale/util";
6
+ import { useMemoizedFn as _ } from "ahooks";
7
+ import { useLocaleContext as z } from "@arcblock/ux/lib/Locale/context";
8
+ import E from "@arcblock/icons/lib/Link";
9
+ import { mergeSx as M } from "@arcblock/ux/lib/Util/style";
10
+ import { withoutProtocol as j } from "ufo";
11
+ import { isValidUrl as g } from "./utils.js";
12
+ import { translations as A } from "../../libs/locales.js";
13
+ import { inputFieldStyle as P, commonInputStyle as R } from "../editable-field.js";
14
+ const h = 10;
15
+ function W(e) {
16
+ return j(e);
16
17
  }
17
- function A({
18
+ function N({
18
19
  value: e,
19
- onChange: n,
20
- errorMsg: i
20
+ onChange: i,
21
+ errorMsg: l
21
22
  }) {
22
23
  return /* @__PURE__ */ t(D, { fullWidth: !0, children: /* @__PURE__ */ t(
23
24
  F,
@@ -26,31 +27,31 @@ function A({
26
27
  value: e,
27
28
  onChange: (a) => {
28
29
  const c = a.target.value;
29
- n(c);
30
+ i(c);
30
31
  },
31
32
  fullWidth: !0,
32
- error: !!i,
33
- helperText: i,
34
- sx: z(R, i ? {} : W)
33
+ error: !!l,
34
+ helperText: l,
35
+ sx: M(P, l ? {} : R)
35
36
  }
36
37
  ) });
37
38
  }
38
- function V({ links: e = [], onChange: n }) {
39
- const { locale: i } = U(), l = T((o, r = {}) => E(P, o, i, "en", r)), [a, c] = v([!1]), u = b(() => {
39
+ function V({ links: e = [], onChange: i }) {
40
+ const { locale: l } = z(), n = _((o, r = {}) => U(A, o, l, "en", r)), [a, c] = L([!1]), f = w(() => {
40
41
  const o = e[e.length - 1];
41
- return !f(o) || a.length > 0 && a[a.length - 1];
42
- }, [a, e]), x = () => {
43
- (e.length < 5 || !u) && n([...e, ""]);
44
- }, g = (o) => {
45
- const r = e.filter((p, h) => h !== o), s = a.filter((p, h) => h !== o);
46
- c(s), n(r);
47
- }, y = (o, r) => {
42
+ return !g(o) || a.length > 0 && a[a.length - 1];
43
+ }, [a, e]), y = () => {
44
+ (e.length < h || !f) && i([...e, ""]);
45
+ }, b = (o) => {
46
+ const r = e.filter((m, u) => u !== o), s = a.filter((m, u) => u !== o);
47
+ c(s), i(r);
48
+ }, v = (o, r) => {
48
49
  const s = [...e];
49
50
  s[o] = r;
50
- const p = [...a];
51
- p[o] = !!(r && !f(r)), c(p), n(s);
51
+ const m = [...a];
52
+ m[o] = !!(r && !g(r)), c(m), i(s);
52
53
  };
53
- return /* @__PURE__ */ m(
54
+ return /* @__PURE__ */ p(
54
55
  d,
55
56
  {
56
57
  sx: {
@@ -66,10 +67,10 @@ function V({ links: e = [], onChange: n }) {
66
67
  alignItems: "center",
67
68
  gap: 1
68
69
  },
69
- children: /* @__PURE__ */ t(w, { variant: "subtitle1", gutterBottom: !0, sx: { mb: 0, fontSize: "12px", color: "text.primary" }, children: l("profile.socialMedia") })
70
+ children: /* @__PURE__ */ t(x, { variant: "subtitle1", gutterBottom: !0, sx: { mb: 0, fontSize: "12px", color: "text.primary" }, children: n("profile.socialMedia") })
70
71
  }
71
72
  ),
72
- e.map((o, r) => /* @__PURE__ */ m(
73
+ e.map((o, r) => /* @__PURE__ */ p(
73
74
  d,
74
75
  {
75
76
  sx: {
@@ -79,25 +80,25 @@ function V({ links: e = [], onChange: n }) {
79
80
  },
80
81
  children: [
81
82
  /* @__PURE__ */ t(
82
- A,
83
+ N,
83
84
  {
84
85
  value: o,
85
- onChange: (s) => y(r, s),
86
- errorMsg: a[r] ? l("profile.invalidURL") : ""
86
+ onChange: (s) => v(r, s),
87
+ errorMsg: a[r] ? n("profile.invalidURL") : ""
87
88
  }
88
89
  ),
89
- /* @__PURE__ */ t(C, { onClick: () => g(r), children: /* @__PURE__ */ t(S, { sx: { color: "text.secondary" } }) })
90
+ /* @__PURE__ */ t(I, { onClick: () => b(r), children: /* @__PURE__ */ t(B, { sx: { color: "text.secondary" } }) })
90
91
  ]
91
92
  },
92
93
  r
93
94
  )),
94
- e.length < 5 && /* @__PURE__ */ m(
95
- I,
95
+ e.length < h ? /* @__PURE__ */ p(
96
+ k,
96
97
  {
97
98
  fullWidth: !0,
98
99
  variant: "outlined",
99
- disabled: u,
100
- onClick: x,
100
+ disabled: f,
101
+ onClick: y,
101
102
  size: "small",
102
103
  sx: {
103
104
  height: "40px",
@@ -113,26 +114,26 @@ function V({ links: e = [], onChange: n }) {
113
114
  }
114
115
  },
115
116
  children: [
116
- /* @__PURE__ */ t(B, {}),
117
+ /* @__PURE__ */ t(T, {}),
117
118
  " ",
118
- /* @__PURE__ */ t("span", { children: l("profile.addLink") })
119
+ /* @__PURE__ */ t("span", { children: n("profile.addLink") })
119
120
  ]
120
121
  }
121
- )
122
+ ) : /* @__PURE__ */ t(x, { variant: "subtitle1", gutterBottom: !0, sx: { mb: 0, fontSize: "12px", color: "text.secondary" }, children: n("profile.maxLinkCount", { count: h }) })
122
123
  ]
123
124
  }
124
125
  );
125
126
  }
126
- function $({ link: e }) {
127
- const i = k().palette.mode === "dark";
128
- return e ? /* @__PURE__ */ t(j, { width: 20, height: 20, style: { filter: i ? "brightness(0) saturate(100%) invert(1)" : "none" } }) : null;
127
+ function K({ link: e }) {
128
+ const l = S().palette.mode === "dark";
129
+ return e ? /* @__PURE__ */ t(E, { width: 20, height: 20, style: { filter: l ? "brightness(0) saturate(100%) invert(1)" : "none" } }) : null;
129
130
  }
130
- function oe({
131
+ function ne({
131
132
  editable: e = !1,
132
- links: n = [],
133
- onChange: i
133
+ links: i = [],
134
+ onChange: l
134
135
  }) {
135
- return e ? /* @__PURE__ */ t(V, { links: n, onChange: i }) : /* @__PURE__ */ t(
136
+ return e ? /* @__PURE__ */ t(V, { links: i, onChange: l }) : /* @__PURE__ */ t(
136
137
  d,
137
138
  {
138
139
  sx: {
@@ -141,7 +142,7 @@ function oe({
141
142
  flexDirection: "column",
142
143
  gap: 2
143
144
  },
144
- children: n.map((l) => /* @__PURE__ */ m(
145
+ children: i.map((n) => /* @__PURE__ */ p(
145
146
  d,
146
147
  {
147
148
  sx: {
@@ -152,16 +153,16 @@ function oe({
152
153
  width: "100%"
153
154
  },
154
155
  children: [
155
- /* @__PURE__ */ t($, { link: l }),
156
- /* @__PURE__ */ t(q, { children: /* @__PURE__ */ t(d, { component: "a", href: l, style: { textDecoration: "none" }, target: "_blank", rel: "noopener noreferrer", children: _(l) }) })
156
+ /* @__PURE__ */ t(K, { link: n }),
157
+ /* @__PURE__ */ t(O, { children: /* @__PURE__ */ t(d, { component: "a", href: n, style: { textDecoration: "none" }, target: "_blank", rel: "noopener noreferrer", children: W(n) }) })
157
158
  ]
158
159
  },
159
- l
160
+ n
160
161
  ))
161
162
  }
162
163
  );
163
164
  }
164
- const q = L("span")`
165
+ const O = C("span")`
165
166
  flex: 1;
166
167
  white-space: nowrap;
167
168
  overflow: hidden;
@@ -180,5 +181,5 @@ const q = L("span")`
180
181
  }
181
182
  `;
182
183
  export {
183
- oe as LinkPreviewInput
184
+ ne as LinkPreviewInput
184
185
  };
@@ -4,7 +4,7 @@ export declare const getTimezones: () => {
4
4
  label: string;
5
5
  value: string;
6
6
  }[];
7
- export declare const isValidUrl: (url: string) => boolean;
7
+ export declare const isValidUrl: (url: string) => any;
8
8
  /**
9
9
  * 根据 duration 类型,计算出date range
10
10
  * @param status
@@ -1,10 +1,12 @@
1
+ import g from "is-url";
2
+ import { withHttps as A } from "ufo";
1
3
  import n from "dayjs";
2
- import T from "dayjs/plugin/timezone";
3
- import g from "dayjs/plugin/utc";
4
- import { DurationEnum as s } from "../../../@types/index.js";
5
- n.extend(g);
6
- n.extend(T);
7
- const u = 3600, l = 1800, m = 600, d = 300, f = 60, p = 1, M = n.tz.guess(), A = [
4
+ import h from "dayjs/plugin/timezone";
5
+ import k from "dayjs/plugin/utc";
6
+ import { DurationEnum as i } from "../../../@types/index.js";
7
+ n.extend(k);
8
+ n.extend(h);
9
+ const u = 3600, l = 1800, m = 600, d = 300, f = 60, p = 1, v = n.tz.guess(), b = [
8
10
  "America/New_York",
9
11
  "America/Chicago",
10
12
  "America/Denver",
@@ -26,7 +28,7 @@ const u = 3600, l = 1800, m = 600, d = 300, f = 60, p = 1, M = n.tz.guess(), A =
26
28
  "America/Mexico_City",
27
29
  "Africa/Cairo",
28
30
  "UTC"
29
- ], h = () => {
31
+ ], C = () => {
30
32
  if (typeof Intl < "u" && Intl.supportedValuesOf)
31
33
  try {
32
34
  return Intl.supportedValuesOf("timeZone");
@@ -36,58 +38,58 @@ const u = 3600, l = 1800, m = 600, d = 300, f = 60, p = 1, M = n.tz.guess(), A =
36
38
  if (typeof Intl < "u" && Intl.DateTimeFormat)
37
39
  try {
38
40
  if (new Intl.DateTimeFormat("en", { timeZone: "UTC" }).resolvedOptions().timeZone)
39
- return A;
41
+ return b;
40
42
  } catch {
41
43
  console.warn("Intl.DateTimeFormat timezone support limited");
42
44
  }
43
- return A;
44
- }, I = () => h().map((e) => {
45
+ return b;
46
+ }, w = () => C().map((e) => {
45
47
  try {
46
- const t = n.tz(n(), e).utcOffset() / 60, a = Math.floor(t), i = t % 1 * 60;
47
- return { label: `GMT${a >= 0 ? "+" : ""}${a}:${i === 30 ? "30" : "00"}`, value: e };
48
+ const o = n.tz(n(), e).utcOffset() / 60, a = Math.floor(o), s = o % 1 * 60;
49
+ return { label: `GMT${a >= 0 ? "+" : ""}${a}:${s === 30 ? "30" : "00"}`, value: e };
48
50
  } catch {
49
51
  return console.warn(`Timezone ${e} not supported, skipping`), null;
50
52
  }
51
- }).filter((e) => e !== null).sort((e, t) => {
52
- const [a, i] = e.label.replace("GMT", "").split(":").map(Number), [c, b] = t.label.replace("GMT", "").split(":").map(Number), y = a * 60 + i;
53
- return c * 60 + b - y;
53
+ }).filter((e) => e !== null).sort((e, o) => {
54
+ const [a, s] = e.label.replace("GMT", "").split(":").map(Number), [c, y] = o.label.replace("GMT", "").split(":").map(Number), T = a * 60 + s;
55
+ return c * 60 + y - T;
54
56
  }).map((e) => ({
55
57
  label: `(${e.label}) ${e.value}`,
56
58
  value: e.value
57
- })), N = (o) => /^(https?:\/\/)?((([a-zA-Z\d]([a-zA-Z\d-]*[a-zA-Z\d])*)\.)+[a-zA-Z]{2,}|((\d{1,3}\.){3}\d{1,3}))(:\d+)?(\/[-a-zA-Z\d%_.~+]*)*(\?[;&a-zA-Z\d%_.~+=-]*)?(#[a-zA-Z\d_]*)?$/.test(o), _ = (o) => {
58
- let r = o?.dateRange?.map((t) => n(t)) ?? [];
59
+ })), z = (t) => g(A(t)), U = (t) => {
60
+ let r = t?.dateRange?.map((o) => n(o)) ?? [];
59
61
  const e = n();
60
- switch (o?.duration) {
61
- case s.ThirtyMinutes:
62
+ switch (t?.duration) {
63
+ case i.ThirtyMinutes:
62
64
  r = [e, e.add(30, "minutes")];
63
65
  break;
64
- case s.OneHour:
66
+ case i.OneHour:
65
67
  r = [e, e.add(1, "hour")];
66
68
  break;
67
- case s.FourHours:
69
+ case i.FourHours:
68
70
  r = [e, e.add(4, "hours")];
69
71
  break;
70
- case s.Today:
72
+ case i.Today:
71
73
  r = [e, e.endOf("day")];
72
74
  break;
73
- case s.ThisWeek:
75
+ case i.ThisWeek:
74
76
  r = [e, e.endOf("week")];
75
77
  break;
76
- case s.NoClear:
78
+ case i.NoClear:
77
79
  r = [e, e];
78
80
  break;
79
81
  }
80
- return r.map((t) => t.toDate());
81
- }, E = (o) => {
82
+ return r.map((o) => o.toDate());
83
+ }, B = (t) => {
82
84
  const r = n();
83
- return r.isAfter(n(o[0])) && r.isBefore(n(o[1]));
84
- }, Z = (o) => {
85
- const { duration: r, dateRange: e } = o ?? {};
86
- return !r || !e ? !1 : r === s.NoClear || n(e?.[0]).isSame(n(e?.[1]));
87
- }, v = (o) => {
88
- const r = n(), t = n(o).diff(r, "seconds"), a = (i) => i * 1e3;
89
- return t >= u ? a(u) : t >= l ? a(l) : t >= m ? a(m) : t >= d ? a(d) : t >= f ? a(f) : t >= p ? a(p) : 0;
90
- }, w = {
85
+ return r.isAfter(n(t[0])) && r.isBefore(n(t[1]));
86
+ }, D = (t) => {
87
+ const { duration: r, dateRange: e } = t ?? {};
88
+ return !r || !e ? !1 : r === i.NoClear || n(e?.[0]).isSame(n(e?.[1]));
89
+ }, R = (t) => {
90
+ const r = n(), o = n(t).diff(r, "seconds"), a = (s) => s * 1e3;
91
+ return o >= u ? a(u) : o >= l ? a(l) : o >= m ? a(m) : o >= d ? a(d) : o >= f ? a(f) : o >= p ? a(p) : 0;
92
+ }, x = {
91
93
  color: "text.primary",
92
94
  borderColor: "grey.100",
93
95
  backgroundColor: "background.default",
@@ -97,7 +99,7 @@ const u = 3600, l = 1800, m = 600, d = 300, f = 60, p = 1, M = n.tz.guess(), A =
97
99
  },
98
100
  py: 0.5,
99
101
  borderRadius: 1
100
- }, B = {
102
+ }, $ = {
101
103
  color: "primary.contrastText",
102
104
  borderColor: "primary.main",
103
105
  backgroundColor: "primary.main",
@@ -109,13 +111,13 @@ const u = 3600, l = 1800, m = 600, d = 300, f = 60, p = 1, M = n.tz.guess(), A =
109
111
  borderRadius: 1
110
112
  };
111
113
  export {
112
- M as currentTimezone,
113
- w as defaultButtonStyle,
114
- _ as getStatusDuration,
115
- v as getTimeRemaining,
116
- I as getTimezones,
117
- Z as isNotClear,
118
- N as isValidUrl,
119
- E as isWithinTimeRange,
120
- B as primaryButtonStyle
114
+ v as currentTimezone,
115
+ x as defaultButtonStyle,
116
+ U as getStatusDuration,
117
+ R as getTimeRemaining,
118
+ w as getTimezones,
119
+ D as isNotClear,
120
+ z as isValidUrl,
121
+ B as isWithinTimeRange,
122
+ $ as primaryButtonStyle
121
123
  };
@@ -158,6 +158,7 @@ export declare const translations: {
158
158
  postalCode: string;
159
159
  invalidPostalCode: string;
160
160
  };
161
+ maxLinkCount: string;
161
162
  };
162
163
  destroyMyself: {
163
164
  title: string;
@@ -330,6 +331,7 @@ export declare const translations: {
330
331
  postalCode: string;
331
332
  invalidPostalCode: string;
332
333
  };
334
+ maxLinkCount: string;
333
335
  };
334
336
  destroyMyself: {
335
337
  title: string;
@@ -157,7 +157,8 @@ const e = {
157
157
  detailedAddress: "详细地址",
158
158
  postalCode: "邮政编码",
159
159
  invalidPostalCode: "邮政编码格式不正确"
160
- }
160
+ },
161
+ maxLinkCount: "最多可添加 {count} 个社交链接"
161
162
  },
162
163
  destroyMyself: {
163
164
  title: "删除账户",
@@ -329,7 +330,8 @@ const e = {
329
330
  detailedAddress: "Detailed Address",
330
331
  postalCode: "Postal Code",
331
332
  invalidPostalCode: "Postal code is invalid"
332
- }
333
+ },
334
+ maxLinkCount: "Up to {count} social links can be added"
333
335
  },
334
336
  destroyMyself: {
335
337
  title: "Delete Account",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "3.0.20",
3
+ "version": "3.0.22",
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.45",
37
37
  "@abtnode/util": "^1.16.45",
38
- "@arcblock/bridge": "3.0.20",
39
- "@arcblock/icons": "3.0.20",
40
- "@arcblock/react-hooks": "3.0.20",
38
+ "@arcblock/bridge": "3.0.22",
39
+ "@arcblock/icons": "3.0.22",
40
+ "@arcblock/react-hooks": "3.0.22",
41
41
  "@arcblock/ws": "^1.20.15",
42
42
  "@blocklet/constant": "^1.16.45",
43
43
  "@blocklet/did-space-react": "^1.1.1",
@@ -90,5 +90,5 @@
90
90
  "jest": "^29.7.0",
91
91
  "unbuild": "^2.0.0"
92
92
  },
93
- "gitHead": "09d1c4320e150a72604e31f2320537641f8f4464"
93
+ "gitHead": "21018768a5b825c02861281925afb54efdf0aa4c"
94
94
  }
@@ -0,0 +1,84 @@
1
+ import Modal from '@mui/material/Modal';
2
+ import styled from '@emotion/styled';
3
+ import NFTDisplay, { getNFTData } from '@arcblock/ux/lib/NFTDisplay';
4
+
5
+ export interface NftInfo {
6
+ address: string;
7
+ data: Record<string, any>;
8
+ display: Record<string, any>;
9
+ issuer: string;
10
+ }
11
+
12
+ export default function NftPreview({
13
+ visible,
14
+ onClose,
15
+ nft,
16
+ }: {
17
+ visible: boolean;
18
+ onClose: () => void;
19
+ nft: NftInfo | null;
20
+ }) {
21
+ if (!visible || !nft) {
22
+ return null;
23
+ }
24
+
25
+ return (
26
+ <NftPreviewRoot>
27
+ {/* Preview Modal */}
28
+ <Modal open={visible} onClose={onClose}>
29
+ <ModalRoot onClick={onClose}>
30
+ <NFTDisplay data={getNFTData(nft)} address={nft.address} inset />
31
+ </ModalRoot>
32
+ </Modal>
33
+ </NftPreviewRoot>
34
+ );
35
+ }
36
+
37
+ const NftPreviewRoot = styled.div`
38
+ position: relative;
39
+ width: 100%;
40
+ height: 100%;
41
+ cursor: pointer;
42
+
43
+ &:hover {
44
+ .mask {
45
+ opacity: 1;
46
+ }
47
+ }
48
+
49
+ .mask {
50
+ position: absolute;
51
+ top: 0;
52
+ left: 0;
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ width: 100%;
57
+ height: 100%;
58
+ background: rgba(0, 0, 0, 0.6);
59
+ opacity: 0;
60
+ transition: opacity 0.2s;
61
+ }
62
+ `;
63
+
64
+ const ModalRoot = styled.div`
65
+ display: flex;
66
+ justify-content: center;
67
+ align-items: center;
68
+ height: 100vh;
69
+ width: 100vw;
70
+
71
+ img,
72
+ object {
73
+ max-width: 90vw;
74
+ max-height: 75vh;
75
+ }
76
+
77
+ .nft-display--inset {
78
+ > .MuiBox-root,
79
+ .nft-display__loading {
80
+ width: 75vmin;
81
+ height: 75vmin;
82
+ }
83
+ }
84
+ `;
@@ -1,21 +1,49 @@
1
- import { Box, Pagination, Skeleton, Typography } from '@mui/material';
1
+ import { useState } from 'react';
2
+ import { Box, Pagination, Skeleton, Typography, IconButton, Stack, alpha } from '@mui/material';
2
3
  import { useCreation, useMemoizedFn, useReactive, useRequest } from 'ahooks';
3
4
  import axios from 'axios';
4
- import NFTDisplay from '@arcblock/ux/lib/NFTDisplay';
5
+ import { Icon } from '@iconify/react';
6
+ import NFTDisplay, { getNFTData } from '@arcblock/ux/lib/NFTDisplay';
5
7
  import Empty from '@arcblock/ux/lib/Empty';
6
8
  import { WELLKNOWN_SERVICE_PATH_PREFIX } from '@abtnode/constant';
7
9
  import { translate } from '@arcblock/ux/lib/Locale/util';
8
10
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
9
- import { translations } from '../libs/locales';
11
+ import { joinURL } from 'ufo';
12
+ import SocialShare from '@arcblock/ux/lib/SocialShare';
10
13
  import { User } from '../../@types';
14
+ import { translations } from '../libs/locales';
15
+ import NftPreview, { NftInfo } from './nft-preview';
11
16
 
12
- interface NftInfo {
13
- address: string;
14
- data: Record<string, any>;
15
- display: Record<string, any>;
16
- issuer: string;
17
+ // 直接定义 SharedProps 类型
18
+ interface SharedProps {
19
+ title: string;
20
+ url: string;
17
21
  }
18
22
 
23
+ const maskStyle = (theme: any) => ({
24
+ position: 'absolute',
25
+ top: 0,
26
+ left: 0,
27
+ right: 0,
28
+ bottom: 0,
29
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
30
+ opacity: 0,
31
+ transition: 'opacity 0.3s ease-in-out',
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ zIndex: 1,
36
+ '& .mask-item': {
37
+ backgroundColor: alpha(theme.palette.background.paper, 0.9),
38
+ color: 'text.primary',
39
+ '&:hover': {
40
+ backgroundColor: alpha(theme.palette.background.paper, 1),
41
+ },
42
+ },
43
+ });
44
+
45
+ const CHAIN_EXPLORER_URL = 'https://main.abtnetwork.io/explorer';
46
+
19
47
  interface ResponseData {
20
48
  assets: NftInfo[];
21
49
  code: string;
@@ -32,6 +60,45 @@ export default function Nft({ user }: { user: User }) {
32
60
  const t = useMemoizedFn((key, data = {}) => {
33
61
  return translate(translations, key, locale, 'en', data);
34
62
  });
63
+
64
+ const [previewProps, setPreviewProps] = useState<{ visible: boolean; nft: NftInfo | null }>({
65
+ visible: false,
66
+ nft: null,
67
+ });
68
+
69
+ const [shareProps, setShareProps] = useState<{ anchorEl: HTMLElement | null; props: SharedProps | null }>({
70
+ anchorEl: null,
71
+ props: null,
72
+ });
73
+
74
+ const handleLinkClick = (nftAddress: string) => {
75
+ const targetURL = joinURL(CHAIN_EXPLORER_URL, 'assets', nftAddress);
76
+ window.open(targetURL, '_blank');
77
+ };
78
+
79
+ const handleShareClick = (element: HTMLElement, nft: NftInfo) => {
80
+ let domain = '';
81
+ if (typeof nft.data?.value === 'string') {
82
+ domain = JSON.parse(nft.data?.value)?.domain;
83
+ } else {
84
+ domain = nft.data?.value?.domain;
85
+ }
86
+ setShareProps({
87
+ anchorEl: element,
88
+ props: {
89
+ title: domain ? `Hey, I just won an NFT at the ${domain} event!` : 'Hey, I just won an NFT!',
90
+ url: joinURL(CHAIN_EXPLORER_URL, 'assets', nft.address),
91
+ },
92
+ });
93
+ };
94
+
95
+ const handleViewClick = (nft: NftInfo) => {
96
+ setPreviewProps({
97
+ visible: true,
98
+ nft,
99
+ });
100
+ };
101
+
35
102
  const paging = useReactive<PaginationProps>({
36
103
  page: 1,
37
104
  size: 20,
@@ -123,9 +190,16 @@ export default function Nft({ user }: { user: User }) {
123
190
  flexShrink: 0,
124
191
  width: { xs: 120, sm: 120, md: 120, lg: 166 },
125
192
  height: { xs: 120, sm: 120, md: 120, lg: 166 },
193
+ position: 'relative',
194
+ borderRadius: 1,
195
+ overflow: 'hidden',
196
+ cursor: 'pointer',
197
+ '&:hover .mask': {
198
+ opacity: 1,
199
+ },
126
200
  }}>
127
201
  <NFTDisplay
128
- data={item.display}
202
+ data={getNFTData(item)}
129
203
  address={item.address}
130
204
  inset
131
205
  imageFilter={{
@@ -134,6 +208,37 @@ export default function Nft({ user }: { user: User }) {
134
208
  f: 'webp',
135
209
  }}
136
210
  />
211
+ <Box className="mask" sx={maskStyle}>
212
+ <Stack direction="row" spacing={1}>
213
+ <IconButton
214
+ size="small"
215
+ className="mask-item"
216
+ onClick={(e) => {
217
+ e.stopPropagation();
218
+ handleLinkClick(item.address);
219
+ }}>
220
+ <Icon icon="tabler:link" fontSize={18} />
221
+ </IconButton>
222
+ <IconButton
223
+ size="small"
224
+ onClick={(e) => {
225
+ e.stopPropagation();
226
+ handleShareClick(e.currentTarget, item);
227
+ }}
228
+ className="mask-item">
229
+ <Icon icon="tabler:share-2" fontSize={18} />
230
+ </IconButton>
231
+ <IconButton
232
+ size="small"
233
+ onClick={(e) => {
234
+ e.stopPropagation();
235
+ handleViewClick(item);
236
+ }}
237
+ className="mask-item">
238
+ <Icon icon="tabler:eye" fontSize={18} />
239
+ </IconButton>
240
+ </Stack>
241
+ </Box>
137
242
  </Box>
138
243
  ))}
139
244
  </Box>
@@ -150,9 +255,25 @@ export default function Nft({ user }: { user: User }) {
150
255
  size="small"
151
256
  />
152
257
  ) : null}
258
+
259
+ <NftPreview
260
+ visible={previewProps.visible && !!previewProps.nft}
261
+ nft={previewProps.nft}
262
+ onClose={() => {
263
+ setPreviewProps({ visible: false, nft: null });
264
+ }}
265
+ />
266
+
267
+ <SocialShare
268
+ anchorEl={shareProps.anchorEl}
269
+ onClose={() => {
270
+ setShareProps({ anchorEl: null, props: null });
271
+ }}
272
+ sharedProps={shareProps.props || { title: '', url: '' }}
273
+ />
153
274
  </>
154
275
  );
155
- }, [loading, dataPage, paging.page, paging.size, handlePageChange]);
276
+ }, [loading, dataPage, paging.page, paging.size, handlePageChange, shareProps, previewProps]);
156
277
 
157
278
  return <Box sx={{ border: '1px solid', borderColor: 'divider', borderRadius: 1, p: 2, mb: 5 }}>{content}</Box>;
158
279
  }
@@ -11,6 +11,8 @@ import { isValidUrl } from './utils';
11
11
  import { translations } from '../../libs/locales';
12
12
  import { commonInputStyle, inputFieldStyle } from '../editable-field';
13
13
 
14
+ const MAX_LINK_COUNT = 10;
15
+
14
16
  /**
15
17
  * 格式化链接显示
16
18
  * 对于 http/https 协议只显示域名,其他协议显示完整链接
@@ -67,7 +69,7 @@ function DynamicLinkForm({ links = [], onChange }: { links?: string[]; onChange:
67
69
  }, [errors, links]);
68
70
 
69
71
  const handleAddLink = () => {
70
- if (links.length < 5 || !isLastError) {
72
+ if (links.length < MAX_LINK_COUNT || !isLastError) {
71
73
  onChange([...links, '']);
72
74
  }
73
75
  };
@@ -127,7 +129,7 @@ function DynamicLinkForm({ links = [], onChange }: { links?: string[]; onChange:
127
129
  </IconButton>
128
130
  </Box>
129
131
  ))}
130
- {links.length < 5 && (
132
+ {links.length < MAX_LINK_COUNT ? (
131
133
  <Button
132
134
  fullWidth
133
135
  variant="outlined"
@@ -149,6 +151,10 @@ function DynamicLinkForm({ links = [], onChange }: { links?: string[]; onChange:
149
151
  }}>
150
152
  <AddIcon /> <span>{t('profile.addLink')}</span>
151
153
  </Button>
154
+ ) : (
155
+ <Typography variant="subtitle1" gutterBottom sx={{ mb: 0, fontSize: '12px', color: 'text.secondary' }}>
156
+ {t('profile.maxLinkCount', { count: MAX_LINK_COUNT })}
157
+ </Typography>
152
158
  )}
153
159
  </Box>
154
160
  );
@@ -1,3 +1,5 @@
1
+ import isUrl from 'is-url';
2
+ import { withHttps } from 'ufo';
1
3
  import dayjs from 'dayjs';
2
4
  import timezone from 'dayjs/plugin/timezone';
3
5
  import utc from 'dayjs/plugin/utc';
@@ -104,9 +106,7 @@ export const getTimezones = () => {
104
106
  };
105
107
 
106
108
  export const isValidUrl = (url: string) => {
107
- const urlPattern =
108
- /^(https?:\/\/)?((([a-zA-Z\d]([a-zA-Z\d-]*[a-zA-Z\d])*)\.)+[a-zA-Z]{2,}|((\d{1,3}\.){3}\d{1,3}))(:\d+)?(\/[-a-zA-Z\d%_.~+]*)*(\?[;&a-zA-Z\d%_.~+=-]*)?(#[a-zA-Z\d_]*)?$/;
109
- return urlPattern.test(url);
109
+ return isUrl(withHttps(url)); // 补充协议后在进行验证是否是合法的url
110
110
  };
111
111
 
112
112
  /**
@@ -160,6 +160,7 @@ export const translations = {
160
160
  postalCode: '邮政编码',
161
161
  invalidPostalCode: '邮政编码格式不正确',
162
162
  },
163
+ maxLinkCount: '最多可添加 {count} 个社交链接',
163
164
  },
164
165
  destroyMyself: {
165
166
  title: '删除账户',
@@ -335,6 +336,7 @@ export const translations = {
335
336
  postalCode: 'Postal Code',
336
337
  invalidPostalCode: 'Postal code is invalid',
337
338
  },
339
+ maxLinkCount: 'Up to {count} social links can be added',
338
340
  },
339
341
  destroyMyself: {
340
342
  title: 'Delete Account',