@zat-design/sisyphus-react 4.2.0 → 4.3.0-beta.1
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.
- package/dist/index.esm.css +1 -1
- package/dist/less.esm.css +1 -1
- package/es/ProForm/components/combination/Group/component/AddonWrapper/index.js +11 -2
- package/es/ProForm/components/combination/Group/style/index.less +36 -28
- package/es/ProForm/components/combination/Group/utils/index.d.ts +16 -16
- package/es/ProLayout/components/TabsManager/components/TabsHeader.d.ts +3 -5
- package/es/ProLayout/components/TabsManager/components/TabsHeader.js +65 -24
- package/es/ProLayout/components/TabsManager/hooks/useTabsState.js +18 -1
- package/es/ProLayout/components/TabsManager/index.js +27 -31
- package/es/ProLayout/components/TabsManager/propTypes.d.ts +6 -0
- package/es/ProLayout/components/TabsManager/style/index.less +55 -69
- package/es/ProLayout/index.js +17 -4
- package/es/ProLayout/propTypes.d.ts +5 -0
- package/es/ProLayout/style/index.less +49 -1
- package/package.json +1 -1
|
@@ -9,11 +9,20 @@ const AddonWrapper = props => {
|
|
|
9
9
|
} = props;
|
|
10
10
|
return /*#__PURE__*/_jsxs("div", {
|
|
11
11
|
className: "addon-wrapper",
|
|
12
|
+
style: {
|
|
13
|
+
display: 'flex',
|
|
14
|
+
alignItems: 'center',
|
|
15
|
+
gap: '8px'
|
|
16
|
+
},
|
|
12
17
|
children: [before && /*#__PURE__*/_jsx("div", {
|
|
13
|
-
|
|
18
|
+
style: {
|
|
19
|
+
whiteSpace: 'nowrap'
|
|
20
|
+
},
|
|
14
21
|
children: before
|
|
15
22
|
}), children, after && /*#__PURE__*/_jsx("div", {
|
|
16
|
-
|
|
23
|
+
style: {
|
|
24
|
+
whiteSpace: 'nowrap'
|
|
25
|
+
},
|
|
17
26
|
children: after
|
|
18
27
|
})]
|
|
19
28
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
@import '../../../../../style/variables.less';
|
|
2
|
+
@import '../component/AddonWrapper/index.less';
|
|
2
3
|
|
|
3
4
|
.pro-group {
|
|
4
5
|
display: flex;
|
|
@@ -115,7 +116,7 @@
|
|
|
115
116
|
|
|
116
117
|
// 确保输入框完全贴合
|
|
117
118
|
.@{ant-prefix}-input,
|
|
118
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
119
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
119
120
|
.@{ant-prefix}-input-number,
|
|
120
121
|
.@{ant-prefix}-input-number-input {
|
|
121
122
|
// 重置z-index确保正确的层级关系
|
|
@@ -127,7 +128,7 @@
|
|
|
127
128
|
// 第一个表单项:左边保持圆角
|
|
128
129
|
&:first-child {
|
|
129
130
|
.@{ant-prefix}-input,
|
|
130
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
131
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
131
132
|
.@{ant-prefix}-input-number,
|
|
132
133
|
.@{ant-prefix}-input-number-input {
|
|
133
134
|
border-top-left-radius: 6px;
|
|
@@ -138,7 +139,7 @@
|
|
|
138
139
|
// 最后一个表单项:右边保持圆角
|
|
139
140
|
&:last-child {
|
|
140
141
|
.@{ant-prefix}-input,
|
|
141
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
142
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
142
143
|
.@{ant-prefix}-input-number,
|
|
143
144
|
.@{ant-prefix}-input-number-input {
|
|
144
145
|
border-top-right-radius: 6px;
|
|
@@ -154,7 +155,7 @@
|
|
|
154
155
|
|
|
155
156
|
// 聚焦时恢复左边框
|
|
156
157
|
.@{ant-prefix}-input:focus,
|
|
157
|
-
.@{ant-prefix}-select:focus .@{ant-prefix}-select-
|
|
158
|
+
.@{ant-prefix}-select:focus .@{ant-prefix}-select-content,
|
|
158
159
|
.@{ant-prefix}-input-number:focus,
|
|
159
160
|
.@{ant-prefix}-input-number:focus .@{ant-prefix}-input-number-input {
|
|
160
161
|
margin-left: -1px;
|
|
@@ -165,7 +166,7 @@
|
|
|
165
166
|
// 错误状态:确保边框变红(优先级要高于上面的 border-left-width: 0)
|
|
166
167
|
&.@{ant-prefix}-form-item-has-error {
|
|
167
168
|
.@{ant-prefix}-input,
|
|
168
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
169
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
169
170
|
.@{ant-prefix}-input-number,
|
|
170
171
|
.@{ant-prefix}-input-number-input {
|
|
171
172
|
border-color: #ff4d4f !important;
|
|
@@ -174,7 +175,7 @@
|
|
|
174
175
|
// 错误状态下,中间元素也需要恢复左边框并设置为红色
|
|
175
176
|
&:not(:first-child) {
|
|
176
177
|
.@{ant-prefix}-input,
|
|
177
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
178
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
178
179
|
.@{ant-prefix}-input-number {
|
|
179
180
|
border-left-color: #ff4d4f !important;
|
|
180
181
|
border-left-width: 1px !important;
|
|
@@ -187,14 +188,14 @@
|
|
|
187
188
|
|
|
188
189
|
// 聚焦时保持红色边框
|
|
189
190
|
.@{ant-prefix}-input:focus,
|
|
190
|
-
.@{ant-prefix}-select:focus .@{ant-prefix}-select-
|
|
191
|
+
.@{ant-prefix}-select:focus .@{ant-prefix}-select-content,
|
|
191
192
|
.@{ant-prefix}-input-number:focus,
|
|
192
193
|
.@{ant-prefix}-input-number:focus .@{ant-prefix}-input-number-input {
|
|
193
194
|
border-color: #ff4d4f !important;
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
// Select 组件的错误状态处理
|
|
197
|
-
.@{ant-prefix}-select.@{ant-prefix}-select-status-error .@{ant-prefix}-select-
|
|
198
|
+
.@{ant-prefix}-select.@{ant-prefix}-select-status-error .@{ant-prefix}-select-content {
|
|
198
199
|
border-color: #ff4d4f !important;
|
|
199
200
|
}
|
|
200
201
|
}
|
|
@@ -317,37 +318,40 @@
|
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
// 确保输入框完全贴合
|
|
321
|
+
.@{ant-prefix}-input-affix-wrapper,
|
|
320
322
|
.@{ant-prefix}-input,
|
|
321
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
323
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
322
324
|
.@{ant-prefix}-input-number,
|
|
323
325
|
.@{ant-prefix}-input-number-input,
|
|
324
326
|
.@{ant-prefix}-picker {
|
|
325
327
|
// 重置z-index确保正确的层级关系
|
|
326
328
|
position: relative;
|
|
327
|
-
//
|
|
328
|
-
border-radius: 0;
|
|
329
|
+
// 重置圆角(!important 确保覆盖 antd v6 CSS-in-JS 懒注入的 disabled 等状态样式)
|
|
330
|
+
border-radius: 0 !important;
|
|
329
331
|
}
|
|
330
332
|
|
|
331
333
|
// 第一个表单项:左边保持圆角
|
|
332
334
|
&:first-child {
|
|
335
|
+
.@{ant-prefix}-input-affix-wrapper,
|
|
333
336
|
.@{ant-prefix}-input,
|
|
334
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
337
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
335
338
|
.@{ant-prefix}-input-number,
|
|
336
339
|
.@{ant-prefix}-input-number-input {
|
|
337
|
-
border-top-left-radius: 6px;
|
|
338
|
-
border-bottom-left-radius: 6px;
|
|
340
|
+
border-top-left-radius: 6px !important;
|
|
341
|
+
border-bottom-left-radius: 6px !important;
|
|
339
342
|
}
|
|
340
343
|
}
|
|
341
344
|
|
|
342
345
|
// 最后一个表单项:右边保持圆角
|
|
343
346
|
&:last-child {
|
|
347
|
+
.@{ant-prefix}-input-affix-wrapper,
|
|
344
348
|
.@{ant-prefix}-input,
|
|
345
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
349
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
346
350
|
.@{ant-prefix}-input-number,
|
|
347
351
|
.@{ant-prefix}-input-number-input,
|
|
348
352
|
.@{ant-prefix}-picker {
|
|
349
|
-
border-top-right-radius: 6px;
|
|
350
|
-
border-bottom-right-radius: 6px;
|
|
353
|
+
border-top-right-radius: 6px !important;
|
|
354
|
+
border-bottom-right-radius: 6px !important;
|
|
351
355
|
}
|
|
352
356
|
}
|
|
353
357
|
|
|
@@ -355,7 +359,7 @@
|
|
|
355
359
|
&:not(:first-child) {
|
|
356
360
|
// 聚焦时恢复左边框
|
|
357
361
|
.@{ant-prefix}-input:focus,
|
|
358
|
-
.@{ant-prefix}-select:focus .@{ant-prefix}-select-
|
|
362
|
+
.@{ant-prefix}-select:focus .@{ant-prefix}-select-content,
|
|
359
363
|
.@{ant-prefix}-input-number:focus,
|
|
360
364
|
.@{ant-prefix}-input-number:focus .@{ant-prefix}-input-number-input {
|
|
361
365
|
margin-left: -1px;
|
|
@@ -426,7 +430,7 @@
|
|
|
426
430
|
&:not(.pro-group-flexible) {
|
|
427
431
|
// 错误状态:确保边框变红(优先级要高于上面的 border-left-width: 0)
|
|
428
432
|
.@{ant-prefix}-input,
|
|
429
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
433
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
430
434
|
.@{ant-prefix}-input-number,
|
|
431
435
|
.@{ant-prefix}-input-number-input {
|
|
432
436
|
border-color: #ff4d4f !important;
|
|
@@ -435,7 +439,7 @@
|
|
|
435
439
|
// 错误状态下,中间元素也需要恢复左边框并设置为红色
|
|
436
440
|
&:not(:first-child) {
|
|
437
441
|
.@{ant-prefix}-input,
|
|
438
|
-
.@{ant-prefix}-select .@{ant-prefix}-select-
|
|
442
|
+
.@{ant-prefix}-select .@{ant-prefix}-select-content,
|
|
439
443
|
.@{ant-prefix}-input-number {
|
|
440
444
|
border-left-color: #ff4d4f !important;
|
|
441
445
|
border-left-width: 1px !important;
|
|
@@ -448,14 +452,14 @@
|
|
|
448
452
|
|
|
449
453
|
// 聚焦时保持红色边框
|
|
450
454
|
.@{ant-prefix}-input:focus,
|
|
451
|
-
.@{ant-prefix}-select:focus .@{ant-prefix}-select-
|
|
455
|
+
.@{ant-prefix}-select:focus .@{ant-prefix}-select-content,
|
|
452
456
|
.@{ant-prefix}-input-number:focus,
|
|
453
457
|
.@{ant-prefix}-input-number:focus .@{ant-prefix}-input-number-input {
|
|
454
458
|
border-color: #ff4d4f !important;
|
|
455
459
|
}
|
|
456
460
|
|
|
457
461
|
// Select 组件的错误状态处理
|
|
458
|
-
.@{ant-prefix}-select.@{ant-prefix}-select-status-error .@{ant-prefix}-select-
|
|
462
|
+
.@{ant-prefix}-select.@{ant-prefix}-select-status-error .@{ant-prefix}-select-content {
|
|
459
463
|
border-color: #ff4d4f !important;
|
|
460
464
|
}
|
|
461
465
|
}
|
|
@@ -464,8 +468,10 @@
|
|
|
464
468
|
// compact模式容器, 修复antd6定制模式下,Group组件内嵌套Form.Item时,表单样式问题
|
|
465
469
|
.pro-group-compact-container {
|
|
466
470
|
.@{ant-prefix}-select {
|
|
467
|
-
border-radius: 0;
|
|
468
|
-
|
|
471
|
+
border-radius: 0 !important;
|
|
472
|
+
}
|
|
473
|
+
.@{ant-prefix}-input-affix-wrapper {
|
|
474
|
+
border-radius: 0 !important;
|
|
469
475
|
}
|
|
470
476
|
|
|
471
477
|
> .pro-group-form-item:not(:first-child) {
|
|
@@ -492,9 +498,10 @@
|
|
|
492
498
|
> .pro-group-form-item:first-child {
|
|
493
499
|
.pro-select .@{ant-prefix}-select,
|
|
494
500
|
.@{ant-prefix}-select,
|
|
501
|
+
.@{ant-prefix}-input-affix-wrapper,
|
|
495
502
|
.@{ant-prefix}-input-number {
|
|
496
|
-
border-top-left-radius: 6px;
|
|
497
|
-
border-bottom-left-radius: 6px;
|
|
503
|
+
border-top-left-radius: 6px !important;
|
|
504
|
+
border-bottom-left-radius: 6px !important;
|
|
498
505
|
}
|
|
499
506
|
}
|
|
500
507
|
|
|
@@ -502,9 +509,10 @@
|
|
|
502
509
|
> .pro-group-form-item:last-child {
|
|
503
510
|
.pro-select .@{ant-prefix}-select,
|
|
504
511
|
.@{ant-prefix}-select,
|
|
512
|
+
.@{ant-prefix}-input-affix-wrapper,
|
|
505
513
|
.@{ant-prefix}-input-number {
|
|
506
|
-
border-top-right-radius: 6px;
|
|
507
|
-
border-bottom-right-radius: 6px;
|
|
514
|
+
border-top-right-radius: 6px !important;
|
|
515
|
+
border-bottom-right-radius: 6px !important;
|
|
508
516
|
}
|
|
509
517
|
}
|
|
510
518
|
}
|
|
@@ -75,31 +75,36 @@ export declare const useFormItemProps: (column: FlexibleGroupColumnType, context
|
|
|
75
75
|
confirm?: boolean | import("antd").ModalFuncProps | import("../../../render/propsType").FunctionArgs<any, boolean | import("antd").ModalFuncProps>;
|
|
76
76
|
show?: boolean | ReactiveFunction<any, boolean>;
|
|
77
77
|
component?: React.ReactNode | ReactiveFunction<any, React.ReactNode>;
|
|
78
|
+
children?: React.ReactNode | ((form: FormInstance<any>) => React.ReactNode);
|
|
78
79
|
trim?: boolean;
|
|
79
80
|
normalize?: (value: any, prevValue: any, allValues: import("@rc-component/form/lib/interface").Store) => any;
|
|
80
|
-
children?: React.ReactNode | ((form: FormInstance<any>) => React.ReactNode);
|
|
81
81
|
className?: string;
|
|
82
82
|
style?: React.CSSProperties;
|
|
83
|
-
|
|
84
|
-
trigger?: string;
|
|
83
|
+
preserve?: boolean;
|
|
85
84
|
id?: string;
|
|
86
|
-
isView?: boolean;
|
|
87
|
-
rootClassName?: string;
|
|
88
85
|
hidden?: boolean;
|
|
89
86
|
onReset?: () => void;
|
|
90
|
-
|
|
87
|
+
prefixCls?: string;
|
|
88
|
+
rootClassName?: string;
|
|
89
|
+
layout?: import("antd/es/form/Form").FormItemLayout;
|
|
90
|
+
help?: React.ReactNode;
|
|
91
91
|
vertical?: boolean;
|
|
92
|
-
validateTrigger?: string | false | string[];
|
|
93
|
-
preserve?: boolean;
|
|
94
92
|
htmlFor?: string;
|
|
95
|
-
|
|
93
|
+
trigger?: string;
|
|
94
|
+
status?: "" | "warning" | "error" | "success" | "validating";
|
|
95
|
+
isView?: boolean;
|
|
96
|
+
getValueProps?: ((value: any) => Record<string, unknown>) & ((value: any) => Record<string, unknown>);
|
|
97
|
+
desensitization?: [number, number] | ReactiveFunction<any, [number, number]>;
|
|
98
|
+
validateTrigger?: string | false | string[];
|
|
99
|
+
clearNotShow?: boolean;
|
|
96
100
|
labelAlign?: import("antd/es/form/interface").FormLabelAlign;
|
|
101
|
+
colon?: boolean;
|
|
97
102
|
labelCol?: import("antd").ColProps;
|
|
103
|
+
wrapperCol?: import("antd").ColProps;
|
|
98
104
|
getValueFromEvent?: (...args: import("@rc-component/form/lib/interface").EventArgs) => any;
|
|
99
105
|
shouldUpdate?: import("@rc-component/form/lib/Field").ShouldUpdate<any>;
|
|
100
106
|
validateDebounce?: number;
|
|
101
107
|
valuePropName?: string;
|
|
102
|
-
getValueProps?: ((value: any) => Record<string, unknown>) & ((value: any) => Record<string, unknown>);
|
|
103
108
|
messageVariables?: Record<string, string>;
|
|
104
109
|
initialValue?: any;
|
|
105
110
|
onMetaChange?: (meta: import("@rc-component/form/lib/Field").MetaEvent) => void;
|
|
@@ -110,9 +115,6 @@ export declare const useFormItemProps: (column: FlexibleGroupColumnType, context
|
|
|
110
115
|
icons: import("antd/es/form/FormItem").FeedbackIcons;
|
|
111
116
|
};
|
|
112
117
|
validateStatus?: "" | "warning" | "error" | "success" | "validating";
|
|
113
|
-
layout?: import("antd/es/form/Form").FormItemLayout;
|
|
114
|
-
wrapperCol?: import("antd").ColProps;
|
|
115
|
-
help?: React.ReactNode;
|
|
116
118
|
fieldId?: string;
|
|
117
119
|
valueType?: import("../../../render/propsType").ProFormValueType;
|
|
118
120
|
switchValue?: [any, any];
|
|
@@ -125,8 +127,6 @@ export declare const useFormItemProps: (column: FlexibleGroupColumnType, context
|
|
|
125
127
|
upperCase?: boolean;
|
|
126
128
|
toISOString?: boolean;
|
|
127
129
|
toCSTString?: boolean;
|
|
128
|
-
clearNotShow?: boolean;
|
|
129
|
-
desensitization?: [number, number] | ReactiveFunction<any, [number, number]>;
|
|
130
130
|
name: any;
|
|
131
131
|
dependencies: any[];
|
|
132
132
|
tooltip: string | {
|
|
@@ -141,7 +141,7 @@ export declare const useFormItemProps: (column: FlexibleGroupColumnType, context
|
|
|
141
141
|
* 创建组件属性
|
|
142
142
|
*/
|
|
143
143
|
export declare const createComponentProps: (column: FlexibleGroupColumnType, formItemProps: any) => {
|
|
144
|
-
componentProps: import("lodash").Omit<any, "format" | "valueType" | "switchValue" | "dependNames" | "toISOString" | "toCSTString" | "
|
|
144
|
+
componentProps: import("lodash").Omit<any, "format" | "clearNotShow" | "valueType" | "switchValue" | "dependNames" | "toISOString" | "toCSTString" | "precision">;
|
|
145
145
|
formItemTransform: {
|
|
146
146
|
getValueProps: any;
|
|
147
147
|
normalize: any;
|
|
@@ -4,9 +4,7 @@ export interface TabsHeaderProps {
|
|
|
4
4
|
tabsItems: TabsProps['items'];
|
|
5
5
|
onTabChange: (activeKey: string) => void;
|
|
6
6
|
onTabEdit: (targetKey: string, action: 'add' | 'remove') => void;
|
|
7
|
+
draggable?: boolean;
|
|
8
|
+
onReorder?: (activeId: string, overId: string) => void;
|
|
7
9
|
}
|
|
8
|
-
|
|
9
|
-
* 标签页头部:监听 window 滚动(实际滚动在 window/documentElement,非 body),
|
|
10
|
-
* 滚动时添加阴影类名;仅此组件随滚动重渲染,不带动父组件内容区。
|
|
11
|
-
*/
|
|
12
|
-
export declare function TabsHeader({ activeKey, tabsItems, onTabChange, onTabEdit }: TabsHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function TabsHeader({ activeKey, tabsItems, onTabChange, onTabEdit, draggable, onReorder, }: TabsHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,29 +1,57 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { createElement as _createElement } from 'react';
|
|
2
2
|
import { Tabs } from 'antd';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { closestCenter, DndContext, PointerSensor, useSensor } from '@dnd-kit/core';
|
|
4
|
+
import { horizontalListSortingStrategy, SortableContext, useSortable } from '@dnd-kit/sortable';
|
|
5
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
6
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
|
+
const DraggableTabNode = props => {
|
|
8
|
+
const {
|
|
9
|
+
attributes,
|
|
10
|
+
listeners,
|
|
11
|
+
setNodeRef,
|
|
12
|
+
transform,
|
|
13
|
+
transition
|
|
14
|
+
} = useSortable({
|
|
15
|
+
id: props['data-node-key']
|
|
16
|
+
});
|
|
17
|
+
const style = {
|
|
18
|
+
...props.style,
|
|
19
|
+
transform: CSS.Translate.toString(transform),
|
|
20
|
+
transition,
|
|
21
|
+
cursor: 'move'
|
|
22
|
+
};
|
|
23
|
+
return /*#__PURE__*/React.cloneElement(props.children, {
|
|
24
|
+
ref: setNodeRef,
|
|
25
|
+
style,
|
|
26
|
+
...attributes,
|
|
27
|
+
...listeners
|
|
28
|
+
});
|
|
29
|
+
};
|
|
8
30
|
export function TabsHeader({
|
|
9
31
|
activeKey,
|
|
10
32
|
tabsItems,
|
|
11
33
|
onTabChange,
|
|
12
|
-
onTabEdit
|
|
34
|
+
onTabEdit,
|
|
35
|
+
draggable = true,
|
|
36
|
+
onReorder
|
|
13
37
|
}) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
const sensor = useSensor(PointerSensor, {
|
|
39
|
+
activationConstraint: {
|
|
40
|
+
distance: 10
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const onDragEnd = ({
|
|
44
|
+
active,
|
|
45
|
+
over
|
|
46
|
+
}) => {
|
|
47
|
+
if (over && active.id !== over.id) {
|
|
48
|
+
onReorder?.(String(active.id), String(over.id));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const tabKeys = (tabsItems ?? []).map(item => item.key);
|
|
52
|
+
return /*#__PURE__*/_jsx("div", {
|
|
25
53
|
className: "pro-layout-tabs-header",
|
|
26
|
-
children:
|
|
54
|
+
children: /*#__PURE__*/_jsx(Tabs, {
|
|
27
55
|
activeKey: activeKey,
|
|
28
56
|
onChange: onTabChange,
|
|
29
57
|
onEdit: onTabEdit,
|
|
@@ -35,10 +63,23 @@ export function TabsHeader({
|
|
|
35
63
|
popup: {
|
|
36
64
|
root: 'pro-layout-tabs-dropdown-menu'
|
|
37
65
|
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
66
|
+
},
|
|
67
|
+
renderTabBar: draggable ? (tabBarProps, DefaultTabBar) => /*#__PURE__*/_jsx(DndContext, {
|
|
68
|
+
sensors: [sensor],
|
|
69
|
+
onDragEnd: onDragEnd,
|
|
70
|
+
collisionDetection: closestCenter,
|
|
71
|
+
children: /*#__PURE__*/_jsx(SortableContext, {
|
|
72
|
+
items: tabKeys,
|
|
73
|
+
strategy: horizontalListSortingStrategy,
|
|
74
|
+
children: /*#__PURE__*/_jsx(DefaultTabBar, {
|
|
75
|
+
...tabBarProps,
|
|
76
|
+
children: node => /*#__PURE__*/_createElement(DraggableTabNode, {
|
|
77
|
+
...node.props,
|
|
78
|
+
key: node.key
|
|
79
|
+
}, node)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
}) : undefined
|
|
83
|
+
})
|
|
43
84
|
});
|
|
44
85
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import { message } from 'antd';
|
|
3
|
+
import { arrayMove } from '@dnd-kit/sortable';
|
|
3
4
|
import { DEFAULT_TABS_CONFIG } from "../propTypes";
|
|
4
5
|
import { useTabsCache } from "./useTabsCache";
|
|
5
6
|
import locale, { formatMessage } from "../../../../locale";
|
|
@@ -332,6 +333,21 @@ export const useTabsState = options => {
|
|
|
332
333
|
});
|
|
333
334
|
}, [clearCache, finalConfig]);
|
|
334
335
|
|
|
336
|
+
/**
|
|
337
|
+
* 重排标签页顺序(拖拽结束后调用)
|
|
338
|
+
*/
|
|
339
|
+
const reorderTabs = useCallback((activeId, overId) => {
|
|
340
|
+
setState(prevState => {
|
|
341
|
+
const activeIndex = prevState.tabsList.findIndex(tab => tab.id === activeId);
|
|
342
|
+
const overIndex = prevState.tabsList.findIndex(tab => tab.id === overId);
|
|
343
|
+
if (activeIndex === -1 || overIndex === -1 || activeIndex === overIndex) return prevState;
|
|
344
|
+
return {
|
|
345
|
+
...prevState,
|
|
346
|
+
tabsList: arrayMove(prevState.tabsList, activeIndex, overIndex)
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
}, []);
|
|
350
|
+
|
|
335
351
|
/**
|
|
336
352
|
* 重置状态
|
|
337
353
|
*/
|
|
@@ -388,6 +404,7 @@ export const useTabsState = options => {
|
|
|
388
404
|
closeOtherTabs,
|
|
389
405
|
closeRightTabs,
|
|
390
406
|
closeAllTabs,
|
|
391
|
-
resetTabs
|
|
407
|
+
resetTabs,
|
|
408
|
+
reorderTabs
|
|
392
409
|
};
|
|
393
410
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useMemo, forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
2
3
|
import { useTabsState } from "./hooks/useTabsState";
|
|
3
4
|
import { TabItemComponent } from "./components/TabItem";
|
|
4
5
|
import { TabsHeader } from "./components/TabsHeader";
|
|
@@ -12,9 +13,10 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
12
13
|
config,
|
|
13
14
|
children,
|
|
14
15
|
dataSource,
|
|
15
|
-
originalOnMenuClick
|
|
16
|
+
originalOnMenuClick,
|
|
17
|
+
tabsBarContainer,
|
|
18
|
+
onTabsChange
|
|
16
19
|
}, ref) => {
|
|
17
|
-
// 使用标签页状态管理Hook
|
|
18
20
|
const {
|
|
19
21
|
state,
|
|
20
22
|
addTab,
|
|
@@ -23,7 +25,8 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
23
25
|
switchTab,
|
|
24
26
|
closeOtherTabs,
|
|
25
27
|
closeRightTabs,
|
|
26
|
-
closeAllTabs
|
|
28
|
+
closeAllTabs,
|
|
29
|
+
reorderTabs
|
|
27
30
|
} = useTabsState({
|
|
28
31
|
config,
|
|
29
32
|
dataSource
|
|
@@ -44,14 +47,16 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
44
47
|
}
|
|
45
48
|
}, [state.activeKey]);
|
|
46
49
|
|
|
50
|
+
// 通知父级(ProLayout)当前是否有 Tab
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
onTabsChange?.(state.tabsList.length > 0);
|
|
53
|
+
}, [state.tabsList.length, onTabsChange]);
|
|
54
|
+
|
|
47
55
|
// 处理菜单点击 - 拦截原有的菜单点击逻辑
|
|
48
56
|
const handleMenuClick = useCallback(params => {
|
|
49
57
|
if (params.item) {
|
|
50
|
-
// 添加到标签页
|
|
51
58
|
addTab(params.item);
|
|
52
59
|
}
|
|
53
|
-
|
|
54
|
-
// 如果有原始的菜单点击处理函数,也调用它
|
|
55
60
|
originalOnMenuClick?.(params);
|
|
56
61
|
}, [addTab, originalOnMenuClick]);
|
|
57
62
|
|
|
@@ -65,11 +70,9 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
65
70
|
} = params;
|
|
66
71
|
const menuItem = {
|
|
67
72
|
id: Date.now(),
|
|
68
|
-
// 生成临时 ID
|
|
69
73
|
code,
|
|
70
74
|
name,
|
|
71
75
|
url: `/${code}`,
|
|
72
|
-
// 生成 URL
|
|
73
76
|
extra
|
|
74
77
|
};
|
|
75
78
|
addTab(menuItem, options);
|
|
@@ -81,13 +84,9 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
81
84
|
activeComponent: state.activeComponent
|
|
82
85
|
})
|
|
83
86
|
}), [addTab, removeTab, state.tabsList, state.activeKey, state.activeComponent]);
|
|
84
|
-
|
|
85
|
-
// 处理标签切换
|
|
86
87
|
const handleTabChange = useCallback(activeKey => {
|
|
87
88
|
switchTab(activeKey);
|
|
88
89
|
}, [switchTab]);
|
|
89
|
-
|
|
90
|
-
// 处理标签关闭
|
|
91
90
|
const handleTabEdit = useCallback((targetKey, action) => {
|
|
92
91
|
if (action === 'remove') {
|
|
93
92
|
removeTab(targetKey);
|
|
@@ -111,12 +110,11 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
111
110
|
menuItems: config?.menuItems,
|
|
112
111
|
tabMenuClick: config?.tabMenuClick
|
|
113
112
|
}),
|
|
114
|
-
closable:
|
|
115
|
-
|
|
113
|
+
closable: false,
|
|
114
|
+
// 关闭 antd 内置 ×,由 TabItemComponent 自定义按钮处理
|
|
115
|
+
children: null
|
|
116
116
|
}));
|
|
117
117
|
}, [state.tabsList, state.activeKey, config?.menuItems, config?.tabMenuClick, switchTab, removeTab, closeOtherTabs, closeRightTabs, closeAllTabs]);
|
|
118
|
-
|
|
119
|
-
// 从 config 中获取组件解析函数和空状态组件
|
|
120
118
|
const activeComponent = config?.activeComponent;
|
|
121
119
|
const emptyComponent = config?.empty;
|
|
122
120
|
|
|
@@ -127,8 +125,6 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
127
125
|
children: [state.tabsList.map(tab => {
|
|
128
126
|
const isActive = tab.id === state.activeKey;
|
|
129
127
|
const hasVisited = visitedTabIds.has(tab.id);
|
|
130
|
-
|
|
131
|
-
// 如果既不是当前激活,也没有访问过,则只渲染占位符(懒加载)
|
|
132
128
|
if (!isActive && !hasVisited) {
|
|
133
129
|
return /*#__PURE__*/_jsx("div", {
|
|
134
130
|
className: "tab-pane hidden",
|
|
@@ -136,12 +132,9 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
136
132
|
}, tab.id);
|
|
137
133
|
}
|
|
138
134
|
let content = children;
|
|
139
|
-
|
|
140
|
-
// 如果提供了组件解析函数,尝试解析组件
|
|
141
135
|
if (activeComponent && tab.menuItem?.code) {
|
|
142
136
|
const ResolvedComponent = activeComponent(tab.menuItem.code);
|
|
143
137
|
if (ResolvedComponent) {
|
|
144
|
-
// 将 extra 中的所有属性作为 props 传递给组件
|
|
145
138
|
content = /*#__PURE__*/_jsx(ResolvedComponent, {
|
|
146
139
|
...(tab.menuItem.extra || {})
|
|
147
140
|
});
|
|
@@ -160,23 +153,26 @@ const TabsManager = /*#__PURE__*/forwardRef(({
|
|
|
160
153
|
});
|
|
161
154
|
};
|
|
162
155
|
|
|
163
|
-
//
|
|
156
|
+
// TabsHeader 节点:有 tabsBarContainer 时用 Portal 渲染到外部,否则原位 fallback
|
|
157
|
+
const tabsHeaderNode = state.tabsList.length > 0 ? /*#__PURE__*/_jsx(TabsHeader, {
|
|
158
|
+
activeKey: state.activeKey || undefined,
|
|
159
|
+
tabsItems: tabsItems,
|
|
160
|
+
onTabChange: handleTabChange,
|
|
161
|
+
onTabEdit: handleTabEdit,
|
|
162
|
+
draggable: config?.draggable !== false,
|
|
163
|
+
onReorder: reorderTabs
|
|
164
|
+
}) : null;
|
|
164
165
|
useImperativeHandle(ref, () => ({
|
|
165
166
|
handleMenuClick,
|
|
166
167
|
canAddTab
|
|
167
168
|
}), [handleMenuClick, canAddTab]);
|
|
168
|
-
return /*#__PURE__*/
|
|
169
|
+
return /*#__PURE__*/_jsxs(TabsContext.Provider, {
|
|
169
170
|
value: tabsInstance,
|
|
170
|
-
children: /*#__PURE__*/
|
|
171
|
+
children: [tabsBarContainer ? /*#__PURE__*/createPortal(tabsHeaderNode, tabsBarContainer) : tabsHeaderNode, /*#__PURE__*/_jsx("div", {
|
|
171
172
|
className: "pro-layout-tabs",
|
|
172
173
|
"data-testid": "tabs-manager",
|
|
173
|
-
children:
|
|
174
|
-
|
|
175
|
-
tabsItems: tabsItems,
|
|
176
|
-
onTabChange: handleTabChange,
|
|
177
|
-
onTabEdit: handleTabEdit
|
|
178
|
-
}), renderContent()]
|
|
179
|
-
})
|
|
174
|
+
children: renderContent()
|
|
175
|
+
})]
|
|
180
176
|
});
|
|
181
177
|
});
|
|
182
178
|
TabsManager.displayName = 'TabsManager';
|
|
@@ -14,6 +14,10 @@ export interface TabsManagerProps {
|
|
|
14
14
|
key: string;
|
|
15
15
|
keyPath: string[];
|
|
16
16
|
}) => void;
|
|
17
|
+
/** Tab 栏的 Portal 挂载目标(由 ProLayout 提供),不传则原位渲染 */
|
|
18
|
+
tabsBarContainer?: HTMLElement | null;
|
|
19
|
+
/** Tab 有无变化回调,供 ProLayout 控制 wrapper 动画 */
|
|
20
|
+
onTabsChange?: (hasTabs: boolean) => void;
|
|
17
21
|
}
|
|
18
22
|
export interface TabContextMenuProps {
|
|
19
23
|
tabId: string;
|
|
@@ -63,6 +67,8 @@ export interface UseTabsStateReturn {
|
|
|
63
67
|
closeAllTabs: () => void;
|
|
64
68
|
/** 重置状态 */
|
|
65
69
|
resetTabs: () => void;
|
|
70
|
+
/** 重排标签页顺序 */
|
|
71
|
+
reorderTabs: (activeId: string, overId: string) => void;
|
|
66
72
|
}
|
|
67
73
|
export interface TabsCacheManager {
|
|
68
74
|
save: (state: TabsState) => void;
|