@arcblock/ux 2.10.2 → 2.10.4

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.
@@ -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;