@arcblock/ux 2.10.3 → 2.10.4
Sign up to get free protection for your applications and to get access to all the features.
package/lib/Header/header.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import PropTypes from 'prop-types';
|
2
|
-
import Box from '@mui/material
|
3
|
-
import Container from '@mui/material/Container';
|
2
|
+
import { Box, Container } from '@mui/material';
|
4
3
|
import { useRef, useState, useEffect } from 'react';
|
5
4
|
import AutoHidden from './auto-hidden';
|
6
5
|
import { styled } from '../Theme';
|
@@ -83,8 +82,10 @@ function Header({
|
|
83
82
|
className: "header-brand-addon",
|
84
83
|
children: brandAddon
|
85
84
|
}), align === 'right' && /*#__PURE__*/_jsx(Box, {
|
85
|
+
display: "inline-block",
|
86
86
|
flexGrow: 1
|
87
87
|
}), children, align === 'left' && /*#__PURE__*/_jsx(Box, {
|
88
|
+
display: "inline-block",
|
88
89
|
flexGrow: 1
|
89
90
|
}), /*#__PURE__*/_jsx("div", {
|
90
91
|
className: "header-addons",
|
package/lib/NavMenu/nav-menu.js
CHANGED
@@ -1,10 +1,8 @@
|
|
1
|
-
import { Children, cloneElement, useEffect, createContext, useContext,
|
1
|
+
import { Children, cloneElement, useEffect, createContext, useContext, useRef, forwardRef } from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import clsx from 'clsx';
|
4
|
-
import MoreHorizIcon from '@mui/icons-material
|
5
|
-
import
|
6
|
-
import MenuIcon from '@mui/icons-material/Menu';
|
7
|
-
import { useMemoizedFn } from 'ahooks';
|
4
|
+
import { MoreHoriz as MoreHorizIcon, ExpandMore as ExpandMoreIcon, Menu as MenuIcon } from '@mui/icons-material';
|
5
|
+
import { useCreation, useMemoizedFn, useReactive, useSize, useThrottleFn } from 'ahooks';
|
8
6
|
import { HorizontalStyle, InlineStyle } from './style';
|
9
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
10
8
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
@@ -42,47 +40,40 @@ function NavMenu({
|
|
42
40
|
if (!items?.length && !children?.length) {
|
43
41
|
throw new Error("One of 'items' or 'children' is required by NavMenu component.");
|
44
42
|
}
|
45
|
-
const
|
43
|
+
const currentState = useReactive({
|
46
44
|
activeId,
|
47
|
-
openedIds: []
|
45
|
+
openedIds: [],
|
46
|
+
hiddenItemCount: 0
|
48
47
|
});
|
49
|
-
const activate =
|
50
|
-
|
51
|
-
...prev,
|
52
|
-
activeId: id
|
53
|
-
}));
|
48
|
+
const activate = useMemoizedFn(id => {
|
49
|
+
currentState.activeId = id;
|
54
50
|
onSelected?.(id);
|
55
|
-
}
|
56
|
-
const open =
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
}
|
62
|
-
const
|
63
|
-
setState(prev => ({
|
64
|
-
...prev,
|
65
|
-
openedIds: prev.openedIds.filter(item => item !== id)
|
66
|
-
}));
|
67
|
-
}, []);
|
68
|
-
const contextValue = useMemo(() => {
|
51
|
+
});
|
52
|
+
const open = useMemoizedFn(id => {
|
53
|
+
currentState.openedIds.push(id);
|
54
|
+
});
|
55
|
+
const close = useMemoizedFn(id => {
|
56
|
+
currentState.openedIds = currentState.openedIds.filter(item => item !== id);
|
57
|
+
});
|
58
|
+
const contextValue = useCreation(() => {
|
69
59
|
return {
|
70
|
-
...
|
60
|
+
...currentState,
|
71
61
|
mode,
|
72
62
|
activate,
|
73
63
|
open,
|
74
64
|
close
|
75
65
|
};
|
76
|
-
}, [
|
77
|
-
const [hiddenItemCount, setHiddenItemCount] = useState(0);
|
66
|
+
}, [currentState, mode, activate, open, close]);
|
78
67
|
const navMenuRef = useRef();
|
79
68
|
const itemRefs = useRef([]);
|
80
69
|
const moreIconRef = useRef();
|
81
|
-
const isAllItemsHidden = hiddenItemCount === itemRefs.current?.length;
|
82
|
-
const icon = isAllItemsHidden ? /*#__PURE__*/_jsx(MenuIcon, {}) : /*#__PURE__*/_jsx(MoreHorizIcon, {});
|
70
|
+
const isAllItemsHidden = currentState.hiddenItemCount === itemRefs.current?.length;
|
83
71
|
const style = isAllItemsHidden ? {
|
84
72
|
marginLeft: '0px'
|
85
73
|
} : undefined;
|
74
|
+
const icon = useCreation(() => {
|
75
|
+
return isAllItemsHidden ? /*#__PURE__*/_jsx(MenuIcon, {}) : /*#__PURE__*/_jsx(MoreHorizIcon, {});
|
76
|
+
}, [isAllItemsHidden]);
|
86
77
|
const renderChildrenWithRef = childrenElement => {
|
87
78
|
return Children.map(childrenElement, (child, index) => {
|
88
79
|
return /*#__PURE__*/cloneElement(child, {
|
@@ -92,11 +83,14 @@ function NavMenu({
|
|
92
83
|
});
|
93
84
|
});
|
94
85
|
};
|
95
|
-
const
|
86
|
+
const containerSize = useSize(navMenuRef.current);
|
87
|
+
const {
|
88
|
+
run: checkItemsFit
|
89
|
+
} = useThrottleFn(useMemoizedFn(() => {
|
96
90
|
let totalWidthUsed = 0;
|
97
91
|
let newHiddenCount = 0;
|
98
92
|
let leftAllHidden = false;
|
99
|
-
const containerWidth =
|
93
|
+
const containerWidth = containerSize?.width || 0;
|
100
94
|
const moreIconWidth = moreIconRef.current ? moreIconRef.current.offsetWidth + parseFloat(window.getComputedStyle(moreIconRef.current).marginLeft) : 0;
|
101
95
|
itemRefs.current.forEach((item, index) => {
|
102
96
|
if (item) {
|
@@ -112,10 +106,12 @@ function NavMenu({
|
|
112
106
|
}
|
113
107
|
}
|
114
108
|
});
|
115
|
-
if (newHiddenCount !== hiddenItemCount) {
|
116
|
-
|
109
|
+
if (newHiddenCount !== currentState.hiddenItemCount) {
|
110
|
+
currentState.hiddenItemCount = newHiddenCount;
|
117
111
|
}
|
118
|
-
}, [hiddenItemCount])
|
112
|
+
}, [currentState.hiddenItemCount, containerSize?.width]), {
|
113
|
+
wait: 100
|
114
|
+
});
|
119
115
|
useEffect(() => {
|
120
116
|
if (mode === 'horizontal') {
|
121
117
|
checkItemsFit();
|
@@ -126,16 +122,14 @@ function NavMenu({
|
|
126
122
|
}
|
127
123
|
return undefined;
|
128
124
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
129
|
-
}, [mode]);
|
125
|
+
}, [mode, items]);
|
130
126
|
useEffect(() => {
|
131
127
|
// NavMenu#activeId 和 Item#active prop 都可以用来控制激活状态 (一般不会混用这两种方式)
|
132
128
|
// 如果未传入 NavMenu#activeId, 应该避免设置一个空值的 activeId 状态 (会与 Item#active 冲突)
|
133
129
|
if (activeId !== undefined && activeId !== null) {
|
134
|
-
|
135
|
-
...prev,
|
136
|
-
activeId
|
137
|
-
}));
|
130
|
+
currentState.activeId = activeId;
|
138
131
|
}
|
132
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
139
133
|
}, [activeId]);
|
140
134
|
const classes = clsx('navmenu', `navmenu--${mode}`, rest.className);
|
141
135
|
const renderItem = (item, index, isTopLevel = false) => {
|
@@ -163,7 +157,7 @@ function NavMenu({
|
|
163
157
|
} : undefined
|
164
158
|
}, index);
|
165
159
|
};
|
166
|
-
const content = items ? items?.slice(-hiddenItemCount).map((item, index) => renderItem(item, index)) : children?.slice(-hiddenItemCount);
|
160
|
+
const content = items ? items?.slice(-currentState.hiddenItemCount).map((item, index) => renderItem(item, index)) : children?.slice(-currentState.hiddenItemCount);
|
167
161
|
const StyledRoot = mode === 'inline' ? InlineStyle : HorizontalStyle;
|
168
162
|
return /*#__PURE__*/_jsx(NavMenuContext.Provider, {
|
169
163
|
value: contextValue,
|
@@ -176,7 +170,7 @@ function NavMenu({
|
|
176
170
|
children: /*#__PURE__*/_jsxs("ul", {
|
177
171
|
className: "navmenu-root",
|
178
172
|
ref: navMenuRef,
|
179
|
-
children: [items ? items.map((item, index) => renderItem(item, index, true)) : renderChildrenWithRef(children), hiddenItemCount > 0 && /*#__PURE__*/_jsx(Sub, {
|
173
|
+
children: [items ? items.map((item, index) => renderItem(item, index, true)) : renderChildrenWithRef(children), currentState.hiddenItemCount > 0 && /*#__PURE__*/_jsx(Sub, {
|
180
174
|
expandIcon: false,
|
181
175
|
icon: icon,
|
182
176
|
label: "",
|
package/lib/NavMenu/style.js
CHANGED
@@ -2,6 +2,7 @@ import { styled } from '../Theme';
|
|
2
2
|
const NavMenuBase = styled('nav')`
|
3
3
|
background-color: ${props => props.$bgColor};
|
4
4
|
font-size: 16px;
|
5
|
+
// width: 100%;
|
5
6
|
ul {
|
6
7
|
list-style: none;
|
7
8
|
margin: 0;
|
@@ -79,7 +80,8 @@ const NavMenuBase = styled('nav')`
|
|
79
80
|
export const HorizontalStyle = styled(NavMenuBase)`
|
80
81
|
padding: 8px 16px;
|
81
82
|
min-width: 50px;
|
82
|
-
|
83
|
+
// FIXME: @zhanghan 这个只是临时的解决方案,会导致 header align right 不能真正的右对齐,需要修改 header 才能真正解决这个问题
|
84
|
+
flex-grow: 100;
|
83
85
|
|
84
86
|
.navmenu-root {
|
85
87
|
display: flex;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@arcblock/ux",
|
3
|
-
"version": "2.10.
|
3
|
+
"version": "2.10.4",
|
4
4
|
"description": "Common used react components for arcblock products",
|
5
5
|
"keywords": [
|
6
6
|
"react",
|
@@ -54,12 +54,12 @@
|
|
54
54
|
"react": ">=18.2.0",
|
55
55
|
"react-router-dom": ">=6.22.3"
|
56
56
|
},
|
57
|
-
"gitHead": "
|
57
|
+
"gitHead": "57248b30b2ad697026249b768894c44345c37ed3",
|
58
58
|
"dependencies": {
|
59
59
|
"@arcblock/did-motif": "^1.1.13",
|
60
|
-
"@arcblock/icons": "^2.10.
|
61
|
-
"@arcblock/nft-display": "^2.10.
|
62
|
-
"@arcblock/react-hooks": "^2.10.
|
60
|
+
"@arcblock/icons": "^2.10.4",
|
61
|
+
"@arcblock/nft-display": "^2.10.4",
|
62
|
+
"@arcblock/react-hooks": "^2.10.4",
|
63
63
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
64
64
|
"@fontsource/inter": "^5.0.16",
|
65
65
|
"@fontsource/ubuntu-mono": "^5.0.18",
|
package/src/Header/header.jsx
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import PropTypes from 'prop-types';
|
2
|
-
import Box from '@mui/material
|
3
|
-
import Container from '@mui/material/Container';
|
2
|
+
import { Box, Container } from '@mui/material';
|
4
3
|
import { useRef, useState, useEffect } from 'react';
|
5
4
|
import AutoHidden from './auto-hidden';
|
6
5
|
import { styled } from '../Theme';
|
@@ -65,9 +64,9 @@ function Header({
|
|
65
64
|
{renderBrand()}
|
66
65
|
</div>
|
67
66
|
<div className="header-brand-addon">{brandAddon}</div>
|
68
|
-
{align === 'right' && <Box flexGrow={1} />}
|
67
|
+
{align === 'right' && <Box display="inline-block" flexGrow={1} />}
|
69
68
|
{children}
|
70
|
-
{align === 'left' && <Box flexGrow={1} />}
|
69
|
+
{align === 'left' && <Box display="inline-block" flexGrow={1} />}
|
71
70
|
<div className="header-addons">{addons}</div>
|
72
71
|
</Container>
|
73
72
|
</Root>
|
@@ -1,21 +1,8 @@
|
|
1
|
-
import {
|
2
|
-
Children,
|
3
|
-
cloneElement,
|
4
|
-
useEffect,
|
5
|
-
createContext,
|
6
|
-
useContext,
|
7
|
-
useMemo,
|
8
|
-
useState,
|
9
|
-
useRef,
|
10
|
-
useCallback,
|
11
|
-
forwardRef,
|
12
|
-
} from 'react';
|
1
|
+
import { Children, cloneElement, useEffect, createContext, useContext, useRef, forwardRef } from 'react';
|
13
2
|
import PropTypes from 'prop-types';
|
14
3
|
import clsx from 'clsx';
|
15
|
-
import MoreHorizIcon from '@mui/icons-material
|
16
|
-
import
|
17
|
-
import MenuIcon from '@mui/icons-material/Menu';
|
18
|
-
import { useMemoizedFn } from 'ahooks';
|
4
|
+
import { MoreHoriz as MoreHorizIcon, ExpandMore as ExpandMoreIcon, Menu as MenuIcon } from '@mui/icons-material';
|
5
|
+
import { useCreation, useMemoizedFn, useReactive, useSize, useThrottleFn } from 'ahooks';
|
19
6
|
import { HorizontalStyle, InlineStyle } from './style';
|
20
7
|
|
21
8
|
const NavMenuContext = createContext();
|
@@ -43,39 +30,44 @@ function NavMenu({ items, mode, children, activeId, textColor, activeTextColor,
|
|
43
30
|
if (!items?.length && !children?.length) {
|
44
31
|
throw new Error("One of 'items' or 'children' is required by NavMenu component.");
|
45
32
|
}
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
33
|
+
|
34
|
+
const currentState = useReactive({
|
35
|
+
activeId,
|
36
|
+
openedIds: [],
|
37
|
+
hiddenItemCount: 0,
|
38
|
+
});
|
39
|
+
|
40
|
+
const activate = useMemoizedFn((id) => {
|
41
|
+
currentState.activeId = id;
|
42
|
+
onSelected?.(id);
|
43
|
+
});
|
44
|
+
const open = useMemoizedFn((id) => {
|
45
|
+
currentState.openedIds.push(id);
|
46
|
+
});
|
47
|
+
const close = useMemoizedFn((id) => {
|
48
|
+
currentState.openedIds = currentState.openedIds.filter((item) => item !== id);
|
49
|
+
});
|
50
|
+
|
51
|
+
const contextValue = useCreation(() => {
|
63
52
|
return {
|
64
|
-
...
|
53
|
+
...currentState,
|
65
54
|
mode,
|
66
55
|
activate,
|
67
56
|
open,
|
68
57
|
close,
|
69
58
|
};
|
70
|
-
}, [
|
71
|
-
|
59
|
+
}, [currentState, mode, activate, open, close]);
|
60
|
+
|
72
61
|
const navMenuRef = useRef();
|
73
62
|
const itemRefs = useRef([]);
|
74
63
|
const moreIconRef = useRef();
|
75
|
-
const isAllItemsHidden = hiddenItemCount === itemRefs.current?.length;
|
76
|
-
const icon = isAllItemsHidden ? <MenuIcon /> : <MoreHorizIcon />;
|
64
|
+
const isAllItemsHidden = currentState.hiddenItemCount === itemRefs.current?.length;
|
77
65
|
const style = isAllItemsHidden ? { marginLeft: '0px' } : undefined;
|
78
66
|
|
67
|
+
const icon = useCreation(() => {
|
68
|
+
return isAllItemsHidden ? <MenuIcon /> : <MoreHorizIcon />;
|
69
|
+
}, [isAllItemsHidden]);
|
70
|
+
|
79
71
|
const renderChildrenWithRef = (childrenElement) => {
|
80
72
|
return Children.map(childrenElement, (child, index) => {
|
81
73
|
return cloneElement(child, {
|
@@ -85,35 +77,41 @@ function NavMenu({ items, mode, children, activeId, textColor, activeTextColor,
|
|
85
77
|
});
|
86
78
|
});
|
87
79
|
};
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
leftAllHidden
|
108
|
-
|
80
|
+
|
81
|
+
const containerSize = useSize(navMenuRef.current);
|
82
|
+
|
83
|
+
const { run: checkItemsFit } = useThrottleFn(
|
84
|
+
useMemoizedFn(() => {
|
85
|
+
let totalWidthUsed = 0;
|
86
|
+
let newHiddenCount = 0;
|
87
|
+
let leftAllHidden = false;
|
88
|
+
const containerWidth = containerSize?.width || 0;
|
89
|
+
const moreIconWidth = moreIconRef.current
|
90
|
+
? moreIconRef.current.offsetWidth + parseFloat(window.getComputedStyle(moreIconRef.current).marginLeft)
|
91
|
+
: 0;
|
92
|
+
|
93
|
+
itemRefs.current.forEach((item, index) => {
|
94
|
+
if (item) {
|
95
|
+
item.style.display = 'flex';
|
96
|
+
const marginLeft = index > 0 ? parseFloat(window.getComputedStyle(item).marginLeft) : 0;
|
97
|
+
const currentItemWidth = item.offsetWidth + marginLeft;
|
98
|
+
|
99
|
+
if (containerWidth - moreIconWidth >= totalWidthUsed + currentItemWidth && !leftAllHidden) {
|
100
|
+
totalWidthUsed += currentItemWidth;
|
101
|
+
} else {
|
102
|
+
item.style.display = 'none';
|
103
|
+
leftAllHidden = true;
|
104
|
+
newHiddenCount++;
|
105
|
+
}
|
109
106
|
}
|
110
|
-
}
|
111
|
-
});
|
107
|
+
});
|
112
108
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
109
|
+
if (newHiddenCount !== currentState.hiddenItemCount) {
|
110
|
+
currentState.hiddenItemCount = newHiddenCount;
|
111
|
+
}
|
112
|
+
}, [currentState.hiddenItemCount, containerSize?.width]),
|
113
|
+
{ wait: 100 }
|
114
|
+
);
|
117
115
|
|
118
116
|
useEffect(() => {
|
119
117
|
if (mode === 'horizontal') {
|
@@ -126,14 +124,15 @@ function NavMenu({ items, mode, children, activeId, textColor, activeTextColor,
|
|
126
124
|
}
|
127
125
|
return undefined;
|
128
126
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
129
|
-
}, [mode]);
|
127
|
+
}, [mode, items]);
|
130
128
|
|
131
129
|
useEffect(() => {
|
132
130
|
// NavMenu#activeId 和 Item#active prop 都可以用来控制激活状态 (一般不会混用这两种方式)
|
133
131
|
// 如果未传入 NavMenu#activeId, 应该避免设置一个空值的 activeId 状态 (会与 Item#active 冲突)
|
134
132
|
if (activeId !== undefined && activeId !== null) {
|
135
|
-
|
133
|
+
currentState.activeId = activeId;
|
136
134
|
}
|
135
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
137
136
|
}, [activeId]);
|
138
137
|
|
139
138
|
const classes = clsx('navmenu', `navmenu--${mode}`, rest.className);
|
@@ -179,8 +178,8 @@ function NavMenu({ items, mode, children, activeId, textColor, activeTextColor,
|
|
179
178
|
};
|
180
179
|
|
181
180
|
const content = items
|
182
|
-
? items?.slice(-hiddenItemCount).map((item, index) => renderItem(item, index))
|
183
|
-
: children?.slice(-hiddenItemCount);
|
181
|
+
? items?.slice(-currentState.hiddenItemCount).map((item, index) => renderItem(item, index))
|
182
|
+
: children?.slice(-currentState.hiddenItemCount);
|
184
183
|
|
185
184
|
const StyledRoot = mode === 'inline' ? InlineStyle : HorizontalStyle;
|
186
185
|
|
@@ -194,7 +193,7 @@ function NavMenu({ items, mode, children, activeId, textColor, activeTextColor,
|
|
194
193
|
$bgColor={bgColor}>
|
195
194
|
<ul className="navmenu-root" ref={navMenuRef}>
|
196
195
|
{items ? items.map((item, index) => renderItem(item, index, true)) : renderChildrenWithRef(children)}
|
197
|
-
{hiddenItemCount > 0 && (
|
196
|
+
{currentState.hiddenItemCount > 0 && (
|
198
197
|
<Sub expandIcon={false} icon={icon} label="" ref={moreIconRef} style={style}>
|
199
198
|
{content}
|
200
199
|
</Sub>
|
package/src/NavMenu/style.js
CHANGED
@@ -3,6 +3,7 @@ import { styled } from '../Theme';
|
|
3
3
|
const NavMenuBase = styled('nav')`
|
4
4
|
background-color: ${(props) => props.$bgColor};
|
5
5
|
font-size: 16px;
|
6
|
+
// width: 100%;
|
6
7
|
ul {
|
7
8
|
list-style: none;
|
8
9
|
margin: 0;
|
@@ -81,7 +82,8 @@ const NavMenuBase = styled('nav')`
|
|
81
82
|
export const HorizontalStyle = styled(NavMenuBase)`
|
82
83
|
padding: 8px 16px;
|
83
84
|
min-width: 50px;
|
84
|
-
|
85
|
+
// FIXME: @zhanghan 这个只是临时的解决方案,会导致 header align right 不能真正的右对齐,需要修改 header 才能真正解决这个问题
|
86
|
+
flex-grow: 100;
|
85
87
|
|
86
88
|
.navmenu-root {
|
87
89
|
display: flex;
|