@arcblock/ux 2.10.43 → 2.10.45

Sign up to get free protection for your applications and to get access to all the features.
package/lib/DID/index.js CHANGED
@@ -1,47 +1,30 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { forwardRef, useState } from 'react';
3
- import PropTypes from 'prop-types';
4
2
  import { getDIDMotifInfo } from '@arcblock/did-motif';
3
+ import { types } from '@ocap/mcrypto';
5
4
  import QRCode from 'qrcode.react';
6
5
  import { Icon } from '@iconify/react';
7
6
  import IconQrCode from '@iconify-icons/material-symbols/qr-code-rounded';
8
- import { Box, Dialog, DialogContent, DialogTitle, Typography } from '@mui/material';
7
+ import { Box, Dialog, DialogContent, Typography } from '@mui/material';
9
8
  import { useCreation, useMemoizedFn } from 'ahooks';
10
- import { temp as colors } from '../Colors';
11
- import { DID_PREFIX } from '../Util/constant';
9
+ import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
12
10
  import Address from '../Address';
13
11
  import Avatar from '../Avatar';
14
- import { isEthereumDid, getFontSize, getDIDColor } from '../Util';
12
+ import { temp as colors } from '../Colors';
13
+ import { DID_PREFIX } from '../Util/constant';
15
14
  import { translate } from '../Locale/util';
15
+ import { getDIDColor, isEthereumDid } from '../Util';
16
16
  const translations = {
17
17
  en: {
18
- scanQrcode: 'Scan with DID Wallet to transfer token to here',
18
+ scanQrcode: 'Scan with DID Wallet to view this {role}',
19
19
  download: 'Download',
20
20
  downloadTips: "Don't have DID Wallet ?"
21
21
  },
22
22
  zh: {
23
- scanQrcode: '扫描此二维码,用 DID Wallet 转账',
23
+ scanQrcode: '使用 DID Wallet 扫码查看该 {role}',
24
24
  download: '下载',
25
25
  downloadTips: '没有 DID Wallet ?'
26
26
  }
27
27
  };
28
- const DIDPropTypes = {
29
- did: PropTypes.string.isRequired,
30
- size: PropTypes.number,
31
- component: PropTypes.string,
32
- copyable: PropTypes.bool,
33
- responsive: PropTypes.bool,
34
- showCopyButtonInTooltip: PropTypes.bool,
35
- showAvatar: PropTypes.bool,
36
- showQrcode: PropTypes.bool,
37
- inline: PropTypes.bool,
38
- append: PropTypes.any,
39
- compact: PropTypes.bool,
40
- startChars: PropTypes.number,
41
- endChars: PropTypes.number,
42
- locale: PropTypes.oneOf(['en', 'zh']),
43
- chainId: PropTypes.string
44
- };
45
28
  const DEFAULT_CHAIN_ID = 'xenon-2020-01-15';
46
29
  const CHAIN_ID_MAP = {
47
30
  'main.abtnetwork.io': DEFAULT_CHAIN_ID,
@@ -63,6 +46,16 @@ const isSquareMotif = roleType => {
63
46
  };
64
47
  return !roles[roleType];
65
48
  };
