@arcblock/ux 2.13.20 → 2.13.21

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,23 @@
1
+ import { SxProps } from '@mui/material';
2
+ export default function DIDConnectContainer({ open, popup, hideCloseButton, children, appPid, slotProps, onClose, }: {
3
+ popup?: boolean;
4
+ open?: boolean;
5
+ hideCloseButton?: boolean;
6
+ children: React.ReactNode;
7
+ onClose?: () => void;
8
+ appPid?: string;
9
+ slotProps?: {
10
+ footer?: {
11
+ sx?: SxProps;
12
+ };
13
+ containerPage?: {
14
+ sx?: SxProps;
15
+ };
16
+ containerDrawer?: {
17
+ sx?: SxProps;
18
+ };
19
+ containerDialog?: {
20
+ sx?: SxProps;
21
+ };
22
+ };
23
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,270 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, memo, useRef, createElement as _createElement } from 'react';
3
+ import { useBrowser } from '@arcblock/react-hooks';
4
+ import { Backdrop, Box, Dialog, DialogContent, Drawer, SwipeableDrawer, useMediaQuery } from '@mui/material';
5
+ import { useCreation, useDebounce } from 'ahooks';
6
+ import colorConvert from 'color-convert';
7
+ import { getDIDMotifInfo } from '@arcblock/did-motif';
8
+ import noop from 'lodash/noop';
9
+ import { useTheme } from '../Theme';
10
+ import { mergeSx } from '../Util/style';
11
+ import { getDIDColor, hexToRgba, isEthereumDid } from '../Util';
12
+ import DIDConnectFooter from './did-connect-footer';
13
+ const BackdropWrap = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((backdropProps, ref) => {
14
+ return /*#__PURE__*/_createElement(Backdrop, {
15
+ open: true,
16
+ ref: ref,
17
+ style: {
18
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
19
+ backdropFilter: 'blur(3px)',
20
+ touchAction: 'none'
21
+ },
22
+ ...backdropProps,
23
+ key: "background"
24
+ });
25
+ }));
26
+ export default function DIDConnectContainer({
27
+ open = false,
28
+ popup = false,
29
+ hideCloseButton = false,
30
+ children,
31
+ appPid,
32
+ slotProps,
33
+ onClose = noop
34
+ }) {
35
+ const color = useCreation(() => {
36
+ const did = appPid || window.blocklet.appPid;
37
+ const isEthDid = isEthereumDid(did);
38
+ const didMotifInfo = isEthDid ? undefined : getDIDMotifInfo(did);
39
+ if (isEthDid) {
40
+ return getDIDColor(did);
41
+ }
42
+ return didMotifInfo.color;
43
+ }, []);
44
+ const drawerDragger = useRef(null);
45
+ const browser = useBrowser();
46
+ // 屏宽小于 sm 且在 mobile 设备中全屏显示 dialog (PC 端屏宽小于 sm 的情况正常弹窗, 不全屏显示)
47
+ const matchSm = useMediaQuery('(max-width:640px)');
48
+ let openVariant = 'page';
49
+ if (popup) {
50
+ openVariant = 'dialog';
51
+ if (matchSm && browser.mobile.any) {
52
+ openVariant = 'drawer';
53
+ }
54
+ }
55
+ const theme = useTheme();
56
+ const leavingScreenDelay = theme?.transitions?.duration?.leavingScreen || 500; // 默认值是 195
57
+ const debouncedOpen = useDebounce(open, {
58
+ wait: leavingScreenDelay
59
+ });
60
+
61
+ // eslint-disable-next-line no-unused-vars
62
+ const handleOnClose = (e, reason) => {
63
+ if (['backdropClick', 'escapeKeyDown'].includes(reason)) return;
64
+ onClose();
65
+ };
66
+ const showModal = debouncedOpen || open;
67
+ const DrawerComponent = hideCloseButton ? Drawer : SwipeableDrawer;
68
+ const hslColor = colorConvert.hex.hsl(color);
69
+ const [h, s, l] = hslColor;
70
+ const percentageList = [0, 30, 60, 30, 0, 30, 60, 30];
71
+ const maxPercentage = Math.max(...percentageList);
72
+ const minPercentage = Math.min(...percentageList);
73
+ let useAlpha = false;
74
+ if (l * (100 + maxPercentage) / 100 > 100 || l * (100 + minPercentage) / 100 < 0) {
75
+ // 超出范围,使用 alpha 通道变化
76
+ useAlpha = true;
77
+ }
78
+ const colorList = percentageList.map(percentageItem => {
79
+ let finalL = l * (100 + percentageItem) / 100;
80
+ let finalAlpha = 0.6;
81
+ if (useAlpha) {
82
+ finalAlpha = 0.5 * (100 + percentageItem) / 100;
83
+ } else {
84
+ finalL = l * (100 + percentageItem) / 100;
85
+ }
86
+ return `hsla(${h}, ${s}%, ${finalL}%, ${finalAlpha})`;
87
+ });
88
+ const background = `linear-gradient(45deg, ${colorList.join(', ')})`;
89
+ const colorListGlow = percentageList.map(percentageItem => {
90
+ let finalL = l * (100 + percentageItem) / 100;
91
+ let finalAlpha = 0.2;
92
+ if (useAlpha) {
93
+ finalAlpha = 0.3 * (100 + percentageItem) / 100;
94
+ } else {
95
+ finalL = l * (100 + percentageItem) / 100;
96
+ }
97
+ return `hsla(${h}, ${s}%, ${finalL}%, ${finalAlpha})`;
98
+ });
99
+ const backgroundGlow = `linear-gradient(45deg, ${colorListGlow.join(', ')})`;
100
+ const glowStyle = {
101
+ overflow: 'visible',
102
+ '&::before, &::after': {
103
+ content: '""',
104
+ position: 'absolute',
105
+ top: '-3px',
106
+ right: '-3px',
107
+ bottom: '-3px',
108
+ left: '-3px',
109
+ background,
110
+ backgroundSize: '300% 300%',
111
+ backgroundRepeat: 'no-repeat',
112
+ animation: 'glowRotate 10s linear infinite',
113
+ borderRadius: '14px !important',
114
+ zIndex: 0
115
+ },
116
+ '&::after': {
117
+ background: backgroundGlow,
118
+ filter: 'blur(15px)'
119
+ },
120
+ '@keyframes glowRotate': {
121
+ '0%': {
122
+ backgroundPosition: '0 0'
123
+ },
124
+ '50%': {
125
+ backgroundPosition: '100% 0'
126
+ },
127
+ '100%': {
128
+ backgroundPosition: '0 0'
129
+ }
130
+ }
131
+ };
132
+ if (openVariant === 'page') {
133
+ return /*#__PURE__*/_jsx(Box, {
134
+ className: "did-connect__container-page",
135
+ sx: mergeSx({
136
+ borderRadius: 1,
137
+ position: 'relative',
138
+ zIndex: 1,
139
+ backgroundColor: 'background.default'
140
+ }, glowStyle, slotProps?.containerPage?.sx),
141
+ children: /*#__PURE__*/_jsxs(Box, {
142
+ sx: {
143
+ border: `1px solid ${hexToRgba(color, 0.1)}`,
144
+ m: '-1px',
145
+ position: 'relative',
146
+ borderRadius: '12px',
147
+ zIndex: 2,
148
+ overflow: 'hidden',
149
+ backgroundColor: 'background.default'
150
+ },
151
+ children: [children, /*#__PURE__*/_jsx(DIDConnectFooter, {
152
+ currentAppColor: color,
153
+ sx: mergeSx({
154
+ mx: 0
155
+ }, slotProps?.footer?.sx)
156
+ })]
157
+ })
158
+ });
159
+ }
160
+ if (openVariant === 'drawer') {
161
+ return /*#__PURE__*/_jsxs(DrawerComponent, {
162
+ className: "did-connect__container-drawer",
163
+ disableSwipeToOpen: true,
164
+ open: open,
165
+ anchor: "bottom",
166
+ drawerDragger: drawerDragger.current
167
+ // @ts-ignore
168
+ ,
169
+ onClose: handleOnClose,
170
+ slots: {
171
+ backdrop: BackdropWrap
172
+ },
173
+ PaperProps: {
174
+ sx: mergeSx({
175
+ backgroundColor: 'background.default',
176
+ borderRadius: 3,
177
+ // 保持跟 DID Wallet 一致
178
+ borderBottomLeftRadius: 0,
179
+ borderBottomRightRadius: 0,
180
+ p: '2px',
181
+ animation: 'glowBreathe 7s linear infinite',
182
+ '.did-connect__root': {
183
+ backgroundColor: 'transparent'
184
+ },
185
+ overflow: 'hidden',
186
+ '@keyframes glowBreathe': {
187
+ '0%, 100%': {
188
+ boxShadow: `
189
+ inset 0 0 7px ${hexToRgba(color, 0.3)},
190
+ inset 0 0 12px ${hexToRgba(color, 0.3)}`
191
+ },
192
+ '50%': {
193
+ boxShadow: `
194
+ inset 0 0 18px ${hexToRgba(color, 0.7)},
195
+ inset 0 0 24px ${hexToRgba(color, 0.5)}`
196
+ }
197
+ }
198
+ }, slotProps?.containerDrawer?.sx)
199
+ },
200
+ children: [hideCloseButton ? null : /*#__PURE__*/_jsx(Box, {
201
+ ref: drawerDragger,
202
+ sx: {
203
+ px: 1,
204
+ pt: 2,
205
+ m: 'auto',
206
+ mt: -1,
207
+ mb: -2,
208
+ zIndex: 2
209
+ },
210
+ children: /*#__PURE__*/_jsx(Box, {
211
+ sx: {
212
+ width: '48px',
213
+ height: '4px',
214
+ borderRadius: '100vw',
215
+ backgroundColor: 'rgba(0, 0, 0, 0.2)'
216
+ }
217
+ })
218
+ }), /*#__PURE__*/_jsxs(Box, {
219
+ sx: {
220
+ touchAction: 'none',
221
+ maxWidth: '100%',
222
+ width: 500,
223
+ height: 'auto',
224
+ backgroundColor: 'background.default'
225
+ },
226
+ children: [showModal ? children : null, /*#__PURE__*/_jsx(DIDConnectFooter, {
227
+ currentAppColor: color,
228
+ sx: mergeSx({
229
+ mx: 0
230
+ }, slotProps?.footer?.sx)
231
+ })]
232
+ })]
233
+ });
234
+ }
235
+ return /*#__PURE__*/_jsx(Dialog, {
236
+ open: open,
237
+ slots: {
238
+ backdrop: BackdropWrap
239
+ },
240
+ className: "did-connect__container-dialog",
241
+ onClose: handleOnClose,
242
+ PaperProps: {
243
+ sx: mergeSx({
244
+ // 避免样式被 server 中的定义覆盖
245
+ '&.MuiPaper-rounded': {
246
+ borderRadius: '12px !important'
247
+ },
248
+ position: 'relative',
249
+ backgroundColor: 'background.default'
250
+ }, glowStyle, slotProps?.containerDialog?.sx)
251
+ },
252
+ children: /*#__PURE__*/_jsxs(DialogContent, {
253
+ sx: {
254
+ maxWidth: 'calc(100vw - 18px)',
255
+ maxHeight: 'calc(100vh - 18px)',
256
+ p: '0px !important',
257
+ height: 'auto',
258
+ borderRadius: '12px !important',
259
+ zIndex: 1,
260
+ backgroundColor: 'background.default'
261
+ },
262
+ children: [showModal ? children : null, /*#__PURE__*/_jsx(DIDConnectFooter, {
263
+ currentAppColor: color,
264
+ sx: mergeSx({
265
+ mx: 0
266
+ }, slotProps?.footer?.sx)
267
+ })]
268
+ })
269
+ });
270
+ }
@@ -1,4 +1,6 @@
1
- export default function DIDConnectFooter({ currentAppInfo, currentAppColor, }: {
1
+ import { SxProps } from '@mui/material';
2
+ export default function DIDConnectFooter({ currentAppInfo, currentAppColor, sx, }: {
2
3
  currentAppInfo?: any;
3
4
  currentAppColor?: string;
5
+ sx?: SxProps;
4
6
  }): import("react/jsx-runtime").JSX.Element;
@@ -4,13 +4,15 @@ import PoweredBy from './powered-by';
4
4
  import AppInfoItem from './app-info-item';
5
5
  import AppIcon from './app-icon';
6
6
  import { getDIDColor, hexToRgba } from '../Util';
7
+ import { mergeSx } from '../Util/style';
7
8
  export default function DIDConnectFooter({
8
9
  currentAppInfo = globalThis.blocklet,
9
- currentAppColor = globalThis.blocklet?.appPid ? getDIDColor(globalThis.blocklet?.appPid) : '#fff'
10
+ currentAppColor = globalThis.blocklet?.appPid ? getDIDColor(globalThis.blocklet?.appPid) : '#fff',
11
+ sx
10
12
  }) {
11
13
  const isSmallView = useMediaQuery('(max-width:640px)');
12
14
  return /*#__PURE__*/_jsxs(Box, {
13
- sx: {
15
+ sx: mergeSx({
14
16
  display: 'flex',
15
17
  justifyContent: 'space-between',
16
18
  alignItems: 'center',
@@ -29,7 +31,7 @@ export default function DIDConnectFooter({
29
31
  '-ms-overflow-style': 'none',
30
32
  // 隐藏滚动条 (IE 浏览器)
31
33
  'scrollbar-width': 'none' // 隐藏滚动条 (Firefox)
32
- },
34
+ }, sx),
33
35
  className: "did-connect__footer",
34
36
  children: [/*#__PURE__*/_jsx(AppInfoItem, {
35
37
  appInfo: currentAppInfo,
@@ -5,3 +5,4 @@ export { default as PoweredBy } from './powered-by';
5
5
  export { default as withContainer } from './with-container';
6
6
  export { default as withUxTheme } from './with-ux-theme';
7
7
  export { default as DIDConnectLogo } from './did-connect-logo';
8
+ export { default as DIDConnectContainer } from './did-connect-container';
@@ -4,4 +4,5 @@ export { default as AppIcon } from './app-icon';
4
4
  export { default as PoweredBy } from './powered-by';
5
5
  export { default as withContainer } from './with-container';
6
6
  export { default as withUxTheme } from './with-ux-theme';
7
- export { default as DIDConnectLogo } from './did-connect-logo';
7
+ export { default as DIDConnectLogo } from './did-connect-logo';
8
+ export { default as DIDConnectContainer } from './did-connect-container';
@@ -6,7 +6,7 @@ import { useMemoizedFn, useReactive } from 'ahooks';
6
6
  import { mergeSx } from '../Util/style';
7
7
  import { callIframe, getCallbackAction } from '../Util/iframe';
8
8
  import NeedStorageAccessApiDialog from './need-storage-access-api-dialog';
9
- import { withContainer, withUxTheme } from '../DIDConnect';
9
+ import { DIDConnectContainer } from '../DIDConnect';
10
10
  const SharedBridge = /*#__PURE__*/memo(function SharedBridge({
11
11
  src,
12
12
  onClick,
@@ -67,7 +67,6 @@ const SharedBridge = /*#__PURE__*/memo(function SharedBridge({
67
67
  };
68
68
  // eslint-disable-next-line react-hooks/exhaustive-deps
69
69
  }, [onClick, dataId, targetIframeRef?.current]);
70
- const DialogComponent = withUxTheme(withContainer(NeedStorageAccessApiDialog));
71
70
  const handleLoad = useMemoizedFn(() => {
72
71
  callIframe(targetIframeRef.current, 'hasStorageAccess').then(({
73
72
  value
@@ -89,14 +88,15 @@ const SharedBridge = /*#__PURE__*/memo(function SharedBridge({
89
88
  return null;
90
89
  }
91
90
  return /*#__PURE__*/_jsxs(_Fragment, {
92
- children: [/*#__PURE__*/_jsx(DialogComponent, {
91
+ children: [/*#__PURE__*/_jsx(DIDConnectContainer, {
93
92
  popup: true,
94
- locale: locale,
95
- blocklet: window.blocklet,
93
+ hideCloseButton: true,
96
94
  open: currentState.open,
97
- origin: currentState.origin,
98
- host: currentState.host,
99
- onClose: () => {}
95
+ children: /*#__PURE__*/_jsx(NeedStorageAccessApiDialog, {
96
+ locale: locale,
97
+ origin: currentState.origin,
98
+ host: currentState.host
99
+ })
100
100
  }), /*#__PURE__*/_jsx(Box, {
101
101
  ...rest,
102
102
  component: "iframe",
@@ -1,7 +1,6 @@
1
1
  import { Locale } from '../type';
2
- export default function NeedStorageAccessApiDialog({ locale, origin, host, setColor, }: {
2
+ export default function NeedStorageAccessApiDialog({ locale, origin, host, }: {
3
3
  locale?: Locale;
4
4
  origin: string;
5
5
  host: string;
6
- setColor: (color: string) => void;
7
6
  }): import("react/jsx-runtime").JSX.Element;
@@ -5,12 +5,8 @@ import externalLinkIcon from '@iconify-icons/tabler/external-link';
5
5
  import lockOutlineIcon from '@iconify-icons/material-symbols/lock-outline';
6
6
  import checkCircleIcon from '@iconify-icons/material-symbols/check-circle';
7
7
  import rocketLaunchRoundedIcon from '@iconify-icons/material-symbols/rocket-launch-rounded';
8
- import { useCreation, useMemoizedFn } from 'ahooks';
9
- import { useEffect } from 'react';
10
- import { getDIDMotifInfo } from '@arcblock/did-motif';
8
+ import { useMemoizedFn } from 'ahooks';
11
9
  import { translate } from '../Locale/util';
12
- import { getDIDColor, isEthereumDid } from '../Util';
13
- import { DIDConnectFooter } from '../DIDConnect';
14
10
  const translations = {
15
11
  en: {
16
12
  allow: 'Allow',
@@ -64,25 +60,11 @@ const translations = {
64
60
  export default function NeedStorageAccessApiDialog({
65
61
  locale = 'en',
66
62
  origin,
67
- host,
68
- setColor
63
+ host
69
64
  }) {
70
65
  const t = useMemoizedFn((key, data = {}) => {
71
66
  return translate(translations, key, locale, 'en', data);
72
67
  });
73
- const currentAppColor = useCreation(() => {
74
- const did = window.blocklet.appPid;
75
- const isEthDid = isEthereumDid(did);
76
- const didMotifInfo = isEthDid ? undefined : getDIDMotifInfo(did);
77
- if (isEthDid) {
78
- return getDIDColor(did);
79
- }
80
- return didMotifInfo.color;
81
- }, []);
82
- useEffect(() => {
83
- setColor(currentAppColor);
84
- // eslint-disable-next-line react-hooks/exhaustive-deps
85
- }, [currentAppColor]);
86
68
  return /*#__PURE__*/_jsxs(Box, {
87
69
  sx: {
88
70
  backgroundColor: 'background.default',
@@ -94,7 +76,6 @@ export default function NeedStorageAccessApiDialog({
94
76
  transition: 'width 0.2s ease-in-out',
95
77
  margin: 'auto',
96
78
  p: 3,
97
- pb: 0,
98
79
  gap: 2
99
80
  },
100
81
  children: [/*#__PURE__*/_jsxs(Typography, {
@@ -205,8 +186,6 @@ export default function NeedStorageAccessApiDialog({
205
186
  color: "grey.700",
206
187
  children: t('dataUsage')
207
188
  })]
208
- }), /*#__PURE__*/_jsx(DIDConnectFooter, {
209
- currentAppColor: currentAppColor
210
189
  })]
211
190
  });
212
191
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/ux",
3
- "version": "2.13.20",
3
+ "version": "2.13.21",
4
4
  "description": "Common used react components for arcblock products",
5
5
  "keywords": [
6
6
  "react",
@@ -71,14 +71,14 @@
71
71
  "react": ">=18.2.0",
72
72
  "react-router-dom": ">=6.22.3"
73
73
  },
74
- "gitHead": "6804d65451c3d09464345b86627bc79ba754847a",
74
+ "gitHead": "ff25dbcf651ad7e8c0b940525e285f2cebe3e52d",
75
75
  "dependencies": {
76
76
  "@arcblock/did-motif": "^1.1.13",
77
- "@arcblock/icons": "^2.13.20",
78
- "@arcblock/nft-display": "^2.13.20",
79
- "@arcblock/react-hooks": "^2.13.20",
77
+ "@arcblock/icons": "^2.13.21",
78
+ "@arcblock/nft-display": "^2.13.21",
79
+ "@arcblock/react-hooks": "^2.13.21",
80
80
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
81
- "@blocklet/theme": "^2.13.20",
81
+ "@blocklet/theme": "^2.13.21",
82
82
  "@fontsource/roboto": "~5.1.1",
83
83
  "@fontsource/ubuntu-mono": "^5.0.18",
84
84
  "@iconify-icons/logos": "^1.2.36",
@@ -0,0 +1,320 @@
1
+ import { forwardRef, memo, useRef } from 'react';
2
+ import { useBrowser } from '@arcblock/react-hooks';
3
+ import { Backdrop, Box, Dialog, DialogContent, Drawer, SwipeableDrawer, SxProps, useMediaQuery } from '@mui/material';
4
+ import { useCreation, useDebounce } from 'ahooks';
5
+ import colorConvert from 'color-convert';
6
+ import { getDIDMotifInfo } from '@arcblock/did-motif';
7
+ import noop from 'lodash/noop';
8
+
9
+ import { useTheme } from '../Theme';
10
+ import { mergeSx } from '../Util/style';
11
+ import { getDIDColor, hexToRgba, isEthereumDid } from '../Util';
12
+ import DIDConnectFooter from './did-connect-footer';
13
+
14
+ const BackdropWrap = memo(
15
+ forwardRef((backdropProps, ref) => {
16
+ return (
17
+ <Backdrop
18
+ open
19
+ ref={ref}
20
+ style={{
21
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
22
+ backdropFilter: 'blur(3px)',
23
+ touchAction: 'none',
24
+ }}
25
+ {...backdropProps}
26
+ key="background"
27
+ />
28
+ );
29
+ })
30
+ );
31
+
32
+ export default function DIDConnectContainer({
33
+ open = false,
34
+ popup = false,
35
+ hideCloseButton = false,
36
+ children,
37
+ appPid,
38
+ slotProps,
39
+ onClose = noop,
40
+ }: {
41
+ // 是否弹出显示, true 表示在 Dialog 中渲染, 并可以通过 open/onClose 控制 dialog 的显示/隐藏, false 表示直接渲染原内容
42
+ popup?: boolean;
43
+ open?: boolean;
44
+ hideCloseButton?: boolean;
45
+ children: React.ReactNode;
46
+ onClose?: () => void;
47
+ appPid?: string;
48
+ slotProps?: {
49
+ footer?: {
50
+ sx?: SxProps;
51
+ };
52
+ containerPage?: {
53
+ sx?: SxProps;
54
+ };
55
+ containerDrawer?: {
56
+ sx?: SxProps;
57
+ };
58
+ containerDialog?: {
59
+ sx?: SxProps;
60
+ };
61
+ };
62
+ }) {
63
+ const color = useCreation(() => {
64
+ const did = appPid || window.blocklet.appPid;
65
+ const isEthDid = isEthereumDid(did);
66
+ const didMotifInfo = isEthDid ? undefined : getDIDMotifInfo(did);
67
+ if (isEthDid) {
68
+ return getDIDColor(did);
69
+ }
70
+
71
+ return didMotifInfo.color;
72
+ }, []);
73
+
74
+ const drawerDragger = useRef(null);
75
+ const browser = useBrowser();
76
+ // 屏宽小于 sm 且在 mobile 设备中全屏显示 dialog (PC 端屏宽小于 sm 的情况正常弹窗, 不全屏显示)
77
+ const matchSm = useMediaQuery('(max-width:640px)');
78
+ let openVariant = 'page';
79
+ if (popup) {
80
+ openVariant = 'dialog';
81
+ if (matchSm && browser.mobile.any) {
82
+ openVariant = 'drawer';
83
+ }
84
+ }
85
+
86
+ const theme = useTheme();
87
+
88
+ const leavingScreenDelay = theme?.transitions?.duration?.leavingScreen || 500; // 默认值是 195
89
+ const debouncedOpen = useDebounce(open, {
90
+ wait: leavingScreenDelay,
91
+ });
92
+
93
+ // eslint-disable-next-line no-unused-vars
94
+ const handleOnClose = (e: React.MouseEvent<HTMLElement>, reason: string) => {
95
+ if (['backdropClick', 'escapeKeyDown'].includes(reason)) return;
96
+ onClose();
97
+ };
98
+
99
+ const showModal = debouncedOpen || open;
100
+
101
+ const DrawerComponent = hideCloseButton ? Drawer : SwipeableDrawer;
102
+
103
+ const hslColor = colorConvert.hex.hsl(color);
104
+
105
+ const [h, s, l] = hslColor;
106
+ const percentageList = [0, 30, 60, 30, 0, 30, 60, 30];
107
+ const maxPercentage = Math.max(...percentageList);
108
+ const minPercentage = Math.min(...percentageList);
109
+ let useAlpha = false;
110
+ if ((l * (100 + maxPercentage)) / 100 > 100 || (l * (100 + minPercentage)) / 100 < 0) {
111
+ // 超出范围,使用 alpha 通道变化
112
+ useAlpha = true;
113
+ }
114
+ const colorList = percentageList.map((percentageItem) => {
115
+ let finalL = (l * (100 + percentageItem)) / 100;
116
+ let finalAlpha = 0.6;
117
+ if (useAlpha) {
118
+ finalAlpha = (0.5 * (100 + percentageItem)) / 100;
119
+ } else {
120
+ finalL = (l * (100 + percentageItem)) / 100;
121
+ }
122
+ return `hsla(${h}, ${s}%, ${finalL}%, ${finalAlpha})`;
123
+ });
124
+ const background = `linear-gradient(45deg, ${colorList.join(', ')})`;
125
+ const colorListGlow = percentageList.map((percentageItem) => {
126
+ let finalL = (l * (100 + percentageItem)) / 100;
127
+ let finalAlpha = 0.2;
128
+ if (useAlpha) {
129
+ finalAlpha = (0.3 * (100 + percentageItem)) / 100;
130
+ } else {
131
+ finalL = (l * (100 + percentageItem)) / 100;
132
+ }
133
+ return `hsla(${h}, ${s}%, ${finalL}%, ${finalAlpha})`;
134
+ });
135
+
136
+ const backgroundGlow = `linear-gradient(45deg, ${colorListGlow.join(', ')})`;
137
+
138
+ const glowStyle = {
139
+ overflow: 'visible',
140
+ '&::before, &::after': {
141
+ content: '""',
142
+ position: 'absolute',
143
+ top: '-3px',
144
+ right: '-3px',
145
+ bottom: '-3px',
146
+ left: '-3px',
147
+ background,
148
+ backgroundSize: '300% 300%',
149
+ backgroundRepeat: 'no-repeat',
150
+ animation: 'glowRotate 10s linear infinite',
151
+ borderRadius: '14px !important',
152
+ zIndex: 0,
153
+ },
154
+ '&::after': {
155
+ background: backgroundGlow,
156
+ filter: 'blur(15px)',
157
+ },
158
+
159
+ '@keyframes glowRotate': {
160
+ '0%': {
161
+ backgroundPosition: '0 0',
162
+ },
163
+ '50%': {
164
+ backgroundPosition: '100% 0',
165
+ },
166
+ '100%': {
167
+ backgroundPosition: '0 0',
168
+ },
169
+ },
170
+ };
171
+
172
+ if (openVariant === 'page') {
173
+ return (
174
+ <Box
175
+ className="did-connect__container-page"
176
+ sx={mergeSx(
177
+ {
178
+ borderRadius: 1,
179
+ position: 'relative',
180
+ zIndex: 1,
181
+ backgroundColor: 'background.default',
182
+ },
183
+ glowStyle,
184
+ slotProps?.containerPage?.sx
185
+ )}>
186
+ <Box
187
+ sx={{
188
+ border: `1px solid ${hexToRgba(color, 0.1)}`,
189
+ m: '-1px',
190
+ position: 'relative',
191
+ borderRadius: '12px',
192
+ zIndex: 2,
193
+ overflow: 'hidden',
194
+ backgroundColor: 'background.default',
195
+ }}>
196
+ {children}
197
+ <DIDConnectFooter currentAppColor={color} sx={mergeSx({ mx: 0 }, slotProps?.footer?.sx)} />
198
+ </Box>
199
+ </Box>
200
+ );
201
+ }
202
+
203
+ if (openVariant === 'drawer') {
204
+ return (
205
+ <DrawerComponent
206
+ className="did-connect__container-drawer"
207
+ disableSwipeToOpen
208
+ open={open}
209
+ anchor="bottom"
210
+ drawerDragger={drawerDragger.current}
211
+ // @ts-ignore
212
+ onClose={handleOnClose}
213
+ slots={{
214
+ backdrop: BackdropWrap,
215
+ }}
216
+ PaperProps={{
217
+ sx: mergeSx(
218
+ {
219
+ backgroundColor: 'background.default',
220
+ borderRadius: 3, // 保持跟 DID Wallet 一致
221
+ borderBottomLeftRadius: 0,
222
+ borderBottomRightRadius: 0,
223
+ p: '2px',
224
+ animation: 'glowBreathe 7s linear infinite',
225
+ '.did-connect__root': {
226
+ backgroundColor: 'transparent',
227
+ },
228
+ overflow: 'hidden',
229
+ '@keyframes glowBreathe': {
230
+ '0%, 100%': {
231
+ boxShadow: `
232
+ inset 0 0 7px ${hexToRgba(color, 0.3)},
233
+ inset 0 0 12px ${hexToRgba(color, 0.3)}`,
234
+ },
235
+ '50%': {
236
+ boxShadow: `
237
+ inset 0 0 18px ${hexToRgba(color, 0.7)},
238
+ inset 0 0 24px ${hexToRgba(color, 0.5)}`,
239
+ },
240
+ },
241
+ },
242
+ slotProps?.containerDrawer?.sx
243
+ ),
244
+ }}>
245
+ {hideCloseButton ? null : (
246
+ <Box
247
+ ref={drawerDragger}
248
+ sx={{
249
+ px: 1,
250
+ pt: 2,
251
+ m: 'auto',
252
+ mt: -1,
253
+ mb: -2,
254
+ zIndex: 2,
255
+ }}>
256
+ <Box
257
+ sx={{
258
+ width: '48px',
259
+ height: '4px',
260
+ borderRadius: '100vw',
261
+ backgroundColor: 'rgba(0, 0, 0, 0.2)',
262
+ }}
263
+ />
264
+ </Box>
265
+ )}
266
+ <Box
267
+ sx={{
268
+ touchAction: 'none',
269
+ maxWidth: '100%',
270
+ width: 500,
271
+ height: 'auto',
272
+ backgroundColor: 'background.default',
273
+ }}>
274
+ {/* HACK: 由于 MUI 文档中描述 使用 keepMounted: false 可能会造成问题,所以采用下面的方案进行 HACK */}
275
+ {/* https://mui.com/material-ui/react-drawer/#keep-mounted */}
276
+ {showModal ? children : null}
277
+ <DIDConnectFooter currentAppColor={color} sx={mergeSx({ mx: 0 }, slotProps?.footer?.sx)} />
278
+ </Box>
279
+ </DrawerComponent>
280
+ );
281
+ }
282
+
283
+ return (
284
+ <Dialog
285
+ open={open}
286
+ slots={{
287
+ backdrop: BackdropWrap,
288
+ }}
289
+ className="did-connect__container-dialog"
290
+ onClose={handleOnClose}
291
+ PaperProps={{
292
+ sx: mergeSx(
293
+ {
294
+ // 避免样式被 server 中的定义覆盖
295
+ '&.MuiPaper-rounded': {
296
+ borderRadius: '12px !important',
297
+ },
298
+ position: 'relative',
299
+ backgroundColor: 'background.default',
300
+ },
301
+ glowStyle,
302
+ slotProps?.containerDialog?.sx
303
+ ),
304
+ }}>
305
+ <DialogContent
306
+ sx={{
307
+ maxWidth: 'calc(100vw - 18px)',
308
+ maxHeight: 'calc(100vh - 18px)',
309
+ p: '0px !important',
310
+ height: 'auto',
311
+ borderRadius: '12px !important',
312
+ zIndex: 1,
313
+ backgroundColor: 'background.default',
314
+ }}>
315
+ {showModal ? children : null}
316
+ <DIDConnectFooter currentAppColor={color} sx={mergeSx({ mx: 0 }, slotProps?.footer?.sx)} />
317
+ </DialogContent>
318
+ </Dialog>
319
+ );
320
+ }
@@ -1,40 +1,46 @@
1
- import { Box, useMediaQuery } from '@mui/material';
1
+ import { Box, SxProps, useMediaQuery } from '@mui/material';
2
2
 
3
3
  import PoweredBy from './powered-by';
4
4
  import AppInfoItem from './app-info-item';
5
5
  import AppIcon from './app-icon';
6
6
  import { getDIDColor, hexToRgba } from '../Util';
7
+ import { mergeSx } from '../Util/style';
7
8
 
8
9
  export default function DIDConnectFooter({
9
10
  currentAppInfo = globalThis.blocklet,
10
11
  currentAppColor = globalThis.blocklet?.appPid ? getDIDColor(globalThis.blocklet?.appPid) : '#fff',
12
+ sx,
11
13
  }: {
12
14
  currentAppInfo?: any;
13
15
  currentAppColor?: string;
16
+ sx?: SxProps;
14
17
  }) {
15
18
  const isSmallView = useMediaQuery('(max-width:640px)');
16
19
 
17
20
  return (
18
21
  <Box
19
- sx={{
20
- display: 'flex',
21
- justifyContent: 'space-between',
22
- alignItems: 'center',
23
- gap: 1,
24
- fontSize: 12,
25
- backgroundColor: hexToRgba(currentAppColor, 0.08),
26
- // 需要保持跟 .did-connect__root 的规则一样
27
- mx: isSmallView ? -2 : -3,
28
- px: isSmallView ? 2 : 3,
29
- py: 1.5,
30
- // HACK: 极限条件下,footer 会溢出,使用隐藏的滚动条来处理这种情况(屏幕宽度小于 360 时会出现)
31
- overflow: 'auto',
32
- '&::-webkit-scrollbar': {
33
- display: 'none', // 隐藏滚动条 (Webkit 浏览器)
22
+ sx={mergeSx(
23
+ {
24
+ display: 'flex',
25
+ justifyContent: 'space-between',
26
+ alignItems: 'center',
27
+ gap: 1,
28
+ fontSize: 12,
29
+ backgroundColor: hexToRgba(currentAppColor, 0.08),
30
+ // 需要保持跟 .did-connect__root 的规则一样
31
+ mx: isSmallView ? -2 : -3,
32
+ px: isSmallView ? 2 : 3,
33
+ py: 1.5,
34
+ // HACK: 极限条件下,footer 会溢出,使用隐藏的滚动条来处理这种情况(屏幕宽度小于 360 时会出现)
35
+ overflow: 'auto',
36
+ '&::-webkit-scrollbar': {
37
+ display: 'none', // 隐藏滚动条 (Webkit 浏览器)
38
+ },
39
+ '-ms-overflow-style': 'none', // 隐藏滚动条 (IE 浏览器)
40
+ 'scrollbar-width': 'none', // 隐藏滚动条 (Firefox)
34
41
  },
35
- '-ms-overflow-style': 'none', // 隐藏滚动条 (IE 浏览器)
36
- 'scrollbar-width': 'none', // 隐藏滚动条 (Firefox)
37
- }}
42
+ sx
43
+ )}
38
44
  className="did-connect__footer">
39
45
  <AppInfoItem
40
46
  appInfo={currentAppInfo}
@@ -5,3 +5,4 @@ export { default as PoweredBy } from './powered-by';
5
5
  export { default as withContainer } from './with-container';
6
6
  export { default as withUxTheme } from './with-ux-theme';
7
7
  export { default as DIDConnectLogo } from './did-connect-logo';
8
+ export { default as DIDConnectContainer } from './did-connect-container';
@@ -8,7 +8,7 @@ import { mergeSx } from '../Util/style';
8
8
  import { callIframe, getCallbackAction } from '../Util/iframe';
9
9
  import { Locale } from '../type';
10
10
  import NeedStorageAccessApiDialog from './need-storage-access-api-dialog';
11
- import { withContainer, withUxTheme } from '../DIDConnect';
11
+ import { DIDConnectContainer } from '../DIDConnect';
12
12
 
13
13
  const SharedBridge = memo(function SharedBridge({
14
14
  src,
@@ -82,8 +82,6 @@ const SharedBridge = memo(function SharedBridge({
82
82
  // eslint-disable-next-line react-hooks/exhaustive-deps
83
83
  }, [onClick, dataId, targetIframeRef?.current]);
84
84
 
85
- const DialogComponent = withUxTheme(withContainer(NeedStorageAccessApiDialog));
86
-
87
85
  const handleLoad = useMemoizedFn(() => {
88
86
  callIframe(targetIframeRef.current as HTMLIFrameElement, 'hasStorageAccess').then(({ value }) => {
89
87
  currentState.hasStorageAccess = value;
@@ -107,15 +105,9 @@ const SharedBridge = memo(function SharedBridge({
107
105
 
108
106
  return (
109
107
  <>
110
- <DialogComponent
111
- popup
112
- locale={locale}
113
- blocklet={window.blocklet}
114
- open={currentState.open}
115
- origin={currentState.origin}
116
- host={currentState.host}
117
- onClose={() => {}}
118
- />
108
+ <DIDConnectContainer popup hideCloseButton open={currentState.open}>
109
+ <NeedStorageAccessApiDialog locale={locale} origin={currentState.origin} host={currentState.host} />
110
+ </DIDConnectContainer>
119
111
  <Box
120
112
  {...rest}
121
113
  component="iframe"
@@ -4,13 +4,10 @@ import externalLinkIcon from '@iconify-icons/tabler/external-link';
4
4
  import lockOutlineIcon from '@iconify-icons/material-symbols/lock-outline';
5
5
  import checkCircleIcon from '@iconify-icons/material-symbols/check-circle';
6
6
  import rocketLaunchRoundedIcon from '@iconify-icons/material-symbols/rocket-launch-rounded';
7
- import { useCreation, useMemoizedFn } from 'ahooks';
8
- import { useEffect } from 'react';
9
- import { getDIDMotifInfo } from '@arcblock/did-motif';
7
+ import { useMemoizedFn } from 'ahooks';
10
8
 
11
9
  import { Locale } from '../type';
12
10
  import { translate } from '../Locale/util';
13
- import { getDIDColor, isEthereumDid } from '../Util';
14
11
  import { DIDConnectFooter } from '../DIDConnect';
15
12
 
16
13
  const translations: Record<
@@ -67,31 +64,14 @@ export default function NeedStorageAccessApiDialog({
67
64
  locale = 'en',
68
65
  origin,
69
66
  host,
70
- setColor,
71
67
  }: {
72
68
  locale?: Locale;
73
69
  origin: string;
74
70
  host: string;
75
- setColor: (color: string) => void;
76
71
  }) {
77
72
  const t = useMemoizedFn((key, data = {}) => {
78
73
  return translate(translations, key, locale, 'en', data);
79
74
  });
80
- const currentAppColor = useCreation(() => {
81
- const did = window.blocklet.appPid;
82
- const isEthDid = isEthereumDid(did);
83
- const didMotifInfo = isEthDid ? undefined : getDIDMotifInfo(did);
84
- if (isEthDid) {
85
- return getDIDColor(did);
86
- }
87
-
88
- return didMotifInfo.color;
89
- }, []);
90
-
91
- useEffect(() => {
92
- setColor(currentAppColor);
93
- // eslint-disable-next-line react-hooks/exhaustive-deps
94
- }, [currentAppColor]);
95
75
 
96
76
  return (
97
77
  <Box
@@ -105,7 +85,6 @@ export default function NeedStorageAccessApiDialog({
105
85
  transition: 'width 0.2s ease-in-out',
106
86
  margin: 'auto',
107
87
  p: 3,
108
- pb: 0,
109
88
  gap: 2,
110
89
  }}>
111
90
  <Typography
@@ -165,7 +144,6 @@ export default function NeedStorageAccessApiDialog({
165
144
  {t('dataUsage')}
166
145
  </Typography>
167
146
  </Box>
168
- <DIDConnectFooter currentAppColor={currentAppColor} />
169
147
  </Box>
170
148
  );
171
149
  }