@arcblock/ux 2.10.2 → 2.10.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,5 @@
1
1
  import PropTypes from 'prop-types';
2
- import Box from '@mui/material/Box';
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",
@@ -1,10 +1,8 @@
1
- import { Children, cloneElement, useEffect, createContext, useContext, useMemo, useState, useRef, useCallback, forwardRef } from 'react';
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/MoreHoriz';
5
- import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
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 [state, setState] = useState({
43
+ const currentState = useReactive({
46
44
  activeId,
47
- openedIds: []
45
+ openedIds: [],
46
+ hiddenItemCount: 0
48
47
  });
49
- const activate = useCallback(id => {
50
- setState(prev => ({
51
- ...prev,
52
- activeId: id
53
- }));
48
+ const activate = useMemoizedFn(id => {
49
+ currentState.activeId = id;
54
50
  onSelected?.(id);
55
- }, [onSelected]);
56
- const open = useCallback(id => {
57
- setState(prev => ({
58
- ...prev,
59
- openedIds: [...prev.openedIds, id]
60
- }));
61
- }, []);
62
- const close = useCallback(id => {
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
- ...state,
60
+ ...currentState,
71
61
  mode,
72
62
  activate,
73
63
  open,
74
64
  close
75
65
  };
76
- }, [state, mode, activate, open, close]);
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 checkItemsFit = useMemoizedFn(() => {
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 = navMenuRef.current?.offsetWidth || 0;
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
- setHiddenItemCount(newHiddenCount);
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
- setState(prev => ({
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: "",
@@ -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
- flex-grow: 1;
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.2",
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": "01c29cd905a2baf029d7be25d41c79a261ebd959",
57
+ "gitHead": "57248b30b2ad697026249b768894c44345c37ed3",
58
58
  "dependencies": {
59
59
  "@arcblock/did-motif": "^1.1.13",
60
- "@arcblock/icons": "^2.10.2",
61
- "@arcblock/nft-display": "^2.10.2",
62
- "@arcblock/react-hooks": "^2.10.2",
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",
@@ -1,6 +1,5 @@
1
1
  import PropTypes from 'prop-types';
2
- import Box from '@mui/material/Box';
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/MoreHoriz';
16
- import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
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
- const [state, setState] = useState({ activeId, openedIds: [] });
47
-
48
- const activate = useCallback(
49
- (id) => {
50
- setState((prev) => ({ ...prev, activeId: id }));
51
- onSelected?.(id);
52
- },
53
- [onSelected]
54
- );
55
- const open = useCallback((id) => {
56
- setState((prev) => ({ ...prev, openedIds: [...prev.openedIds, id] }));
57
- }, []);
58
- const close = useCallback((id) => {
59
- setState((prev) => ({ ...prev, openedIds: prev.openedIds.filter((item) => item !== id) }));
60
- }, []);
61
-
62
- const contextValue = useMemo(() => {
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
- ...state,
53
+ ...currentState,
65
54
  mode,
66
55
  activate,
67
56
  open,
68
57
  close,
69
58
  };
70
- }, [state, mode, activate, open, close]);
71
- const [hiddenItemCount, setHiddenItemCount] = useState(0);
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
- const checkItemsFit = useMemoizedFn(() => {
89
- let totalWidthUsed = 0;
90
- let newHiddenCount = 0;
91
- let leftAllHidden = false;
92
- const containerWidth = navMenuRef.current?.offsetWidth || 0;
93
- const moreIconWidth = moreIconRef.current
94
- ? moreIconRef.current.offsetWidth + parseFloat(window.getComputedStyle(moreIconRef.current).marginLeft)
95
- : 0;
96
-
97
- itemRefs.current.forEach((item, index) => {
98
- if (item) {
99
- item.style.display = 'flex';
100
- const marginLeft = index > 0 ? parseFloat(window.getComputedStyle(item).marginLeft) : 0;
101
- const currentItemWidth = item.offsetWidth + marginLeft;
102
-
103
- if (containerWidth - moreIconWidth >= totalWidthUsed + currentItemWidth && !leftAllHidden) {
104
- totalWidthUsed += currentItemWidth;
105
- } else {
106
- item.style.display = 'none';
107
- leftAllHidden = true;
108
- newHiddenCount++;
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
- if (newHiddenCount !== hiddenItemCount) {
114
- setHiddenItemCount(newHiddenCount);
115
- }
116
- }, [hiddenItemCount]);
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
- setState((prev) => ({ ...prev, activeId }));
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>
@@ -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
- flex-grow: 1;
85
+ // FIXME: @zhanghan 这个只是临时的解决方案,会导致 header align right 不能真正的右对齐,需要修改 header 才能真正解决这个问题
86
+ flex-grow: 100;
85
87
 
86
88
  .navmenu-root {
87
89
  display: flex;