@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.
@@ -5,11 +5,12 @@ type NavMenuProps = {
5
5
  $textColor: string;
6
6
  };
7
7
 
8
- // .navmenu-root
9
- export const NavMenuRoot = styled('nav', {
8
+ // .navmenu
9
+ export const NavMenuStyled = styled('nav', {
10
10
  shouldForwardProp: (prop) => prop !== '$bgColor' && prop !== '$textColor',
11
11
  })<NavMenuProps>(({ $bgColor, $textColor }) => ({
12
- padding: '8px 16px',
12
+ position: 'relative',
13
+ padding: '0 16px',
13
14
  minWidth: '50px',
14
15
  // FIXME: @zhanghan 这个只是临时的解决方案,会导致 header align right 不能真正的右对齐,需要修改 header 才能真正解决这个问题
15
16
  flexGrow: 100,
@@ -18,15 +19,15 @@ export const NavMenuRoot = styled('nav', {
18
19
  fontSize: '16px',
19
20
  }));
20
21
 
21
- // .navmenu-list
22
- export const NavMenuList = styled('ul')(() => ({
22
+ // .navmenu-root
23
+ export const NavMenuRoot = styled('ul')(() => ({
23
24
  listStyle: 'none',
24
25
  margin: 0,
25
26
  padding: 0,
26
27
  display: 'flex',
27
28
  alignItems: 'center',
28
29
  // inline 布局
29
- '&.navmenu-list--inline ': {
30
+ '&.navmenu-root--inline ': {
30
31
  flexDirection: 'column',
31
32
  alignItems: 'stretch',
32
33
  },
@@ -38,14 +39,16 @@ type NavMenuItemProps = {
38
39
  // 菜单项 .navmenu-item
39
40
  export const NavMenuItem = styled('li', {
40
41
  shouldForwardProp: (prop) => prop !== '$activeTextColor',
41
- })<NavMenuItemProps>(({ $activeTextColor }) => ({
42
+ })<NavMenuItemProps>(({ $activeTextColor, theme }) => ({
42
43
  display: 'flex',
43
44
  alignItems: 'center',
44
45
  position: 'relative',
45
- padding: '0 12px',
46
+ padding: '8px 12px',
46
47
  whiteSpace: 'nowrap',
47
48
  cursor: 'pointer',
48
- transition: 'color 0.2s ease-in-out',
49
+ transition: theme.transitions.create('color', {
50
+ duration: theme.transitions.duration.standard,
51
+ }),
49
52
  // 间距调整
50
53
  '&:first-of-type': {
51
54
  paddingLeft: 0,
@@ -98,7 +101,9 @@ export const NavMenuItem = styled('li', {
98
101
  marginLeft: '6px',
99
102
  fontSize: '14px',
100
103
  opacity: 0,
101
- transition: 'opacity 0.2s ease-in-out',
104
+ transition: theme.transitions.create('opacity', {
105
+ duration: theme.transitions.duration.standard,
106
+ }),
102
107
  },
103
108
  },
104
109
  '.navmenu-item__desc': {
@@ -136,13 +141,17 @@ export const NavMenuItem = styled('li', {
136
141
  },
137
142
  '&:hover': {
138
143
  background: '#f9f9fb',
139
- transition: 'background 0.2s ease-in-out',
144
+ transition: theme.transitions.create('background', {
145
+ duration: theme.transitions.duration.standard,
146
+ }),
140
147
  '.navmenu-item__label-arrow': {
141
148
  opacity: 1,
142
149
  },
143
150
  '.navmenu-item__desc': {
144
151
  color: '#26292e',
145
- transition: 'color 0.2s ease-in-out',
152
+ transition: theme.transitions.create('color', {
153
+ duration: theme.transitions.duration.standard,
154
+ }),
146
155
  },
147
156
  },
148
157
  '&.navmenu-item--active': {
@@ -160,24 +169,26 @@ export const NavMenuItem = styled('li', {
160
169
  }));
161
170
 
162
171
  // 包含子菜单的导航项 .navmenu-item .navmenu-sub
163
- export const NavMenuSub = styled(NavMenuItem)(() => ({
172
+ export const NavMenuSub = styled(NavMenuItem)(({ theme }) => ({
164
173
  '.navmenu-item': {
165
174
  marginLeft: 0,
175
+ overflow: 'hidden',
166
176
  },
167
177
  '& .navmenu-sub__container': {
168
- visibility: 'hidden',
178
+ pointerEvents: 'none',
169
179
  opacity: 0,
170
180
  position: 'absolute',
171
181
  top: '100%',
172
- left: '50%',
182
+ left: 0,
173
183
  zIndex: 1,
174
- transform: 'translateX(-50%)',
175
- paddingTop: '16px',
176
- transition: 'opacity 0.2s ease-in-out, visibility 0.2s ease-in-out',
177
184
  },
178
185
  '&.navmenu-sub--opened > .navmenu-sub__container': {
179
- visibility: 'visible',
186
+ pointerEvents: 'auto',
180
187
  opacity: 1,
188
+ transition: theme.transitions.create('opacity', {
189
+ duration: theme.transitions.duration.standard,
190
+ easing: theme.transitions.easing.easeOut,
191
+ }),
181
192
  },
182
193
  // 扩展图标
183
194
  '.navmenu-sub__expand-icon': {
@@ -188,7 +199,9 @@ export const NavMenuSub = styled(NavMenuItem)(() => ({
188
199
  '& > *': {
189
200
  width: '0.8em',
190
201
  height: '0.8em',
191
- transition: 'transform 0.2s ease-in-out',
202
+ transition: theme.transitions.create('transform', {
203
+ duration: theme.transitions.duration.standard,
204
+ }),
192
205
  },
193
206
  '.navmenu-item--inline &': {
194
207
  marginLeft: 'auto',
@@ -204,7 +217,9 @@ export const NavMenuSub = styled(NavMenuItem)(() => ({
204
217
  padding: 0,
205
218
  height: 0,
206
219
  overflow: 'hidden',
207
- transition: 'height 0.2s ease-in-out',
220
+ transition: theme.transitions.create('height', {
221
+ duration: theme.transitions.duration.standard,
222
+ }),
208
223
  },
209
224
  '&.navmenu-sub--opened > .navmenu-sub__container': {
210
225
  height: 'auto',
@@ -214,10 +229,13 @@ export const NavMenuSub = styled(NavMenuItem)(() => ({
214
229
  paddingLeft: '16px',
215
230
  boxShadow: 'none',
216
231
  },
232
+ '.navmenu-item__content': {
233
+ height: '48px',
234
+ },
217
235
  },
218
236
  }));
219
237
 
220
- // 下拉子菜单 .navmenu-sub-list
238
+ // 下拉子菜单 .navmenu-sub__list
221
239
  export const NavMenuSubList = styled('ul')(() => ({
222
240
  margin: 0,
223
241
  padding: '16px',
@@ -0,0 +1,96 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+
3
+ interface SubContainerProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ children: React.ReactNode;
5
+ }
6
+
7
+ const paddingTop = 8;
8
+
9
+ // 下拉子菜单容器
10
+ export function SubContainer({ children, ...props }: SubContainerProps) {
11
+ const rootRef = useRef<HTMLDivElement>(null);
12
+ const [position, setPosition] = useState(0); // 只需要保存水平偏移量
13
+
14
+ const updatePosition = () => {
15
+ if (!rootRef.current) return;
16
+ const anchor = rootRef.current.parentElement; // 以父容器作为参照
17
+ if (!anchor) return;
18
+
19
+ const anchorRect = anchor.getBoundingClientRect();
20
+ const containerRect = rootRef.current.getBoundingClientRect();
21
+ const windowWidth = window.innerWidth;
22
+ const padding = 32;
23
+
24
+ // 1. 计算相对于父元素的水平居中位置
25
+ let left = (anchorRect.width - containerRect.width) / 2;
26
+
27
+ // 2. 检查容器在视口中的绝对位置
28
+ const absoluteLeft = anchorRect.left + left;
29
+
30
+ // 3. 调整水平位置确保不超出窗口
31
+ if (absoluteLeft < 0) {
32
+ // 如果超出左边界,向右偏移
33
+ left = left - absoluteLeft + padding;
34
+ } else if (absoluteLeft + containerRect.width > windowWidth) {
35
+ // 如果超出右边界,向左偏移
36
+ left -= absoluteLeft + containerRect.width - windowWidth + padding;
37
+ }
38
+
39
+ setPosition(left);
40
+ };
41
+
42
+ // 监听自身大小变化
43
+ useEffect(() => {
44
+ const resizeObserver = new ResizeObserver(() => {
45
+ updatePosition();
46
+ });
47
+
48
+ resizeObserver.observe(rootRef.current!);
49
+
50
+ return () => resizeObserver.disconnect();
51
+ // eslint-disable-next-line react-hooks/exhaustive-deps
52
+ }, []);
53
+
54
+ // 监听 anchor 的大小变化
55
+ useEffect(() => {
56
+ const anchor = rootRef.current?.parentElement;
57
+ let resizeObserver: ResizeObserver | null = null;
58
+
59
+ if (anchor) {
60
+ resizeObserver = new ResizeObserver(() => {
61
+ updatePosition();
62
+ });
63
+
64
+ resizeObserver.observe(anchor);
65
+ }
66
+
67
+ return () => resizeObserver?.disconnect();
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ }, []);
70
+
71
+ // 监听窗口大小变化
72
+ useEffect(() => {
73
+ const handleResize = () => {
74
+ updatePosition();
75
+ };
76
+
77
+ window.addEventListener('resize', handleResize);
78
+ return () => window.removeEventListener('resize', handleResize);
79
+ // eslint-disable-next-line react-hooks/exhaustive-deps
80
+ }, []);
81
+
82
+ return (
83
+ <div
84
+ ref={rootRef}
85
+ className="navmenu-sub__container"
86
+ style={{
87
+ paddingTop: `${paddingTop}px`,
88
+ transform: `translateX(${position}px)`,
89
+ }}
90
+ {...props}>
91
+ {children}
92
+ </div>
93
+ );
94
+ }
95
+
96
+ export default SubContainer;