49
+ const getRoleName = roleType => {
50
+ const [roleName] = Object.entries(types.RoleType).find(item => item[1] === roleType) || [];
51
+ if (roleName) {
52
+ // UpperCase first word, example:
53
+ // ROLE_ACCOUNT -> Account
54
+ // ROLE_APPLICATION -> Application
55
+ return roleName.toLowerCase().replace(/role_(\S)/g, (_, $1) => $1.toUpperCase());
56
+ }
57
+ return 'Address';
58
+ };
66
59
  const getAvatarSize = (didMotifInfo, isEthDid, size) => {
67
60
  if (isEthDid) {
68
61
  return size * 0.75;
@@ -72,28 +65,46 @@ const getAvatarSize = (didMotifInfo, isEthDid, size) => {
72
65
  }
73
66
  return size;
74
67
  };
75
-
76
- /**
77
- * @type React.ForwardRefRenderFunction<HTMLElement, typeof DIDPropTypes>
78
- */
79
- const DID = /*#__PURE__*/forwardRef(({
80
- did,
81
- showAvatar,
82
- showQrcode,
83
- chainId,
84
- locale,
85
- ...rest
86
- }, ref) => {
68
+ const DID = /*#__PURE__*/forwardRef((props, ref) => {
69
+ const {
70
+ did,
71
+ showAvatar = true,
72
+ showQrcode = false,
73
+ locale = 'en',
74
+ size = 0,
75
+ component = 'span',
76
+ copyable = true,
77
+ responsive = true,
78
+ showCopyButtonInTooltip = false,
79
+ inline = false,
80
+ append = null,
81
+ compact = false,
82
+ startChars = 6,
83
+ endChars = 6
84
+ } = props;
85
+ const rest = {
86
+ size,
87
+ component,
88
+ copyable,
89
+ responsive,
90
+ showCopyButtonInTooltip,
91
+ inline,
92
+ append,
93
+ compact,
94
+ startChars,
95
+ endChars
96
+ };
97
+ const addressRef = useRef(null);
87
98
  const t = useMemoizedFn((key, data = {}) => {
88
99
  return translate(translations, key, locale, 'en', data);
89
100
  });
101
+ let chainId = props.chainId || '';
90
102
  try {
91
103
  if (!chainId) {
92
- const chainHostUrl = window?.blocklet?.CHAIN_HOST;
104
+ const chainHostUrl = window.blocklet?.CHAIN_HOST;
93
105
  if (chainHostUrl) {
94
106
  const chainHost = new URL(chainHostUrl).hostname;
95
107
  if (chainHost) {
96
- // eslint-disable-next-line no-param-reassign
97
108
  chainId = CHAIN_ID_MAP[chainHost];
98
109
  }
99
110
  }
@@ -104,23 +115,19 @@ const DID = /*#__PURE__*/forwardRef(({
104
115
  const [open, setOpen] = useState(false);
105
116
  const isEthDid = isEthereumDid(did);
106
117
  const didMotifInfo = isEthDid ? undefined : getDIDMotifInfo(did);
107
- const fontSize = getFontSize(rest.size);
108
- const prepend = [/*#__PURE__*/_jsx("span", {
118
+ const getPrepend = avatarSize => [/*#__PURE__*/_jsx("span", {
109
119
  style: {
110
120
  flex: '0 0 auto',
111
- fontSize,
112
121
  color: getFontColor(did, didMotifInfo, isEthDid)
113
122
  },
114
123
  children: "DID:"
115
124
  }, "prefix-did"), /*#__PURE__*/_jsx("span", {
116
125
  className: "did-address-text",
117
- style: {
118
- fontSize
119
- },
120
126
  children: "ABT:"
121
127
  }, "prefix-abt"), showAvatar && /*#__PURE__*/_jsx(Avatar, {
128
+ src: "",
122
129
  did: did,
123
- size: getAvatarSize(didMotifInfo, isEthDid, rest.size || 18),
130
+ size: getAvatarSize(didMotifInfo, isEthDid, avatarSize || 18),
124
131
  style: {
125
132
  display: 'inline-flex',
126
133
  alignItems: 'center',
@@ -138,8 +145,18 @@ const DID = /*#__PURE__*/forwardRef(({
138
145
  e.preventDefault();
139
146
  setOpen(true);
140
147
  });
148
+ useImperativeHandle(ref, () => {
149
+ return new Proxy({
150
+ openQRCode: () => setOpen(true),
151
+ closeQRCode: () => setOpen(false)
152
+ }, {
153
+ get(target, key) {
154
+ return target[key] || addressRef?.current?.[key];
155
+ }
156
+ });
157
+ });
141
158
  const downloadUrl = useCreation(() => {
142
- if (['zh', 'en'].includes) {
159
+ if (['zh', 'en'].includes(locale)) {
143
160
  return `https://www.didwallet.io/${locale}`;
144
161
  }
145
162
  return 'https://www.didwallet.io/en';
@@ -164,13 +181,14 @@ const DID = /*#__PURE__*/forwardRef(({
164
181
  }, [downloadUrl]);
165
182
  return /*#__PURE__*/_jsxs(_Fragment, {
166
183
  children: [/*#__PURE__*/_jsx(Address, {
167
- ref: ref,
168
184
  locale: locale,
169
185
  content: `${DID_PREFIX}${did}`,
170
186
  ...rest,
171
- prepend: prepend,
187
+ ref: addressRef,
188
+ prepend: getPrepend(rest.size),
172
189
  append: /*#__PURE__*/_jsxs(_Fragment, {
173
190
  children: [showQrcode ? /*#__PURE__*/_jsx(Box, {
191
+ id: "did-qrcode-button",
174
192
  component: Icon,
175
193
  icon: IconQrCode,
176
194
  onClick: openQrCode,
@@ -183,20 +201,31 @@ const DID = /*#__PURE__*/forwardRef(({
183
201
  }) : null, rest.append]
184
202
  }),
185
203
  children: did
186
- }), /*#__PURE__*/_jsxs(Dialog, {
204
+ }), /*#__PURE__*/_jsx(Dialog, {
187
205
  open: open,
188
206
  onClose: closeQrCode,
189
207
  maxWidth: "sm",
190
- children: [/*#__PURE__*/_jsx(DialogTitle, {
191
- align: "center",
192
- children: /*#__PURE__*/_jsx(Typography, {
193
- variant: "h6",
194
- component: "h3",
195
- children: t('scanQrcode')
196
- })
197
- }), /*#__PURE__*/_jsxs(DialogContent, {
208
+ PaperProps: {
209
+ sx: {
210
+ boxShadow: 'none',
211
+ borderRadius: '12px'
212
+ }
213
+ },
214
+ children: /*#__PURE__*/_jsxs(DialogContent, {
198
215
  align: "center",
199
- children: [/*#__PURE__*/_jsx(QRCode
216
+ sx: {
217
+ p: 3
218
+ },
219
+ children: [/*#__PURE__*/_jsx(Typography, {
220
+ sx: {
221
+ mb: 2,
222
+ fontWeight: 500,
223
+ fontSize: 18
224
+ },
225
+ children: t('scanQrcode', {
226
+ role: getRoleName(didMotifInfo?.roleType)
227
+ })
228
+ }), /*#__PURE__*/_jsx(QRCode
200
229
  // eslint-disable-next-line max-len
201
230
  , {
202
231
  value: `abt://abtwallet.io/i?did=${DID_PREFIX}${did}&action=didRecognize&chainID=${chainId || DEFAULT_CHAIN_ID}`,
@@ -205,14 +234,16 @@ const DID = /*#__PURE__*/forwardRef(({
205
234
  level: "M"
206
235
  }), /*#__PURE__*/_jsx(Box, {
207
236
  sx: {
208
- marginTop: 1,
237
+ mt: 1.5,
209
238
  textAlign: 'center'
210
239
  },
211
240
  children: /*#__PURE__*/_jsx(Address, {
212
- copyable: true,
213
241
  locale: locale,
214
242
  content: `${DID_PREFIX}${did}`,
215
- prepend: prepend,
243
+ prepend: getPrepend(16),
244
+ ...rest,
245
+ size: 16,
246
+ copyable: true,
216
247
  children: did
217
248
  })
218
249
  }), /*#__PURE__*/_jsx(Typography, {
@@ -224,25 +255,8 @@ const DID = /*#__PURE__*/forwardRef(({
224
255
  },
225
256
  children: downloadTips
226
257
  })]
227
- })]
258
+ })
228
259
  })]
229
260
  });
230
261
  });
231
- export default DID;
232
- DID.propTypes = DIDPropTypes;
233
- DID.defaultProps = {
234
- size: 0,
235
- component: 'span',
236
- copyable: true,
237
- responsive: true,
238
- showCopyButtonInTooltip: false,
239
- showAvatar: true,
240
- showQrcode: false,
241
- inline: false,
242
- append: null,
243
- compact: false,
244
- startChars: 6,
245
- endChars: 6,
246
- locale: 'en',
247
- chainId: ''
248
- };
262
+ export default DID;
package/lib/type.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module '@arcblock/did-motif';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcblock/ux",
3
- "version": "2.10.43",
3
+ "version": "2.10.45",
4
4
  "description": "Common used react components for arcblock products",
5
5
  "keywords": [
6
6
  "react",
@@ -64,12 +64,12 @@
64
64
  "react": ">=18.2.0",
65
65
  "react-router-dom": ">=6.22.3"
66
66
  },
67
- "gitHead": "efd3c4cefb4772be2e53ab7ae509e3ed4540f49a",
67
+ "gitHead": "f4387cd434dcdf0bab88973b014575613dcb3611",
68
68
  "dependencies": {
69
69
  "@arcblock/did-motif": "^1.1.13",
70
- "@arcblock/icons": "^2.10.43",
71
- "@arcblock/nft-display": "^2.10.43",
72
- "@arcblock/react-hooks": "^2.10.43",
70
+ "@arcblock/icons": "^2.10.45",
71
+ "@arcblock/nft-display": "^2.10.45",
72
+ "@arcblock/react-hooks": "^2.10.45",
73
73
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
74
74
  "@fontsource/inter": "^5.0.16",
75
75
  "@fontsource/ubuntu-mono": "^5.0.18",
@@ -65,7 +65,7 @@ export default function CompactText({ startChars, endChars, children, showCopyBu
65
65
  CompactText.propTypes = {
66
66
  startChars: PropTypes.number,
67
67
  endChars: PropTypes.number,
68
- children: PropTypes.node.isRequired,
68
+ children: PropTypes.node,
69
69
  // 在 tooltip 中完整地址后显示复制按钮
70
70
  showCopyButtonInTooltip: PropTypes.bool,
71
71
  };
@@ -73,5 +73,6 @@ CompactText.propTypes = {
73
73
  CompactText.defaultProps = {
74
74
  startChars: 6,
75
75
  endChars: 6,
76
+ children: null,
76
77
  showCopyButtonInTooltip: false,
77
78
  };
@@ -0,0 +1,215 @@
1
+ import '@fontsource/ubuntu-mono/400.css';
2
+ import { Check as CheckIcon, ContentCopy as CopyIcon } from '@mui/icons-material';
3
+ import { Box, BoxProps, Tooltip } from '@mui/material';
4
+ import { green } from '@mui/material/colors';
5
+ import copy from 'copy-to-clipboard';
6
+ import noop from 'lodash/noop';
7
+ import React, { forwardRef, ReactNode, useImperativeHandle, useRef, useState } from 'react';
8
+ import { styled } from '../Theme';
9
+ import { getFontSize } from '../Util';
10
+ import CompactText from './compact-text';
11
+
12
+ const translations = {
13
+ en: {
14
+ copy: 'Click To Copy',
15
+ copied: 'Copied!',
16
+ },
17
+ zh: {
18
+ copy: '点击复制',
19
+ copied: '已复制!',
20
+ },
21
+ };
22
+
23
+ export interface HTMLDidAddressElement extends HTMLDivElement {
24
+ copy: () => void;
25
+ }
26
+ export interface IDidAddressProps extends BoxProps {
27
+ component?: React.ElementType;
28
+ size?: number;
29
+ copyable?: boolean;
30
+ // compact mode 下, hover 时会在 tooltip 中显示完整地址, showCopyButtonInTooltip = true 时会在完整地址后显示一个复制按钮
31
+ showCopyButtonInTooltip?: boolean;
32
+ content?: string;
33
+ inline?: boolean;
34
+ prepend?: ReactNode;
35
+ append?: ReactNode;
36
+ // 紧凑模式
37
+ compact?: boolean;
38
+ startChars?: number;
39
+ endChars?: number;
40
+ locale?: 'en' | 'zh';
41
+ }
42
+
43
+ /**
44
+ * DidAddress 组件 (新版设计)
45
+ *
46
+ * - 样式调整
47
+ * - click-to-copy 调整
48
+ * - 长文本截断处理 (Ellipsis)
49
+ * - 支持 inline 或 block 的显示方式
50
+ * - 支持紧凑模式, 该模式下:
51
+ * - 占用宽度较小, 因此不考虑水平空间不够用的情况, 且忽略末尾省略号
52
+ * - 对于多层元素结构的 children, 保持元素结构, 将最内层 text 替换为 CompactText 组件
53
+ * - 为保证 copy 功能正常工作, 原 children 始终渲染, 但在紧凑式下会隐藏
54
+ * - 可配合 useMediaQuery 使用
55
+ */
56
+ const DidAddress = forwardRef<HTMLDidAddressElement, IDidAddressProps>((props, ref) => {
57
+ const {
58
+ component = 'span',
59
+ size = 0,
60
+ copyable = true,
61
+ showCopyButtonInTooltip = false,
62
+ children = null,
63
+ content = '',
64
+ prepend = null,
65
+ append = null,
66
+ compact = false,
67
+ startChars = 6,
68
+ endChars = 6,
69
+ locale: originalLocale = 'en',
70
+ ...rest
71
+ } = props;
72
+ let locale = originalLocale;
73
+ if (!translations[locale]) {
74
+ locale = 'en';
75
+ }
76
+
77
+ const [copied, setCopied] = useState(false);
78
+ const textRef = useRef<HTMLDivElement>(null);
79
+ const rootRef = useRef<any>(null);
80
+
81
+ const handleCopy = () => {
82
+ copy(content || textRef.current?.textContent || '');
83
+ setCopied(true);
84
+ // 恢复 copied 状态
85
+ setTimeout(() => {
86
+ setCopied(false);
87
+ }, 1500);
88
+ };
89
+ const onCopy = (e: any) => {
90
+ e.stopPropagation();
91
+ e.preventDefault();
92
+ handleCopy();
93
+ };
94
+ useImperativeHandle(
95
+ ref,
96
+ () =>
97
+ new Proxy(
98
+ {
99
+ copy: handleCopy,
100
+ },
101
+ {
102
+ get(target: any, key: string) {
103
+ return target[key] || rootRef.current?.[key];
104
+ },
105
+ }
106
+ )
107
+ );
108
+
109
+ let copyElement = null;
110
+ if (copyable) {
111
+ copyElement = (
112
+ <span className="did-address-copy-wrapper" title={copied ? '' : translations[locale].copy}>
113
+ {copied ? (
114
+ <Tooltip title={translations[locale].copied} placement="bottom" arrow open={copied}>
115
+ <CheckIcon className="did-address-copy" style={{ color: green[500] }} />
116
+ </Tooltip>
117
+ ) : (
118
+ /* title prop 直接加在 icon 上不生效 */
119
+ <CopyIcon className="did-address-copy" onClick={onCopy} />
120
+ )}
121
+ </span>
122
+ );
123
+ }
124
+
125
+ return (
126
+ <Root as={component} size={size} {...rest} ref={rootRef}>
127
+ {prepend}
128
+ <Box sx={{ display: 'none' }} ref={textRef}>
129
+ {children}
130
+ </Box>
131
+ {/* 注意: 该元素必须渲染(可以隐藏), 以便 compact 模式下复制的文本是完整的 */}
132
+ <Tooltip title={copyable ? '' : translations[locale].copied} placement="bottom" arrow open={copied}>
133
+ {compact ? (
134
+ <Box
135
+ component="span"
136
+ className="did-address-text"
137
+ sx={{
138
+ cursor: copyable ? 'unset' : 'pointer',
139
+ }}
140
+ onDoubleClick={copyable ? noop : onCopy}>
141
+ <CompactText startChars={startChars} endChars={endChars} showCopyButtonInTooltip={showCopyButtonInTooltip}>
142
+ {children}
143
+ </CompactText>
144
+ </Box>
145
+ ) : (
146
+ <Box
147
+ component="span"
148
+ className="did-address-text did-address-truncate"
149
+ sx={{
150
+ display: compact ? 'none' : 'inline',
151
+ cursor: copyable ? 'unset' : 'pointer',
152
+ }}
153
+ onDoubleClick={copyable ? noop : onCopy}>
154
+ {children}
155
+ </Box>
156
+ )}
157
+ </Tooltip>
158
+ {copyElement}
159
+ {append}
160
+ </Root>
161
+ );
162
+ });
163
+
164
+ export default DidAddress;
165
+
166
+ const Root = styled<any>(Box, { shouldForwardProp: (prop) => prop !== 'inline' })`
167
+ font-family: 'Ubuntu Mono', monospace;
168
+ && {
169
+ display: ${({ inline }: any) => (inline ? 'inline-flex' : 'flex')};
170
+ align-items: center;
171
+ max-width: 100%;
172
+ overflow: hidden;
173
+ color: #ccc;
174
+ font-size: ${(props: any) => getFontSize(props.size)};
175
+ font-weight: 400;
176
+
177
+ svg {
178
+ fill: currentColor;
179
+ }
180
+ }
181
+
182
+ .did-address-text {
183
+ color: #666;
184
+ }
185
+ /* truncate string with ellipsis */
186
+ .did-address-truncate {
187
+ white-space: nowrap;
188
+ overflow: hidden;
189
+ text-overflow: ellipsis;
190
+ }
191
+
192
+ .did-address-copy-wrapper {
193
+ display: flex;
194
+ justify-content: center;
195
+ align-items: center;
196
+ width: 1em;
197
+ height: 1em;
198
+ margin-left: 8px;
199
+ }
200
+ .did-address-copy {
201
+ flex: 0 0 auto;
202
+ font-size: 1em;
203
+ color: #999;
204
+ cursor: pointer;
205
+ }
206
+
207
+ /* link */
208
+ a {
209
+ color: #666;
210
+ }
211
+ &:hover a {
212
+ color: #222;
213
+ text-decoration: underline;
214
+ }
215
+ `;
@@ -0,0 +1,18 @@
1
+ import { forwardRef } from 'react';
2
+ import DidAddress, { HTMLDidAddressElement, IDidAddressProps } from './did-address';
3
+ import ResponsiveDidAddress from './responsive-did-address';
4
+
5
+ export interface IDidAddressWrapper extends IDidAddressProps {
6
+ responsive?: boolean;
7
+ }
8
+
9
+ const DidAddressWrapper = forwardRef<HTMLDidAddressElement, IDidAddressWrapper>(
10
+ ({ responsive = true, ...rest }, ref) => {
11
+ if (responsive) {
12
+ return <ResponsiveDidAddress {...rest} ref={ref} />;
13
+ }
14
+ return <DidAddress {...rest} ref={ref} />;
15
+ }
16
+ );
17
+
18
+ export default DidAddressWrapper;
@@ -0,0 +1,90 @@
1
+ import { useSize } from 'ahooks';
2
+ import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
3
+ import { styled } from '../Theme';
4
+
5
+ import DidAddress, { HTMLDidAddressElement, IDidAddressProps } from './did-address';
6
+
7
+ /**
8
+ * 根据父容器宽度自动切换 compact 模式
9
+ *
10
+ * 实现逻辑:
11
+ * - DidAddress 外层包裹一个容器, 其宽度自动撑满父容器宽度 (即这个容器需要是块级元素或 100% 宽的 inline-block)
12
+ * - DidAddress 本身以 inline 形式渲染 (方便探测 did-address 的 full-width)
13
+ * - 组件 mounted 时记录 did address 的 full-width (非 compact 模式的宽度)
14
+ * - 监听容器宽度变化, 当容器宽度变化时, 对比容器宽度和 did address full-width, => 切换 compact 模式
15
+ * - TODO: 初始化时, 在确定是否应该以 compact 模式渲染前, 隐藏显示, 避免闪烁问题
16
+ */
17
+ const ResponsiveDidAddress = forwardRef<HTMLDidAddressElement, IResponsiveDidAddressProps>(
18
+ ({ style, className, component = 'span', ...rest }, ref) => {
19
+ const [compact, setCompact] = useState(false);
20
+ // did address 完整显示时的宽度
21
+ const [addressFullWidth, setAddressFullWidth] = useState<number | null | undefined>(null);
22
+ const containerRef = useRef<HTMLDivElement>(null);
23
+ const innerRef = useRef<HTMLDidAddressElement>(null);
24
+ const size = useSize(containerRef);
25
+ const containerWidth = size?.width || 0;
26
+
27
+ useImperativeHandle(
28
+ ref,
29
+ () =>
30
+ new Proxy(
31
+ {},
32
+ {
33
+ get(target: any, key: keyof HTMLDidAddressElement) {
34
+ return innerRef.current?.[key];
35
+ },
36
+ }
37
+ )
38
+ );
39
+
40
+ // 存储完整显示时 address 组件的宽度
41
+ useEffect(() => {
42
+ if (!compact && addressFullWidth === null) {
43
+ setAddressFullWidth(innerRef.current?.offsetWidth);
44
+ }
45
+ // eslint-disable-next-line react-hooks/exhaustive-deps
46
+ }, []);
47
+
48
+ useEffect(() => {
49
+ if (containerWidth && addressFullWidth) {
50
+ setCompact(containerWidth < addressFullWidth);
51
+ }
52
+ }, [containerWidth, addressFullWidth]);
53
+ return (
54
+ <Root as={component} ref={containerRef} style={style} className={className}>
55
+ <StyledDidAddress {...rest} component={component} inline compact={compact} ref={innerRef} />
56
+ </Root>
57
+ );
58
+ }
59
+ );
60
+
61
+ export default ResponsiveDidAddress;
62
+
63
+ export interface IResponsiveDidAddressProps extends IDidAddressProps {
64
+ style?: React.CSSProperties;
65
+ className?: string;
66
+ component?: React.ElementType;
67
+ }
68
+
69
+ const Root = styled('div')`
70
+ display: block;
71
+ overflow: hidden;
72
+ ${({ inline }: any) =>
73
+ inline &&
74
+ `
75
+ display: inline-block;
76
+ width: 100%;
77
+ `}
78
+ `;
79
+
80
+ const StyledDidAddress = styled(DidAddress)`
81
+ && {
82
+ max-width: none;
83
+ }
84
+ .did-address-text {
85
+ /* 禁止文本 Ellipsis/截断, 以便测量真实的宽度 */
86
+ white-space: nowrap;
87
+ overflow: visible;
88
+ text-overflow: unset;
89
+ }
90
+ `;
@@ -124,7 +124,9 @@ export default function BlockletStore(props: IBlockletStore) {
124
124
  />
125
125
  </Stack>
126
126
  <Stack direction="row" alignItems="center" gap={2} color="text.secondary">
127
- <IconText icon={<Avatar src={avatar} did={did} size={20} variant="circle" />}>{author}</IconText>
127
+ <IconText icon={<Avatar src={avatar} did={did} size={20} variant="circle" />} maxWidth={200}>
128
+ {author}
129
+ </IconText>
128
130
  <IconText icon={<Icon icon={Download} />} title={download}>
129
131
  {formatDownloadCount(download)}
130
132
  </IconText>
@@ -1,4 +1,4 @@
1
- import { Stack, Typography } from '@mui/material';
1
+ import { Stack, StackProps, Typography } from '@mui/material';
2
2
  import React from 'react';
3
3
 
4
4
  export default function IconText({
@@ -6,15 +6,21 @@ export default function IconText({
6
6
  children,
7
7
  maxWidth = 100,
8
8
  title,
9
+ sx,
9
10
  }: {
10
11
  icon?: React.ReactNode;
11
12
  children?: React.ReactNode;
12
13
  maxWidth?: number;
13
14
  title?: string;
15
+ sx?: StackProps['sx'];
14
16
  }) {
15
17
  return (
16
18
  (children === 0 || children) && (
17
- <Stack direction="row" alignItems="center" gap={1} sx={{ maxWidth, overflow: 'hidden' }}>
19
+ <Stack
20
+ direction="row"
21
+ alignItems="center"
22
+ gap={1}
23
+ sx={[{ maxWidth, overflow: 'hidden' }, ...(Array.isArray(sx) ? sx : [sx])]}>
18
24
  {icon}
19
25
  <Typography
20
26
  flex={1}