@zat-design/sisyphus-react 4.6.4 → 4.6.5-beta.2

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.
@@ -49,8 +49,7 @@
49
49
  }
50
50
  }
51
51
 
52
- .pro-form-list,
53
- .pro-form-list-tabs {
52
+ .pro-form-list {
54
53
  .@{ant-prefix}-tabs-nav {
55
54
  margin-bottom: 0 !important;
56
55
  }
@@ -116,63 +115,94 @@
116
115
  background: transparent !important;
117
116
  }
118
117
  }
119
-
120
- .pro-form-list-tool-btn-tabs {
121
- width: auto;
122
- min-width: 20px;
123
- height: 20px;
124
- padding: 0;
125
-
126
- &:hover {
127
- background: transparent !important;
128
- }
129
- }
130
118
  }
131
119
 
132
120
  .pro-form-list-tabs {
133
121
  &.@{ant-prefix}-tabs > .@{ant-prefix}-tabs-nav {
122
+ margin-bottom: 0;
123
+ padding-top: 8px;
124
+
125
+ &::before {
126
+ display: none;
127
+ }
128
+
134
129
  .@{ant-prefix}-tabs-nav-wrap {
130
+ min-width: 0;
135
131
  overflow-x: clip;
136
132
  overflow-y: visible;
137
133
  }
138
134
 
139
135
  .@{ant-prefix}-tabs-nav-list {
140
136
  overflow: visible;
137
+ align-items: center;
138
+ gap: 12px;
141
139
  }
142
140
  }
143
141
 
144
142
  &.@{ant-prefix}-tabs-card > .@{ant-prefix}-tabs-nav {
145
- &::before {
146
- border-bottom-color: var(--ant-color-border, #d9d9d9);
147
- }
148
-
149
143
  .@{ant-prefix}-tabs-tab {
150
- padding: 0;
151
- border-color: var(--ant-color-border, #d9d9d9);
152
- border-radius: var(--zaui-border-radius, 8px) var(--zaui-border-radius, 8px) 0 0;
153
-
154
- &.@{ant-prefix}-tabs-tab-active {
155
- border-bottom-color: var(--ant-color-bg-container, #fff);
144
+ height: 32px;
145
+ // 覆盖全局 card Tabs 的强制内外边距,间距统一交给 nav-list。
146
+ padding: 0 !important;
147
+ margin: 0 !important;
148
+ background: transparent;
149
+ border: 0;
150
+
151
+ &.@{ant-prefix}-tabs-tab-active .pro-form-list-tabs-label {
152
+ color: var(--pro-form-list-tabs-primary);
153
+ background: var(--pro-form-list-tabs-primary-bg);
154
+ border-color: var(--pro-form-list-tabs-primary);
156
155
  }
157
156
  }
158
157
 
159
- .@{ant-prefix}-tabs-tab + .@{ant-prefix}-tabs-tab,
160
- .@{ant-prefix}-tabs-nav-add {
161
- margin-left: 4px;
158
+ .@{ant-prefix}-tabs-tab-btn {
159
+ color: inherit;
160
+ text-shadow: none;
162
161
  }
163
162
 
164
163
  .@{ant-prefix}-tabs-nav-add {
165
164
  width: auto;
166
- padding: 0 @zaui-space-size-xs;
167
- border-color: var(--ant-color-border, #d9d9d9);
168
- border-radius: var(--zaui-border-radius, 8px) var(--zaui-border-radius, 8px) 0 0;
165
+ min-width: 32px;
166
+ height: 32px;
167
+ padding: 0 12px;
168
+ margin: 0;
169
+ color: var(--pro-form-list-tabs-muted);
170
+ background: var(--pro-form-list-tabs-bg);
171
+ border: 1px dashed var(--pro-form-list-tabs-border);
172
+ border-radius: 16px;
173
+
174
+ &:focus:not(:hover) {
175
+ color: var(--pro-form-list-tabs-muted);
176
+ border-color: var(--pro-form-list-tabs-border);
177
+ }
178
+
179
+ &:hover {
180
+ color: var(--pro-form-list-tabs-primary-hover);
181
+ border-color: var(--pro-form-list-tabs-primary-hover);
182
+ }
169
183
  }
170
184
  }
171
185
 
172
186
  .pro-form-list-tabs-label {
187
+ position: relative;
173
188
  display: inline-flex;
174
189
  align-items: center;
175
190
  gap: 4px;
191
+ height: 32px;
192
+ padding: 0 12px;
193
+ color: var(--pro-form-list-tabs-text);
194
+ background: var(--pro-form-list-tabs-bg);
195
+ border: 1px solid var(--pro-form-list-tabs-border);
196
+ border-radius: 16px;
197
+
198
+ &:hover {
199
+ color: var(--pro-form-list-tabs-primary-hover);
200
+ border-color: var(--pro-form-list-tabs-primary-hover);
201
+ }
202
+
203
+ &[data-drag-over='true'] {
204
+ border-color: var(--pro-form-list-tabs-primary-hover);
205
+ }
176
206
  }
177
207
 
178
208
  .pro-form-list-tabs-title {
@@ -180,15 +210,20 @@
180
210
  }
181
211
 
182
212
  .pro-form-list-tabs-error-badge {
213
+ // 角标以整个胶囊为定位容器,随拖动一起移动。
214
+ position: static;
183
215
  display: inline-flex;
216
+ color: inherit;
184
217
 
185
218
  .@{ant-prefix}-badge-count {
186
- top: 0;
187
- right: 0;
219
+ top: 4px;
220
+ right: 4px;
188
221
  z-index: 2;
189
222
  pointer-events: none;
190
223
  transform: translate(50%, -50%);
191
- box-shadow: 0 0 0 2px var(--ant-color-bg-container, #fff);
224
+ color: var(--pro-form-list-tabs-solid-text);
225
+ background: var(--pro-form-list-tabs-error);
226
+ box-shadow: 0 0 0 2px var(--pro-form-list-tabs-bg);
192
227
  transition: none;
193
228
  animation: none;
194
229
 
@@ -204,36 +239,132 @@
204
239
  align-items: center;
205
240
  }
206
241
 
242
+ .pro-form-list-tabs-add {
243
+ display: inline-flex;
244
+ gap: 4px;
245
+ align-items: center;
246
+ white-space: nowrap;
247
+ .pro-form-list-tabs-add-label {
248
+ font-size: 14px;
249
+ }
250
+ }
251
+
252
+ .pro-form-list-tabs-content {
253
+ padding: 12px 0 0;
254
+ border: 0;
255
+ }
256
+ }
257
+
258
+ .pro-form-list-tabs,
259
+ .pro-form-list-tabs-dropdown {
207
260
  .pro-form-list-tabs-drag-handle {
208
261
  display: inline-flex;
262
+ flex-shrink: 0;
209
263
  align-items: center;
264
+ color: var(--pro-form-list-tabs-muted);
265
+ touch-action: none;
266
+
210
267
  .pro-icon {
211
268
  cursor: move;
212
269
  }
213
270
  }
214
- .pro-form-list-delete-icon {
215
- &:hover {
216
- background: transparent !important;
271
+
272
+ .pro-form-list-action {
273
+ padding-bottom: 0;
274
+
275
+ [data-form-list-action] {
276
+ min-width: 20px;
277
+ height: 20px;
278
+ padding: 0;
279
+ color: inherit;
280
+ }
281
+
282
+ [data-form-list-action='delete'] {
283
+ color: var(--pro-form-list-tabs-close);
284
+ border-radius: 4px;
285
+ transition: color 0.2s;
286
+
287
+ &:hover {
288
+ color: var(--pro-form-list-tabs-muted);
289
+ background: transparent;
290
+ }
217
291
  }
218
292
  }
293
+ }
219
294
 
220
- .pro-form-list-tabs-add {
221
- display: inline-flex;
295
+ .pro-form-list-tabs-dropdown {
296
+ min-width: 180px;
297
+ max-width: calc(100vw - 32px);
298
+
299
+ .@{ant-prefix}-tabs-dropdown-menu-item {
300
+ &:hover,
301
+ &-active,
302
+ &-selected {
303
+ background: transparent;
304
+ }
305
+ }
306
+
307
+ .@{ant-prefix}-tabs-dropdown-menu-item > span {
308
+ display: block;
309
+ width: 100%;
310
+ min-width: 0;
311
+ }
312
+
313
+ .pro-form-list-tabs-label {
314
+ display: flex;
315
+ align-items: center;
222
316
  gap: 4px;
317
+ width: 100%;
318
+ min-width: 0;
319
+ }
320
+
321
+ .pro-form-list-tabs-error-badge {
322
+ display: flex;
323
+ flex: 1;
223
324
  align-items: center;
325
+ gap: 8px;
326
+ min-width: 0;
327
+ color: inherit;
328
+ }
329
+
330
+ .pro-form-list-tabs-title-content {
331
+ display: contents;
332
+ }
333
+
334
+ .pro-form-list-tabs-title {
335
+ flex: 1;
336
+ min-width: 0;
337
+ overflow: hidden;
224
338
  white-space: nowrap;
225
- .pro-form-list-tabs-add-label {
226
- font-size: 14px;
227
- }
339
+ text-overflow: ellipsis;
228
340
  }
229
341
 
230
342
  .pro-form-list-action {
231
- padding-bottom: 0;
343
+ order: 2;
344
+ flex-shrink: 0;
345
+ margin-inline-start: auto;
232
346
  }
233
347
 
234
- .pro-form-list-tabs-content {
235
- padding: 12px 0 0;
236
- border: 0;
348
+ .@{ant-prefix}-badge-count {
349
+ position: static;
350
+ order: 1;
351
+ flex-shrink: 0;
352
+ transform: none;
353
+ }
354
+ }
355
+
356
+ .pro-form-list-tabs-drag-overlay {
357
+ pointer-events: none;
358
+
359
+ .pro-form-list-tabs-label {
360
+ width: max-content;
361
+ min-width: 100%;
362
+ }
363
+
364
+ &[data-active='true'] .pro-form-list-tabs-label {
365
+ color: var(--pro-form-list-tabs-primary);
366
+ background: var(--pro-form-list-tabs-primary-bg);
367
+ border-color: var(--pro-form-list-tabs-primary);
237
368
  }
238
369
  }
239
370
 
@@ -2,16 +2,16 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useContext } from "react";
3
3
  import { Popover, Tooltip } from "antd";
4
4
  import classnames from "classnames";
5
- import { Link as RouterLink } from "react-router-dom";
5
+ import { Link as RouterLink, useLocation } from "react-router-dom";
6
6
  import ProIcon from "../../../../../ProIcon";
7
7
  import SideMenu from "../SideMenu";
8
8
  import { LayoutContext } from "../../../../index";
9
- import { getUrlParams, getPathNameKey, findMenuItemByKey } from "../../../../utils";
9
+ import { findMenuItemByKey, resolveMenuKeyIdPath } from "../../../../utils";
10
10
  const Link = RouterLink;
11
11
  const FoldMenu = (props) => {
12
12
  const { style, dataSource, onMenuClick } = props;
13
13
  const { menus, iconfontUrl, notice, collapsed, headerHeight } = dataSource;
14
- const pathNamePrefix = `/${window.location.pathname.split("/")[1]}`;
14
+ const location = useLocation();
15
15
  const noticeCls = classnames({
16
16
  "pro-layout-menu-tooltip": true,
17
17
  "pro-layout-menu-tooltip-has-notice": notice,
@@ -22,17 +22,16 @@ const FoldMenu = (props) => {
22
22
  onSelected: () => {
23
23
  }
24
24
  };
25
+ const currentKeyIdPath = resolveMenuKeyIdPath({
26
+ menus,
27
+ pathname: location.pathname,
28
+ search: location.search,
29
+ selectedPath
30
+ });
25
31
  return /* @__PURE__ */ jsx("div", { className: "pro-layout-menu-fold", style: { ...style }, children: /* @__PURE__ */ jsx("ul", { className: "pro-layout-menu-fold-list", style: { ...style }, children: menus.filter((item) => !item.hideInMenu).map((item, index) => {
26
32
  var _a;
27
33
  const { id, url, name, icon, imgUrl, imgActiveUrl, children } = item;
28
34
  const toPath = item.redirectUrl || item.url;
29
- const selectedMenu = getUrlParams("activeMenu");
30
- const pathToMatch = selectedPath || selectedMenu || window.location.pathname;
31
- const currentKeyIdPath = getPathNameKey({
32
- menus,
33
- pathName: pathToMatch
34
- // 优先匹配上下文路径
35
- }) || [];
36
35
  const cls = classnames({
37
36
  active: currentKeyIdPath.includes(id)
38
37
  });
@@ -42,7 +41,7 @@ const FoldMenu = (props) => {
42
41
  "img",
43
42
  {
44
43
  className: "pro-layout-icon",
45
- src: pathNamePrefix === url ? imgActiveUrl || imgUrl : imgUrl,
44
+ src: currentKeyIdPath.includes(id) ? imgActiveUrl || imgUrl : imgUrl,
46
45
  alt: "icon"
47
46
  }
48
47
  ) : /* @__PURE__ */ jsx(
@@ -4,15 +4,14 @@ import { Menu } from "antd";
4
4
  import { useDeepCompareEffect, useSetState } from "ahooks";
5
5
  import { CaretDownOutlined } from "@ant-design/icons";
6
6
  import classnames from "classnames";
7
- import { Link as RouterLink } from "react-router-dom";
7
+ import { Link as RouterLink, useLocation } from "react-router-dom";
8
8
  import cloneDeep from "lodash/cloneDeep";
9
9
  import { LayoutContext } from "../../../../index";
10
- import { getIdsByPathName, findMenuItemByKey } from "../../../../utils";
10
+ import { findMenuItemByKey, resolveMenuKeyIdPath } from "../../../../utils";
11
11
  import { canOpenAsTab } from "../../../TabsManager/utils";
12
12
  import ProIcon from "../../../../../ProIcon";
13
13
  const Link = RouterLink;
14
14
  const OpenMenu = (props) => {
15
- var _a;
16
15
  const { className, dataSource, style, onMenuClick, textVisible = true } = props;
17
16
  const { menus, sideMenu } = dataSource || {};
18
17
  const linkRef = useRef(null);
@@ -21,7 +20,7 @@ const OpenMenu = (props) => {
21
20
  openKeys: [],
22
21
  router: ""
23
22
  });
24
- const query = new URLSearchParams(window.location.search);
23
+ const location = useLocation();
25
24
  const layoutContext = useContext(LayoutContext);
26
25
  const layoutSelectedPath = layoutContext == null ? void 0 : layoutContext.selectedPath;
27
26
  const layoutOnSelected = layoutContext == null ? void 0 : layoutContext.onSelected;
@@ -31,8 +30,12 @@ const OpenMenu = (props) => {
31
30
  "pro-layout-menu-text-hidden": textVisible === false,
32
31
  [`${className}`]: className
33
32
  });
34
- const preferredPath = query.get("activeMenu") || (layoutSelectedPath === "##EMPTY##" ? null : layoutSelectedPath) || window.location.pathname;
35
- const selectKeyIdPath = preferredPath ? ((_a = getIdsByPathName(menus, preferredPath)) == null ? void 0 : _a.map((item) => String(item))) || [] : [];
33
+ const selectKeyIdPath = resolveMenuKeyIdPath({
34
+ menus,
35
+ pathname: location.pathname,
36
+ search: location.search,
37
+ selectedPath: layoutSelectedPath
38
+ }).map((item) => String(item));
36
39
  const selectedRootKey = selectKeyIdPath[0];
37
40
  const itemsTransform = useMemo(() => {
38
41
  const result = cloneDeep(menus);
@@ -41,8 +44,8 @@ const OpenMenu = (props) => {
41
44
  }
42
45
  const menuDeep = (data) => {
43
46
  data.forEach((item) => {
44
- var _a2, _b, _c;
45
- const isFirstMenu = ((_a2 = item.keyIdPath) == null ? void 0 : _a2.length) === 1 || sideMenu && ((_b = item.keyIdPath) == null ? void 0 : _b.length) === 2;
47
+ var _a, _b, _c;
48
+ const isFirstMenu = ((_a = item.keyIdPath) == null ? void 0 : _a.length) === 1 || sideMenu && ((_b = item.keyIdPath) == null ? void 0 : _b.length) === 2;
46
49
  item.label = /* @__PURE__ */ jsxs(
47
50
  "div",
48
51
  {
@@ -140,7 +143,7 @@ const OpenMenu = (props) => {
140
143
  }
141
144
  if (menuItem && canOpenAsTab(menuItem) && shouldActivate) {
142
145
  setState({
143
- selectedKeys: keyPath,
146
+ selectedKeys: menuKeyPath,
144
147
  router: menuRouter
145
148
  });
146
149
  layoutOnSelected == null ? void 0 : layoutOnSelected({
@@ -6,6 +6,16 @@ import type { MenusType } from '../propTypes';
6
6
  * @return result
7
7
  */
8
8
  export declare const getIdsByPathName: (menus: any[], activeMenu?: string) => number[];
9
+ interface ResolveMenuKeyIdPathOptions {
10
+ menus: any[];
11
+ pathname: string;
12
+ search?: string;
13
+ selectedPath?: string;
14
+ }
15
+ /**
16
+ * 统一解析菜单激活路径:显式 activeMenu 优先,其次匹配真实路由,最后回退上次选择。
17
+ */
18
+ export declare const resolveMenuKeyIdPath: ({ menus, pathname, search, selectedPath, }: ResolveMenuKeyIdPathOptions) => number[];
9
19
  /**
10
20
  * 转换菜单数据源评价keyUrlPath与keyIdPath
11
21
  * @param menuData 菜单数据源
@@ -41,3 +51,4 @@ export declare const getPathNameKey: ({ menus, pathName }: {
41
51
  * @returns 找到的菜单项数据或null
42
52
  */
43
53
  export declare const findMenuItemByKey: (menus: any[], key: string) => any;
54
+ export {};
@@ -1,12 +1,21 @@
1
1
  import isPlainObject from "lodash/isPlainObject";
2
2
  const updateBestMatch = (pathName, itemUrl, itemRedirectUrl, keyIdPath, bestMatch) => {
3
- if (itemUrl && pathName.includes(itemUrl)) {
3
+ const isSameOrNestedPath = (candidate) => {
4
+ if (!candidate)
5
+ return false;
6
+ const normalizedCandidate = candidate.length > 1 ? candidate.replace(/\/+$/, "") : candidate;
7
+ if (pathName === normalizedCandidate)
8
+ return true;
9
+ const boundary = pathName.charAt(normalizedCandidate.length);
10
+ return pathName.startsWith(normalizedCandidate) && ["/", "?", "#"].includes(boundary);
11
+ };
12
+ if (isSameOrNestedPath(itemUrl)) {
4
13
  const urlLength = itemUrl.length;
5
14
  if (!bestMatch || urlLength > bestMatch.urlLength) {
6
15
  return { keyIdPath, urlLength };
7
16
  }
8
17
  }
9
- if (itemRedirectUrl && pathName.includes(itemRedirectUrl)) {
18
+ if (isSameOrNestedPath(itemRedirectUrl)) {
10
19
  const urlLength = itemRedirectUrl.length;
11
20
  if (!bestMatch || urlLength > bestMatch.urlLength) {
12
21
  return { keyIdPath, urlLength };
@@ -14,6 +23,27 @@ const updateBestMatch = (pathName, itemUrl, itemRedirectUrl, keyIdPath, bestMatc
14
23
  }
15
24
  return bestMatch;
16
25
  };
26
+ const getExactMenuKeyIdPath = (menus, pathname) => {
27
+ const normalizePathname = (value) => {
28
+ if (!value)
29
+ return "";
30
+ const path = value.split(/[?#]/)[0];
31
+ return path.length > 1 ? path.replace(/\/+$/, "") : path;
32
+ };
33
+ const targetPathname = normalizePathname(pathname);
34
+ for (const item of menus || []) {
35
+ const routes = [item == null ? void 0 : item.redirectUrl, item == null ? void 0 : item.router, item == null ? void 0 : item.url].map(normalizePathname);
36
+ if (targetPathname && routes.includes(targetPathname)) {
37
+ return item.keyIdPath || [];
38
+ }
39
+ if (Array.isArray(item == null ? void 0 : item.children) && item.children.length) {
40
+ const childPath = getExactMenuKeyIdPath(item.children, pathname);
41
+ if (childPath.length)
42
+ return childPath;
43
+ }
44
+ }
45
+ return [];
46
+ };
17
47
  const getIdsByPathName = (menus, activeMenu) => {
18
48
  const _pathName = activeMenu || window.location.pathname;
19
49
  let result;
@@ -42,6 +72,26 @@ const getIdsByPathName = (menus, activeMenu) => {
42
72
  menuDeep(menus);
43
73
  return result || (bestMatch == null ? void 0 : bestMatch.keyIdPath) || [];
44
74
  };
75
+ const resolveMenuKeyIdPath = ({
76
+ menus,
77
+ pathname,
78
+ search = "",
79
+ selectedPath
80
+ }) => {
81
+ if (selectedPath === "##EMPTY##")
82
+ return [];
83
+ const activeMenu = new URLSearchParams(search).get("activeMenu");
84
+ const activeMenuPath = activeMenu ? getIdsByPathName(menus, activeMenu) : [];
85
+ if (activeMenuPath.length)
86
+ return activeMenuPath;
87
+ const exactRoutePath = getExactMenuKeyIdPath(menus, pathname);
88
+ if (exactRoutePath.length)
89
+ return exactRoutePath;
90
+ const nestedRoutePath = getIdsByPathName(menus, pathname);
91
+ if (nestedRoutePath.length)
92
+ return nestedRoutePath;
93
+ return selectedPath ? getIdsByPathName(menus, selectedPath) : [];
94
+ };
45
95
  const transformMenu = (menuData, pathPrefix) => {
46
96
  const data = [...menuData];
47
97
  const menuDeep = (menuDeepItem) => {
@@ -163,6 +213,7 @@ export {
163
213
  getIdsByPathName,
164
214
  getPathNameKey,
165
215
  getUrlParams,
216
+ resolveMenuKeyIdPath,
166
217
  transformMenu,
167
218
  transformMenus
168
219
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zat-design/sisyphus-react",
3
- "version": "4.6.4",
3
+ "version": "4.6.5-beta.2",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -106,6 +106,14 @@
106
106
  "engines": {
107
107
  "node": ">=20.0.0"
108
108
  },
109
+ "resolutions": {
110
+ "@umijs/bundler-utils@3.5.43": "3.5.43"
111
+ },
112
+ "overrides": {
113
+ "@umijs/bundler-webpack@3.5.43": {
114
+ "@umijs/bundler-utils": "3.5.43"
115
+ }
116
+ },
109
117
  "browserslist": [
110
118
  "last 2 versions",
111
119
  "Firefox ESR",