@arcblock/ux 2.10.43 → 2.10.45

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.
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}