@arcblock/ux 2.12.3 → 2.12.5
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/NavMenu/nav-menu.d.ts +1 -1
- package/lib/NavMenu/nav-menu.js +22 -12
- package/lib/NavMenu/products.js +74 -63
- package/lib/NavMenu/style.d.ts +2 -2
- package/lib/NavMenu/style.js +46 -25
- package/lib/NavMenu/sub-container.d.ts +6 -0
- package/lib/NavMenu/sub-container.js +83 -0
- package/package.json +5 -5
- package/src/NavMenu/nav-menu.tsx +26 -15
- package/src/NavMenu/products.tsx +140 -78
- package/src/NavMenu/style.ts +40 -22
- package/src/NavMenu/sub-container.tsx +96 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
3
|
+
const paddingTop = 8;
|
4
|
+
|
5
|
+
// 下拉子菜单容器
|
6
|
+
export function SubContainer({
|
7
|
+
children,
|
8
|
+
...props
|
9
|
+
}) {
|
10
|
+
const rootRef = useRef(null);
|
11
|
+
const [position, setPosition] = useState(0); // 只需要保存水平偏移量
|
12
|
+
|
13
|
+
const updatePosition = () => {
|
14
|
+
if (!rootRef.current) return;
|
15
|
+
const anchor = rootRef.current.parentElement; // 以父容器作为参照
|
16
|
+
if (!anchor) return;
|
17
|
+
const anchorRect = anchor.getBoundingClientRect();
|
18
|
+
const containerRect = rootRef.current.getBoundingClientRect();
|
19
|
+
const windowWidth = window.innerWidth;
|
20
|
+
const padding = 32;
|
21
|
+
|
22
|
+
// 1. 计算相对于父元素的水平居中位置
|
23
|
+
let left = (anchorRect.width - containerRect.width) / 2;
|
24
|
+
|
25
|
+
// 2. 检查容器在视口中的绝对位置
|
26
|
+
const absoluteLeft = anchorRect.left + left;
|
27
|
+
|
28
|
+
// 3. 调整水平位置确保不超出窗口
|
29
|
+
if (absoluteLeft < 0) {
|
30
|
+
// 如果超出左边界,向右偏移
|
31
|
+
left = left - absoluteLeft + padding;
|
32
|
+
} else if (absoluteLeft + containerRect.width > windowWidth) {
|
33
|
+
// 如果超出右边界,向左偏移
|
34
|
+
left -= absoluteLeft + containerRect.width - windowWidth + padding;
|
35
|
+
}
|
36
|
+
setPosition(left);
|
37
|
+
};
|
38
|
+
|
39
|
+
// 监听自身大小变化
|
40
|
+
useEffect(() => {
|
41
|
+
const resizeObserver = new ResizeObserver(() => {
|
42
|
+
updatePosition();
|
43
|
+
});
|
44
|
+
resizeObserver.observe(rootRef.current);
|
45
|
+
return () => resizeObserver.disconnect();
|
46
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
47
|
+
}, []);
|
48
|
+
|
49
|
+
// 监听 anchor 的大小变化
|
50
|
+
useEffect(() => {
|
51
|
+
const anchor = rootRef.current?.parentElement;
|
52
|
+
let resizeObserver = null;
|
53
|
+
if (anchor) {
|
54
|
+
resizeObserver = new ResizeObserver(() => {
|
55
|
+
updatePosition();
|
56
|
+
});
|
57
|
+
resizeObserver.observe(anchor);
|
58
|
+
}
|
59
|
+
return () => resizeObserver?.disconnect();
|
60
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
61
|
+
}, []);
|
62
|
+
|
63
|
+
// 监听窗口大小变化
|
64
|
+
useEffect(() => {
|
65
|
+
const handleResize = () => {
|
66
|
+
updatePosition();
|
67
|
+
};
|
68
|
+
window.addEventListener('resize', handleResize);
|
69
|
+
return () => window.removeEventListener('resize', handleResize);
|
70
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
71
|
+
}, []);
|
72
|
+
return /*#__PURE__*/_jsx("div", {
|
73
|
+
ref: rootRef,
|
74
|
+
className: "navmenu-sub__container",
|
75
|
+
style: {
|
76
|
+
paddingTop: `${paddingTop}px`,
|
77
|
+
transform: `translateX(${position}px)`
|
78
|
+
},
|
79
|
+
...props,
|
80
|
+
children: children
|
81
|
+
});
|
82
|
+
}
|
83
|
+
export default SubContainer;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@arcblock/ux",
|
3
|
-
"version": "2.12.
|
3
|
+
"version": "2.12.5",
|
4
4
|
"description": "Common used react components for arcblock products",
|
5
5
|
"keywords": [
|
6
6
|
"react",
|
@@ -68,12 +68,12 @@
|
|
68
68
|
"react": ">=18.2.0",
|
69
69
|
"react-router-dom": ">=6.22.3"
|
70
70
|
},
|
71
|
-
"gitHead": "
|
71
|
+
"gitHead": "3999edcf6f4b5fd30f3c5d27b27f85e0051315b4",
|
72
72
|
"dependencies": {
|
73
73
|
"@arcblock/did-motif": "^1.1.13",
|
74
|
-
"@arcblock/icons": "^2.12.
|
75
|
-
"@arcblock/nft-display": "^2.12.
|
76
|
-
"@arcblock/react-hooks": "^2.12.
|
74
|
+
"@arcblock/icons": "^2.12.5",
|
75
|
+
"@arcblock/nft-display": "^2.12.5",
|
76
|
+
"@arcblock/react-hooks": "^2.12.5",
|
77
77
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
78
78
|
"@fontsource/inter": "^5.0.16",
|
79
79
|
"@fontsource/ubuntu-mono": "^5.0.18",
|
package/src/NavMenu/nav-menu.tsx
CHANGED
@@ -5,7 +5,8 @@ import { MoreHoriz as MoreHorizIcon, ExpandMore as ExpandMoreIcon, Menu as MenuI
|
|
5
5
|
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
|
6
6
|
import { useCreation, useMemoizedFn, useReactive, useSize, useThrottleFn } from 'ahooks';
|
7
7
|
import { NavMenuProvider, useNavMenuContext } from './nav-menu-context';
|
8
|
-
import { NavMenuRoot,
|
8
|
+
import { NavMenuRoot, NavMenuItem, NavMenuSub, NavMenuSubList, NavMenuStyled } from './style';
|
9
|
+
import { SubContainer } from './sub-container';
|
9
10
|
|
10
11
|
// 过滤 children 中的 Item/Sub, 忽略其它类型的 element
|
11
12
|
function filterItems(children: React.ReactNode) {
|
@@ -67,7 +68,6 @@ function NavMenu({
|
|
67
68
|
if (!items?.length && !children?.length) {
|
68
69
|
throw new Error("One of 'items' or 'children' is required by NavMenu component.");
|
69
70
|
}
|
70
|
-
|
71
71
|
const currentState = useReactive({
|
72
72
|
activeId,
|
73
73
|
openedIds: [] as string[],
|
@@ -112,6 +112,7 @@ function NavMenu({
|
|
112
112
|
const navMenuRef = useRef<HTMLUListElement | null>(null);
|
113
113
|
const itemRefs = useRef<HTMLElement[]>([]);
|
114
114
|
const moreIconRef = useRef<HTMLLIElement | null>(null);
|
115
|
+
const containerWidth = useRef(0);
|
115
116
|
const isAllItemsHidden = currentState.hiddenItemCount === itemRefs.current?.length;
|
116
117
|
const style = isAllItemsHidden ? { marginLeft: '0px' } : undefined;
|
117
118
|
|
@@ -136,7 +137,6 @@ function NavMenu({
|
|
136
137
|
let totalWidthUsed = 0;
|
137
138
|
let newHiddenCount = 0;
|
138
139
|
let leftAllHidden = false;
|
139
|
-
const containerWidth = containerSize?.width || 0;
|
140
140
|
const moreIconWidth = moreIconRef.current
|
141
141
|
? moreIconRef.current.offsetWidth + parseFloat(window.getComputedStyle(moreIconRef.current).marginLeft)
|
142
142
|
: 0;
|
@@ -147,7 +147,7 @@ function NavMenu({
|
|
147
147
|
const marginLeft = index > 0 ? parseFloat(window.getComputedStyle(item).marginLeft) : 0;
|
148
148
|
const currentItemWidth = item.offsetWidth + marginLeft;
|
149
149
|
|
150
|
-
if (containerWidth - moreIconWidth >= totalWidthUsed + currentItemWidth && !leftAllHidden) {
|
150
|
+
if (containerWidth.current - moreIconWidth >= totalWidthUsed + currentItemWidth && !leftAllHidden) {
|
151
151
|
totalWidthUsed += currentItemWidth;
|
152
152
|
} else {
|
153
153
|
item.style.display = 'none';
|
@@ -165,6 +165,7 @@ function NavMenu({
|
|
165
165
|
);
|
166
166
|
|
167
167
|
useLayoutEffect(() => {
|
168
|
+
containerWidth.current = containerSize?.width || navMenuRef.current?.clientWidth || 0;
|
168
169
|
if (mode === 'horizontal') {
|
169
170
|
checkItemsFit();
|
170
171
|
}
|
@@ -195,8 +196,15 @@ function NavMenu({
|
|
195
196
|
|
196
197
|
const classes = clsx('navmenu', `navmenu--${mode}`, rest.className);
|
197
198
|
|
198
|
-
const renderItem = (item: ItemOptions, index: number,
|
199
|
+
const renderItem = (item: ItemOptions, index: number, level = 0) => {
|
200
|
+
const isTopLevel = level === 0;
|
201
|
+
|
199
202
|
if (item?.children) {
|
203
|
+
// 只渲染两级子菜单
|
204
|
+
if (level > 0) {
|
205
|
+
return null;
|
206
|
+
}
|
207
|
+
|
200
208
|
// 对于 Sub 组件,如果它是顶级组件,则包含 ref
|
201
209
|
return (
|
202
210
|
<Sub
|
@@ -215,7 +223,7 @@ function NavMenu({
|
|
215
223
|
{typeof item.children === 'function'
|
216
224
|
? item.children
|
217
225
|
: item.children.map((childItem, childIndex: number) =>
|
218
|
-
renderItem({ ...childItem, variant: 'panel' }, childIndex,
|
226
|
+
renderItem({ ...childItem, variant: 'panel' }, childIndex, level + 1)
|
219
227
|
)}
|
220
228
|
</Sub>
|
221
229
|
);
|
@@ -243,21 +251,24 @@ function NavMenu({
|
|
243
251
|
};
|
244
252
|
|
245
253
|
const content = items
|
246
|
-
? items?.slice(-currentState.hiddenItemCount).map((item, index) => renderItem(item, index))
|
254
|
+
? items?.slice(-currentState.hiddenItemCount).map((item, index) => renderItem(item, index, 1))
|
247
255
|
: children?.slice(-currentState.hiddenItemCount);
|
248
256
|
|
257
|
+
// 当前展开的子菜单
|
258
|
+
const openedId = currentState.openedIds[0];
|
259
|
+
|
249
260
|
return (
|
250
261
|
<NavMenuProvider value={contextValue}>
|
251
|
-
<
|
252
|
-
<
|
253
|
-
{items ? items.map((item, index) => renderItem(item, index
|
262
|
+
<NavMenuStyled {...rest} className={classes} $textColor={textColor} $bgColor={bgColor}>
|
263
|
+
<NavMenuRoot className={clsx('navmenu-root', `navmenu-root--${mode}`)} ref={navMenuRef}>
|
264
|
+
{items ? items.map((item, index) => renderItem(item, index)) : renderChildrenWithRef(children || [])}
|
254
265
|
{currentState.hiddenItemCount > 0 && (
|
255
266
|
<Sub expandIcon={false} icon={icon} label="" ref={moreIconRef} style={style}>
|
256
267
|
{content}
|
257
268
|
</Sub>
|
258
269
|
)}
|
259
|
-
</
|
260
|
-
</
|
270
|
+
</NavMenuRoot>
|
271
|
+
</NavMenuStyled>
|
261
272
|
</NavMenuProvider>
|
262
273
|
);
|
263
274
|
}
|
@@ -324,7 +335,7 @@ export const Item = forwardRef<HTMLLIElement, ItemProps>(
|
|
324
335
|
Item.displayName = 'NavMenu.Item';
|
325
336
|
|
326
337
|
export interface SubProps extends Omit<ItemProps, 'children' | 'active'> {
|
327
|
-
children?: Array<React.ReactElement> | ((props: { isOpen: boolean }) => React.ReactElement | null);
|
338
|
+
children?: Array<React.ReactElement | null> | ((props: { isOpen: boolean }) => React.ReactElement | null);
|
328
339
|
expandIcon?: React.ReactNode | ((props: { isOpen: boolean }) => React.ReactNode);
|
329
340
|
}
|
330
341
|
|
@@ -394,13 +405,13 @@ export const Sub = forwardRef<HTMLLIElement, SubProps>(
|
|
394
405
|
{typeof expandIcon === 'function' ? expandIcon({ isOpen }) : expandIcon}
|
395
406
|
</span>
|
396
407
|
)}
|
397
|
-
<
|
408
|
+
<SubContainer {...containerProps}>
|
398
409
|
{typeof children === 'function' ? (
|
399
410
|
children({ isOpen }) // 自定义渲染
|
400
411
|
) : (
|
401
412
|
<NavMenuSubList className="navmenu-sub__list">{filterItems(children)}</NavMenuSubList>
|
402
413
|
)}
|
403
|
-
</
|
414
|
+
</SubContainer>
|
404
415
|
</NavMenuSub>
|
405
416
|
);
|
406
417
|
}
|
package/src/NavMenu/products.tsx
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
import {
|
1
|
+
import { useRef } from 'react';
|
2
2
|
import { Link } from 'react-router-dom';
|
3
3
|
import { useCreation, useMemoizedFn } from 'ahooks';
|
4
4
|
import { Box, BoxProps, Grid } from '@mui/material';
|
5
|
-
import { useWindowSize } from 'react-use';
|
6
5
|
import SubItemGroup from './sub-item-group';
|
7
6
|
import { Item } from './nav-menu';
|
8
7
|
import { styled } from '../Theme';
|
@@ -38,58 +37,58 @@ const translations = {
|
|
38
37
|
},
|
39
38
|
products: {
|
40
39
|
nftStudio: {
|
41
|
-
description: '
|
40
|
+
description: 'Mint and manage NFTs',
|
42
41
|
},
|
43
42
|
creatorStudio: {
|
44
|
-
description:
|
43
|
+
description: 'All-in-one creator tool',
|
45
44
|
},
|
46
45
|
aigne: {
|
47
|
-
description: '
|
46
|
+
description: 'Your AI assistant',
|
48
47
|
},
|
49
48
|
aistro: {
|
50
|
-
description: '
|
49
|
+
description: 'AI-powered astrology',
|
51
50
|
},
|
52
51
|
blockletLauncher: {
|
53
|
-
description: '
|
52
|
+
description: 'One-click app launcher',
|
54
53
|
},
|
55
54
|
alKit: {
|
56
|
-
description: '
|
55
|
+
description: 'Boost apps with AI',
|
57
56
|
},
|
58
57
|
blockletStore: {
|
59
|
-
description: '
|
58
|
+
description: 'Discover & deploy apps',
|
60
59
|
},
|
61
60
|
web3Kit: {
|
62
|
-
description: '
|
61
|
+
description: 'Web3 dev toolkit',
|
63
62
|
},
|
64
63
|
blockletFramework: {
|
65
|
-
description: '
|
64
|
+
description: 'Build and run blocklets',
|
66
65
|
},
|
67
66
|
didSpaces: {
|
68
|
-
description: '
|
67
|
+
description: 'Secure personal storage',
|
69
68
|
},
|
70
69
|
abtNetwork: {
|
71
|
-
description:
|
70
|
+
description: 'Fast blockchain network',
|
72
71
|
},
|
73
72
|
blockletServer: {
|
74
|
-
description: '
|
73
|
+
description: 'Host your apps easily',
|
75
74
|
},
|
76
|
-
|
77
|
-
description: '
|
75
|
+
ocap: {
|
76
|
+
description: 'Multi-chain connector',
|
78
77
|
},
|
79
78
|
did: {
|
80
|
-
description: '
|
79
|
+
description: 'Self-sovereign ID',
|
81
80
|
},
|
82
81
|
didWallet: {
|
83
|
-
description: '
|
82
|
+
description: 'Smart digital wallet',
|
84
83
|
},
|
85
84
|
didNameService: {
|
86
|
-
description: '
|
85
|
+
description: 'Web3 domain names',
|
87
86
|
},
|
88
87
|
vc: {
|
89
88
|
description: 'Verifiable Credentials',
|
90
89
|
},
|
91
90
|
didConnect: {
|
92
|
-
description: '
|
91
|
+
description: 'Passwordless login',
|
93
92
|
},
|
94
93
|
},
|
95
94
|
},
|
@@ -102,58 +101,58 @@ const translations = {
|
|
102
101
|
},
|
103
102
|
products: {
|
104
103
|
nftStudio: {
|
105
|
-
description: 'NFT
|
104
|
+
description: '铸造和管理 NFT',
|
106
105
|
},
|
107
106
|
creatorStudio: {
|
108
|
-
description: '
|
107
|
+
description: '一体化创作工具',
|
109
108
|
},
|
110
109
|
aigne: {
|
111
|
-
description: '
|
110
|
+
description: '您的人工智能助手',
|
112
111
|
},
|
113
112
|
aistro: {
|
114
|
-
description: '
|
113
|
+
description: 'AI 占星术',
|
115
114
|
},
|
116
115
|
blockletLauncher: {
|
117
|
-
description: '
|
116
|
+
description: '一键启动应用程序',
|
118
117
|
},
|
119
118
|
alKit: {
|
120
|
-
description: '
|
119
|
+
description: 'AI 赋能应用',
|
121
120
|
},
|
122
121
|
blockletStore: {
|
123
|
-
description: '
|
122
|
+
description: '发现和部署应用程序',
|
124
123
|
},
|
125
124
|
web3Kit: {
|
126
|
-
description: '
|
125
|
+
description: 'Web3 开发工具包',
|
127
126
|
},
|
128
127
|
blockletFramework: {
|
129
|
-
description: '
|
128
|
+
description: '构建并运行 Blocklet',
|
130
129
|
},
|
131
130
|
didSpaces: {
|
132
|
-
description: '
|
131
|
+
description: '安全的个人存储',
|
133
132
|
},
|
134
133
|
abtNetwork: {
|
135
|
-
description: '
|
134
|
+
description: '快速区块链网络',
|
136
135
|
},
|
137
136
|
blockletServer: {
|
138
|
-
description: '
|
137
|
+
description: '轻松托管应用程序',
|
139
138
|
},
|
140
|
-
|
141
|
-
description: '
|
139
|
+
ocap: {
|
140
|
+
description: '多链连接器',
|
142
141
|
},
|
143
142
|
did: {
|
144
|
-
description: '
|
143
|
+
description: '自主身份',
|
145
144
|
},
|
146
145
|
didWallet: {
|
147
146
|
description: '智能数字钱包',
|
148
147
|
},
|
149
148
|
didNameService: {
|
150
|
-
description: '
|
149
|
+
description: 'Web3 域名',
|
151
150
|
},
|
152
151
|
vc: {
|
153
152
|
description: '可验证凭证',
|
154
153
|
},
|
155
154
|
didConnect: {
|
156
|
-
description: '
|
155
|
+
description: '无密码登录',
|
157
156
|
},
|
158
157
|
},
|
159
158
|
},
|
@@ -191,7 +190,6 @@ export interface ProductsProps extends BoxProps {
|
|
191
190
|
export default function Products({ className, isOpen, ...rest }: ProductsProps) {
|
192
191
|
const { mode } = useNavMenuContext();
|
193
192
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
194
|
-
const { width, height } = useWindowSize();
|
195
193
|
const { locale = 'en' } = useLocaleContext() || {};
|
196
194
|
const t = useMemoizedFn((key, data = {}) => translate(translations, key, locale, 'en', data));
|
197
195
|
const groups = useCreation(() => {
|
@@ -202,13 +200,22 @@ export default function Products({ className, isOpen, ...rest }: ProductsProps)
|
|
202
200
|
children: [
|
203
201
|
[
|
204
202
|
{
|
205
|
-
label:
|
203
|
+
label: (
|
204
|
+
<Link to={`https://www.nftstudio.rocks/${locale}`} target="_blank" rel="noreferrer noopener">
|
205
|
+
NFT Studio
|
206
|
+
</Link>
|
207
|
+
),
|
206
208
|
description: t('products.nftStudio.description'),
|
207
209
|
icon: <NftStudioSvg />,
|
208
210
|
},
|
209
211
|
{
|
210
212
|
label: (
|
211
|
-
<Link
|
213
|
+
<Link
|
214
|
+
to={`https://www.arcblock.io/content/collections/${locale}/creator-studio`}
|
215
|
+
target="_blank"
|
216
|
+
rel="noreferrer noopener">
|
217
|
+
Creator Studio
|
218
|
+
</Link>
|
212
219
|
),
|
213
220
|
description: t('products.creatorStudio.description'),
|
214
221
|
icon: <CreatorStudioSvg />,
|
@@ -216,12 +223,20 @@ export default function Products({ className, isOpen, ...rest }: ProductsProps)
|
|
216
223
|
],
|
217
224
|
[
|
218
225
|
{
|
219
|
-
label:
|
226
|
+
label: (
|
227
|
+
<Link to={`https://www.aigne.io/${locale}`} target="_blank" rel="noreferrer noopener">
|
228
|
+
AIGNE
|
229
|
+
</Link>
|
230
|
+
),
|
220
231
|
description: t('products.aigne.description'),
|
221
232
|
icon: <AigneSvg />,
|
222
233
|
},
|
223
234
|
{
|
224
|
-
label:
|
235
|
+
label: (
|
236
|
+
<Link to={`https://www.aistro.io/${locale}`} target="_blank" rel="noreferrer noopener">
|
237
|
+
Aistro
|
238
|
+
</Link>
|
239
|
+
),
|
225
240
|
description: t('products.aistro.description'),
|
226
241
|
icon: <AistroSvg />,
|
227
242
|
},
|
@@ -234,24 +249,43 @@ export default function Products({ className, isOpen, ...rest }: ProductsProps)
|
|
234
249
|
children: [
|
235
250
|
[
|
236
251
|
{
|
237
|
-
label:
|
252
|
+
label: (
|
253
|
+
<Link to={`https://launcher.arcblock.io/${locale}`} target="_blank" rel="noreferrer noopener">
|
254
|
+
Blocklet Launcher
|
255
|
+
</Link>
|
256
|
+
),
|
238
257
|
description: t('products.blockletLauncher.description'),
|
239
258
|
icon: <BlockletLauncherSvg />,
|
240
259
|
},
|
241
260
|
{
|
242
|
-
label:
|
261
|
+
label: (
|
262
|
+
<Link
|
263
|
+
to={`https://www.arcblock.io/content/collections/${locale}/ai-kit`}
|
264
|
+
target="_blank"
|
265
|
+
rel="noreferrer noopener">
|
266
|
+
Al Kit
|
267
|
+
</Link>
|
268
|
+
),
|
243
269
|
description: t('products.alKit.description'),
|
244
270
|
icon: <AIKitSvg />,
|
245
271
|
},
|
246
272
|
],
|
247
273
|
[
|
248
274
|
{
|
249
|
-
label:
|
275
|
+
label: (
|
276
|
+
<Link to={`https://store.blocklet.dev/${locale}`} target="_blank" rel="noreferrer noopener">
|
277
|
+
Blocklet Store
|
278
|
+
</Link>
|
279
|
+
),
|
250
280
|
description: t('products.blockletStore.description'),
|
251
281
|
icon: <BlockletStoreSvg />,
|
252
282
|
},
|
253
283
|
{
|
254
|
-
label:
|
284
|
+
label: (
|
285
|
+
<Link to={`https://www.web3kit.rocks/${locale}`} target="_blank" rel="noreferrer noopener">
|
286
|
+
Web3 Kit
|
287
|
+
</Link>
|
288
|
+
),
|
255
289
|
description: t('products.web3Kit.description'),
|
256
290
|
icon: <Web3KitSvg />,
|
257
291
|
},
|
@@ -265,18 +299,31 @@ export default function Products({ className, isOpen, ...rest }: ProductsProps)
|
|
265
299
|
[
|
266
300
|
{
|
267
301
|
label: (
|
268
|
-
<Link
|
302
|
+
<Link
|
303
|
+
to={`https://www.arcblock.io/content/collections/${locale}/blocklet`}
|
304
|
+
target="_blank"
|
305
|
+
rel="noreferrer noopener">
|
306
|
+
Blocklet Framework
|
307
|
+
</Link>
|
269
308
|
),
|
270
309
|
description: t('products.blockletFramework.description'),
|
271
310
|
icon: <BlockletFrameworkSvg />,
|
272
311
|
},
|
273
312
|
{
|
274
|
-
label:
|
313
|
+
label: (
|
314
|
+
<Link to={`https://www.didspaces.com/${locale}`} target="_blank" rel="noreferrer noopener">
|
315
|
+
DID Spaces
|
316
|
+
</Link>
|
317
|
+
),
|
275
318
|
description: t('products.didSpaces.description'),
|
276
319
|
icon: <DidSvg />,
|
277
320
|
},
|
278
321
|
{
|
279
|
-
label:
|
322
|
+
label: (
|
323
|
+
<Link to={`https://main.abtnetwork.io/${locale}`} target="_blank" rel="noreferrer noopener">
|
324
|
+
ABT Network
|
325
|
+
</Link>
|
326
|
+
),
|
280
327
|
description: t('products.abtNetwork.description'),
|
281
328
|
icon: <AbtNetworkSvg />,
|
282
329
|
},
|
@@ -284,7 +331,10 @@ export default function Products({ className, isOpen, ...rest }: ProductsProps)
|
|
284
331
|
[
|
285
332
|
{
|
286
333
|
label: (
|
287
|
-
<Link
|
334
|
+
<Link
|
335
|
+
to={`https://www.arcblock.io/content/collections/${locale}/blocklet-server`}
|
336
|
+
target="_blank"
|
337
|
+
rel="noreferrer noopener">
|
288
338
|
Blocklet Server
|
289
339
|
</Link>
|
290
340
|
),
|
@@ -292,8 +342,15 @@ export default function Products({ className, isOpen, ...rest }: ProductsProps)
|
|
292
342
|
icon: <BlockletServerSvg />,
|
293
343
|
},
|
294
344
|
{
|
295
|
-
label:
|
296
|
-
|
345
|
+
label: (
|
346
|
+
<Link
|
347
|
+
to={`https://www.arcblock.io/content/collections/${locale}/blockchain`}
|
348
|
+
target="_blank"
|
349
|
+
rel="noreferrer noopener">
|
350
|
+
ОСАР
|
351
|
+
</Link>
|
352
|
+
),
|
353
|
+
description: t('products.ocap.description'),
|
297
354
|
icon: <OCAPSvg />,
|
298
355
|
},
|
299
356
|
],
|
@@ -305,29 +362,55 @@ export default function Products({ className, isOpen, ...rest }: ProductsProps)
|
|
305
362
|
children: [
|
306
363
|
[
|
307
364
|
{
|
308
|
-
label:
|
365
|
+
label: (
|
366
|
+
<Link
|
367
|
+
to={`https://www.arcblock.io/content/collections/${locale}/did`}
|
368
|
+
target="_blank"
|
369
|
+
rel="noreferrer noopener">
|
370
|
+
DID
|
371
|
+
</Link>
|
372
|
+
),
|
309
373
|
description: t('products.did.description'),
|
310
374
|
icon: <DidSvg />,
|
311
375
|
},
|
312
376
|
{
|
313
|
-
label:
|
377
|
+
label: (
|
378
|
+
<Link to={`https://www.didwallet.io/${locale}`} target="_blank" rel="noreferrer noopener">
|
379
|
+
DID Wallet
|
380
|
+
</Link>
|
381
|
+
),
|
314
382
|
description: t('products.didWallet.description'),
|
315
383
|
icon: <DidWalletSvg />,
|
316
384
|
},
|
317
385
|
{
|
318
|
-
label:
|
386
|
+
label: (
|
387
|
+
<Link to={`https://www.didnames.io/${locale}`} target="_blank" rel="noreferrer noopener">
|
388
|
+
DID Names
|
389
|
+
</Link>
|
390
|
+
),
|
319
391
|
description: t('products.didNameService.description'),
|
320
392
|
icon: <DidNameServiceSvg />,
|
321
393
|
},
|
322
394
|
],
|
323
395
|
[
|
324
396
|
{
|
325
|
-
label:
|
397
|
+
label: (
|
398
|
+
<Link
|
399
|
+
to={`https://www.arcblock.io/content/collections/${locale}/verifiable-credential`}
|
400
|
+
target="_blank"
|
401
|
+
rel="noreferrer noopener">
|
402
|
+
VC
|
403
|
+
</Link>
|
404
|
+
),
|
326
405
|
description: t('products.vc.description'),
|
327
406
|
icon: <VCSvg />,
|
328
407
|
},
|
329
408
|
{
|
330
|
-
label:
|
409
|
+
label: (
|
410
|
+
<Link to={`https://www.didconnect.io/${locale}`} target="_blank" rel="noreferrer noopener">
|
411
|
+
DID Connect
|
412
|
+
</Link>
|
413
|
+
),
|
331
414
|
description: t('products.didConnect.description'),
|
332
415
|
icon: <DidConnectSvg />,
|
333
416
|
},
|
@@ -337,27 +420,6 @@ export default function Products({ className, isOpen, ...rest }: ProductsProps)
|
|
337
420
|
];
|
338
421
|
}, [t, locale]);
|
339
422
|
|
340
|
-
// 防止弹框超出 window
|
341
|
-
useLayoutEffect(() => {
|
342
|
-
const wrapper = wrapperRef.current;
|
343
|
-
if (!wrapper) return;
|
344
|
-
if (!isOpen) {
|
345
|
-
wrapper.style.transform = '';
|
346
|
-
return;
|
347
|
-
}
|
348
|
-
|
349
|
-
const rect = wrapper.getBoundingClientRect();
|
350
|
-
const windowWidth = window.innerWidth;
|
351
|
-
if (rect.right > windowWidth) {
|
352
|
-
const offset = rect.right - windowWidth;
|
353
|
-
wrapper.style.transform = `translateX(-${offset + 16}px)`;
|
354
|
-
} else if (rect.left < 0) {
|
355
|
-
wrapper.style.transform = `translateX(${Math.abs(rect.left) + 16}px)`;
|
356
|
-
} else {
|
357
|
-
wrapper.style.transform = '';
|
358
|
-
}
|
359
|
-
}, [width, height, isOpen]);
|
360
|
-
|
361
423
|
return (
|
362
424
|
<Wrapper ref={wrapperRef} className={`is-${mode} ${className}`} {...rest}>
|
363
425
|
{groups.map((group) => (
|