@gx-design-vue/pro-layout 0.1.0-alpha.2 → 0.1.0-alpha.21
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/ProLayout.d.ts +4 -536
- package/dist/ProLayout.js +489 -358
- package/dist/components/AppPage/context.d.ts +4 -3
- package/dist/components/AppPage/context.js +3 -5
- package/dist/components/AppPage/index.d.ts +29 -52
- package/dist/components/AppPage/index.js +71 -54
- package/dist/components/AppPage/style.d.ts +8 -7
- package/dist/components/AppPage/style.js +16 -17
- package/dist/components/Breadcrumb/index.d.ts +19 -0
- package/dist/components/Breadcrumb/index.js +129 -0
- package/dist/components/Breadcrumb/interface.d.ts +33 -0
- package/dist/components/Breadcrumb/interface.js +1 -0
- package/dist/components/CollapseButton/Arrow.d.ts +4 -0
- package/dist/components/{SiderMenu → CollapseButton}/Arrow.js +2 -4
- package/dist/components/CollapseButton/index.d.ts +7 -0
- package/dist/components/CollapseButton/index.js +45 -0
- package/dist/components/CollapseButton/interface.d.ts +19 -0
- package/dist/components/CollapseButton/interface.js +1 -0
- package/dist/components/Footer/index.d.ts +10 -0
- package/dist/components/Footer/index.js +69 -0
- package/dist/components/Footer/interface.d.ts +22 -0
- package/dist/components/Footer/interface.js +1 -0
- package/dist/components/FooterToolbar/index.d.ts +5 -72
- package/dist/components/FooterToolbar/index.js +77 -90
- package/dist/components/FooterToolbar/style.d.ts +10 -0
- package/dist/components/FooterToolbar/style.js +34 -0
- package/dist/components/Header/index.d.ts +7 -0
- package/dist/components/Header/index.js +140 -0
- package/dist/components/Header/interface.d.ts +41 -0
- package/dist/components/Header/interface.js +1 -0
- package/dist/components/Logo/index.d.ts +9 -0
- package/dist/components/Logo/index.js +63 -0
- package/dist/components/Logo/interface.d.ts +13 -0
- package/dist/components/Logo/interface.js +1 -0
- package/dist/components/Menu/iconRender.d.ts +10 -0
- package/dist/components/Menu/iconRender.js +23 -0
- package/dist/components/Menu/index.d.ts +13 -0
- package/dist/components/Menu/index.js +778 -0
- package/dist/components/Menu/interface.d.ts +50 -0
- package/dist/components/Menu/interface.js +9 -0
- package/dist/components/Menu/overflow.d.ts +39 -0
- package/dist/components/Menu/overflow.js +81 -0
- package/dist/components/Menu/style/base.d.ts +12 -0
- package/dist/components/Menu/style/base.js +36 -0
- package/dist/components/Menu/style/horizontal.d.ts +19 -0
- package/dist/components/Menu/style/horizontal.js +303 -0
- package/dist/components/Menu/style/index.d.ts +356 -0
- package/dist/components/Menu/style/index.js +147 -0
- package/dist/components/Menu/style/inline.d.ts +13 -0
- package/dist/components/Menu/style/inline.js +292 -0
- package/dist/components/PageContainer/PageHeader.d.ts +30 -36
- package/dist/components/PageContainer/PageHeader.js +80 -46
- package/dist/components/PageContainer/index.d.ts +8 -194
- package/dist/components/PageContainer/index.js +297 -209
- package/dist/components/PageContainer/style.d.ts +5 -0
- package/dist/components/PageContainer/style.js +142 -0
- package/dist/components/PageTransition/animate.d.ts +41 -0
- package/dist/components/PageTransition/animate.js +106 -0
- package/dist/components/PageTransition/index.d.ts +57 -0
- package/dist/components/PageTransition/index.js +66 -0
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/back.d.ts +4 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/back.js +2 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/bounce.d.ts +4 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/bounce.js +2 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/fade.d.ts +4 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/fade.js +2 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/flip.d.ts +4 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/flip.js +2 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/lightSpeed.d.ts +4 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/lightSpeed.js +2 -4
- package/dist/components/PageTransition/keyFrame/roll.d.ts +10 -0
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/roll.js +2 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/rotate.d.ts +4 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/rotate.js +2 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/slide.d.ts +4 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/slide.js +2 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/zoom.d.ts +4 -4
- package/dist/components/{PageTranstion → PageTransition}/keyFrame/zoom.js +2 -4
- package/dist/components/PageTransition/style.d.ts +11 -0
- package/dist/components/{PageTranstion → PageTransition}/style.js +9 -13
- package/dist/components/Sider/index.d.ts +7 -0
- package/dist/components/Sider/index.js +206 -0
- package/dist/components/Sider/interface.d.ts +39 -0
- package/dist/components/Sider/interface.js +1 -0
- package/dist/components/Tabs/InnerTabs.d.ts +7 -0
- package/dist/components/Tabs/InnerTabs.js +117 -0
- package/dist/components/Tabs/contextMenu.d.ts +41 -0
- package/dist/components/Tabs/contextMenu.js +89 -0
- package/dist/components/Tabs/hooks/flipAnimate.d.ts +24 -0
- package/dist/components/Tabs/hooks/flipAnimate.js +40 -0
- package/dist/components/Tabs/hooks/useAutoScroll.d.ts +27 -0
- package/dist/components/Tabs/hooks/useAutoScroll.js +122 -0
- package/dist/components/Tabs/hooks/useTabDrag.d.ts +31 -0
- package/dist/components/Tabs/hooks/useTabDrag.js +252 -0
- package/dist/components/Tabs/index.d.ts +13 -0
- package/dist/components/Tabs/index.js +181 -0
- package/dist/components/Tabs/interface.d.ts +74 -0
- package/dist/components/Tabs/interface.js +1 -0
- package/dist/components/Tabs/style/index.d.ts +35 -0
- package/dist/components/Tabs/style/index.js +161 -0
- package/dist/components/WrapContent/index.d.ts +9 -0
- package/dist/components/WrapContent/index.js +26 -0
- package/dist/components/WrapContent/interface.d.ts +15 -0
- package/dist/components/WrapContent/interface.js +1 -0
- package/dist/context/index.d.ts +53 -0
- package/dist/context/index.js +5 -0
- package/dist/defaultConfig.d.ts +10 -0
- package/dist/defaultConfig.js +38 -0
- package/dist/hooks/useLayoutBase.d.ts +78 -0
- package/dist/hooks/useLayoutBase.js +86 -0
- package/dist/hooks/useMenu.d.ts +77 -0
- package/dist/hooks/useMenu.js +166 -0
- package/dist/hooks/useTabs.d.ts +54 -0
- package/dist/hooks/useTabs.js +388 -0
- package/dist/index.d.ts +22 -27
- package/dist/index.js +15 -24
- package/dist/interface.d.ts +516 -0
- package/dist/interface.js +34 -0
- package/dist/pro-layout.esm.js +5848 -5075
- package/dist/pro-layout.js +16 -37
- package/dist/style/breadcrumb.d.ts +8 -0
- package/dist/style/breadcrumb.js +77 -0
- package/dist/style/collapseButton.d.ts +8 -0
- package/dist/style/collapseButton.js +57 -0
- package/dist/style/content.d.ts +8 -0
- package/dist/style/content.js +29 -0
- package/dist/style/footer.d.ts +8 -0
- package/dist/style/footer.js +33 -0
- package/dist/style/header.d.ts +8 -0
- package/dist/style/header.js +76 -0
- package/dist/style/index.d.ts +3 -22
- package/dist/style/index.js +51 -55
- package/dist/style/logo.d.ts +8 -0
- package/dist/style/logo.js +76 -0
- package/dist/style/menu.d.ts +15 -0
- package/dist/style/menu.js +49 -0
- package/dist/style/rightContent.d.ts +12 -0
- package/dist/style/rightContent.js +74 -0
- package/dist/style/search.d.ts +12 -0
- package/dist/style/search.js +94 -0
- package/dist/style/sider.d.ts +8 -0
- package/dist/style/sider.js +92 -0
- package/dist/style/tabs.d.ts +14 -0
- package/dist/style/tabs.js +97 -0
- package/dist/theme/augment.d.ts +20 -0
- package/dist/theme/augment.js +1 -0
- package/dist/theme/interface/components.d.ts +20 -0
- package/dist/theme/interface/components.js +1 -0
- package/dist/utils/config.d.ts +12 -0
- package/dist/utils/config.js +18 -0
- package/dist/utils/menu.d.ts +111 -20
- package/dist/utils/menu.js +263 -118
- package/dist/utils/style.d.ts +4 -0
- package/dist/utils/style.js +21 -0
- package/dist/utils/themeComponents.d.ts +28 -0
- package/dist/utils/themeComponents.js +89 -0
- package/package.json +24 -15
- package/dist/Context/expose.d.ts +0 -6
- package/dist/Context/expose.js +0 -7
- package/dist/Context/index.d.ts +0 -43
- package/dist/Context/index.js +0 -18
- package/dist/WrapContent.d.ts +0 -100
- package/dist/WrapContent.js +0 -45
- package/dist/components/BaseMenu/index.d.ts +0 -255
- package/dist/components/BaseMenu/index.js +0 -381
- package/dist/components/BaseMenu/props.d.ts +0 -105
- package/dist/components/BaseMenu/props.js +0 -94
- package/dist/components/BaseMenu/style.d.ts +0 -6
- package/dist/components/BaseMenu/style.js +0 -155
- package/dist/components/BreadCrumb/Breadcrumb.d.ts +0 -33
- package/dist/components/BreadCrumb/Breadcrumb.js +0 -75
- package/dist/components/BreadCrumb/index.d.ts +0 -7
- package/dist/components/BreadCrumb/index.js +0 -44
- package/dist/components/BreadCrumb/style.d.ts +0 -8
- package/dist/components/BreadCrumb/style.js +0 -87
- package/dist/components/CollapsedIcon/index.d.ts +0 -33
- package/dist/components/CollapsedIcon/index.js +0 -51
- package/dist/components/CollapsedIcon/style.d.ts +0 -9
- package/dist/components/CollapsedIcon/style.js +0 -34
- package/dist/components/FooterToolbar/props.d.ts +0 -47
- package/dist/components/FooterToolbar/props.js +0 -33
- package/dist/components/FooterToolbar/style/index.d.ts +0 -14
- package/dist/components/FooterToolbar/style/index.js +0 -48
- package/dist/components/FooterToolbar/style/stylish.d.ts +0 -18
- package/dist/components/FooterToolbar/style/stylish.js +0 -16
- package/dist/components/GlobalFooter/index.d.ts +0 -73
- package/dist/components/GlobalFooter/index.js +0 -77
- package/dist/components/GlobalFooter/style.d.ts +0 -6
- package/dist/components/GlobalFooter/style.js +0 -29
- package/dist/components/GlobalHeader/DefaultHeader.d.ts +0 -164
- package/dist/components/GlobalHeader/DefaultHeader.js +0 -149
- package/dist/components/GlobalHeader/index.d.ts +0 -164
- package/dist/components/GlobalHeader/index.js +0 -78
- package/dist/components/GlobalHeader/props.d.ts +0 -70
- package/dist/components/GlobalHeader/props.js +0 -56
- package/dist/components/GlobalHeader/style/defaultHeader.d.ts +0 -6
- package/dist/components/GlobalHeader/style/defaultHeader.js +0 -108
- package/dist/components/GlobalHeader/style/header.d.ts +0 -6
- package/dist/components/GlobalHeader/style/header.js +0 -41
- package/dist/components/LogoContent/index.d.ts +0 -80
- package/dist/components/LogoContent/index.js +0 -90
- package/dist/components/LogoContent/props.d.ts +0 -57
- package/dist/components/LogoContent/props.js +0 -37
- package/dist/components/LogoContent/style.d.ts +0 -6
- package/dist/components/LogoContent/style.js +0 -63
- package/dist/components/PageContainer/props.d.ts +0 -138
- package/dist/components/PageContainer/props.js +0 -103
- package/dist/components/PageContainer/style/index.d.ts +0 -14
- package/dist/components/PageContainer/style/index.js +0 -52
- package/dist/components/PageContainer/style/pageHeader.d.ts +0 -6
- package/dist/components/PageContainer/style/pageHeader.js +0 -44
- package/dist/components/PageTranstion/index.d.ts +0 -40
- package/dist/components/PageTranstion/index.js +0 -71
- package/dist/components/PageTranstion/keyFrame/roll.d.ts +0 -10
- package/dist/components/PageTranstion/style.d.ts +0 -10
- package/dist/components/SiderMenu/Arrow.d.ts +0 -6
- package/dist/components/SiderMenu/SiderMenu.d.ts +0 -180
- package/dist/components/SiderMenu/SiderMenu.js +0 -265
- package/dist/components/SiderMenu/index.d.ts +0 -193
- package/dist/components/SiderMenu/index.js +0 -51
- package/dist/components/SiderMenu/props.d.ts +0 -77
- package/dist/components/SiderMenu/props.js +0 -67
- package/dist/components/SiderMenu/style/index.d.ts +0 -12
- package/dist/components/SiderMenu/style/index.js +0 -162
- package/dist/components/SiderMenu/typings.d.ts +0 -38
- package/dist/components/SiderMenu/typings.js +0 -1
- package/dist/components/TabsRoute/index.d.ts +0 -95
- package/dist/components/TabsRoute/index.js +0 -405
- package/dist/components/TabsRoute/props.d.ts +0 -78
- package/dist/components/TabsRoute/props.js +0 -47
- package/dist/components/TabsRoute/style.d.ts +0 -15
- package/dist/components/TabsRoute/style.js +0 -133
- package/dist/components/TabsRoute/utils/config.d.ts +0 -18
- package/dist/components/TabsRoute/utils/config.js +0 -64
- package/dist/config/animate.d.ts +0 -11
- package/dist/config/animate.js +0 -76
- package/dist/config/index.d.ts +0 -34
- package/dist/config/index.js +0 -30
- package/dist/hooks/collapsed/index.d.ts +0 -15
- package/dist/hooks/collapsed/index.js +0 -50
- package/dist/hooks/layout/useLayoutExpose.d.ts +0 -15
- package/dist/hooks/layout/useLayoutExpose.js +0 -22
- package/dist/hooks/layout/usePageContent.d.ts +0 -6
- package/dist/hooks/layout/usePageContent.js +0 -28
- package/dist/hooks/menu/context.d.ts +0 -21
- package/dist/hooks/menu/context.js +0 -7
- package/dist/hooks/menu/index.d.ts +0 -29
- package/dist/hooks/menu/index.js +0 -118
- package/dist/hooks/theme/index.d.ts +0 -11
- package/dist/hooks/theme/index.js +0 -13
- package/dist/props/collapsedProps.d.ts +0 -39
- package/dist/props/collapsedProps.js +0 -45
- package/dist/props/defaultSettings.d.ts +0 -62
- package/dist/props/defaultSettings.js +0 -56
- package/dist/props/index.d.ts +0 -226
- package/dist/props/index.js +0 -109
- package/dist/props/themeProps.d.ts +0 -14
- package/dist/props/themeProps.js +0 -10
- package/dist/types/Breadcrumb.d.ts +0 -22
- package/dist/types/Breadcrumb.js +0 -1
- package/dist/types/MenuTypings.d.ts +0 -46
- package/dist/types/MenuTypings.js +0 -1
- package/dist/types/RouteTypings.d.ts +0 -96
- package/dist/types/RouteTypings.js +0 -1
- package/dist/types/SlotsTypings.d.ts +0 -124
- package/dist/types/SlotsTypings.js +0 -32
- package/dist/types/typings.d.ts +0 -73
- package/dist/types/typings.js +0 -3
- package/dist/utils/index.d.ts +0 -7
- package/dist/utils/index.js +0 -17
- package/dist/utils/style/index.d.ts +0 -44
- package/dist/utils/style/index.js +0 -103
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
//#region src/components/Tabs/hooks/useAutoScroll.ts
|
|
2
|
+
const ScrollDirection = {
|
|
3
|
+
Idle: 0,
|
|
4
|
+
Forward: 1,
|
|
5
|
+
Reverse: -1
|
|
6
|
+
};
|
|
7
|
+
const SCROLL_EPSILON = .5;
|
|
8
|
+
/**
|
|
9
|
+
* 横向自动滚动 —— 精简复刻 `@dnd-kit/dom` 的 `detectScrollIntent` + `Scroller` 思路。
|
|
10
|
+
*
|
|
11
|
+
* 指针进入 `scrollEl` 左/右边缘 threshold 区域时,按距边缘的比例计算速度
|
|
12
|
+
* (越靠边越快)。滚动方向必须先被用户同向移动解锁;如果当前方向已经到达边界,
|
|
13
|
+
* 则不产生滚动 intent,避免触底后因为鼠标还在边缘区被反复重新激活。
|
|
14
|
+
*/
|
|
15
|
+
function useAutoScroll(scrollEl, getPointerX, options = {}) {
|
|
16
|
+
const { threshold = .2, acceleration = 18, tolerance = 10, onScroll } = options;
|
|
17
|
+
let rafId;
|
|
18
|
+
let lastPointerX = null;
|
|
19
|
+
let maxScrollLeft;
|
|
20
|
+
const unlockedDirections = /* @__PURE__ */ new Set();
|
|
21
|
+
function getMaxScrollLeft() {
|
|
22
|
+
const el = scrollEl.value;
|
|
23
|
+
if (!el) return 0;
|
|
24
|
+
return maxScrollLeft ?? Math.max(0, el.scrollWidth - el.clientWidth);
|
|
25
|
+
}
|
|
26
|
+
function getEdgeIntent() {
|
|
27
|
+
const el = scrollEl.value;
|
|
28
|
+
const pointerX = getPointerX();
|
|
29
|
+
if (!el || pointerX == null) return {
|
|
30
|
+
direction: ScrollDirection.Idle,
|
|
31
|
+
speed: 0
|
|
32
|
+
};
|
|
33
|
+
const rect = el.getBoundingClientRect();
|
|
34
|
+
const triggerWidth = rect.width * threshold;
|
|
35
|
+
if (triggerWidth <= 0) return {
|
|
36
|
+
direction: ScrollDirection.Idle,
|
|
37
|
+
speed: 0
|
|
38
|
+
};
|
|
39
|
+
let direction = ScrollDirection.Idle;
|
|
40
|
+
let speed = 0;
|
|
41
|
+
const distRight = rect.right - pointerX;
|
|
42
|
+
const distLeft = pointerX - rect.left;
|
|
43
|
+
const canScrollRight = canScroll(ScrollDirection.Forward);
|
|
44
|
+
const canScrollLeft = canScroll(ScrollDirection.Reverse);
|
|
45
|
+
if (canScrollRight && distRight <= triggerWidth && pointerX <= rect.right + tolerance) {
|
|
46
|
+
const ratio = Math.abs((rect.right - triggerWidth - pointerX) / triggerWidth);
|
|
47
|
+
direction = ScrollDirection.Forward;
|
|
48
|
+
speed = acceleration * Math.min(ratio, 1);
|
|
49
|
+
} else if (canScrollLeft && distLeft <= triggerWidth && pointerX >= rect.left - tolerance) {
|
|
50
|
+
const ratio = Math.abs((rect.left + triggerWidth - pointerX) / triggerWidth);
|
|
51
|
+
direction = ScrollDirection.Reverse;
|
|
52
|
+
speed = acceleration * Math.min(ratio, 1);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
direction,
|
|
56
|
+
speed
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function canScroll(direction) {
|
|
60
|
+
const el = scrollEl.value;
|
|
61
|
+
if (!el) return false;
|
|
62
|
+
const max = getMaxScrollLeft();
|
|
63
|
+
if (direction === ScrollDirection.Forward) return el.scrollLeft < max - SCROLL_EPSILON;
|
|
64
|
+
if (direction === ScrollDirection.Reverse) return el.scrollLeft > SCROLL_EPSILON;
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function applyScroll() {
|
|
68
|
+
const el = scrollEl.value;
|
|
69
|
+
if (!el) return false;
|
|
70
|
+
const { direction, speed } = getEdgeIntent();
|
|
71
|
+
if (direction === ScrollDirection.Idle) return false;
|
|
72
|
+
if (!unlockedDirections.has(direction)) return false;
|
|
73
|
+
if (!canScroll(direction)) return false;
|
|
74
|
+
const delta = direction * speed;
|
|
75
|
+
if (delta !== 0) {
|
|
76
|
+
const before = el.scrollLeft;
|
|
77
|
+
const next = Math.min(getMaxScrollLeft(), Math.max(0, before + delta));
|
|
78
|
+
if (Math.abs(next - before) <= SCROLL_EPSILON) return false;
|
|
79
|
+
el.scrollLeft = next;
|
|
80
|
+
onScroll?.();
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
function tick() {
|
|
86
|
+
rafId = void 0;
|
|
87
|
+
if (applyScroll()) rafId = requestAnimationFrame(tick);
|
|
88
|
+
}
|
|
89
|
+
function scroll() {
|
|
90
|
+
const pointerX = getPointerX();
|
|
91
|
+
if (pointerX == null) return false;
|
|
92
|
+
if (lastPointerX != null) {
|
|
93
|
+
const moveDirection = Math.sign(pointerX - lastPointerX);
|
|
94
|
+
if (moveDirection !== ScrollDirection.Idle) unlockedDirections.add(moveDirection);
|
|
95
|
+
}
|
|
96
|
+
lastPointerX = pointerX;
|
|
97
|
+
const didScroll = applyScroll();
|
|
98
|
+
if (didScroll) rafId ??= requestAnimationFrame(tick);
|
|
99
|
+
return didScroll;
|
|
100
|
+
}
|
|
101
|
+
function start() {
|
|
102
|
+
const el = scrollEl.value;
|
|
103
|
+
maxScrollLeft = el ? Math.max(0, el.scrollWidth - el.clientWidth) : void 0;
|
|
104
|
+
scroll();
|
|
105
|
+
}
|
|
106
|
+
function stop() {
|
|
107
|
+
if (rafId != null) {
|
|
108
|
+
cancelAnimationFrame(rafId);
|
|
109
|
+
rafId = void 0;
|
|
110
|
+
}
|
|
111
|
+
lastPointerX = null;
|
|
112
|
+
maxScrollLeft = void 0;
|
|
113
|
+
unlockedDirections.clear();
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
scroll,
|
|
117
|
+
start,
|
|
118
|
+
stop
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { useAutoScroll };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { InnerTabItem } from "../interface.js";
|
|
2
|
+
import { ComputedRef, Ref } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/components/Tabs/hooks/useTabDrag.d.ts
|
|
5
|
+
interface UseTabDragOptions {
|
|
6
|
+
items: ComputedRef<InnerTabItem[]>;
|
|
7
|
+
scrollEl: Ref<HTMLElement | undefined>;
|
|
8
|
+
onReorder: (fromKey: string, toKey: string) => void;
|
|
9
|
+
enabled?: ComputedRef<boolean>;
|
|
10
|
+
}
|
|
11
|
+
interface TabDragItemProps {
|
|
12
|
+
onMousedown: (event: MouseEvent) => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 页签拖拽排序 —— 精简复刻 dnd-kit 的 useDraggable + sortable 实时让位。
|
|
16
|
+
*
|
|
17
|
+
* - 移动阈值(3px)区分点击与拖拽:未超阈值视为点击,让原生 click 走切换
|
|
18
|
+
* - 拖拽项 `transform: translateX(delta)` 跟随指针
|
|
19
|
+
* - 同组其他 tab 按虚拟插入位置实时 `translate` 让位(CSS translate 属性)
|
|
20
|
+
* - drop 锚点只在 `item.group === dragGroup` 内查找 → 天然不跨级
|
|
21
|
+
* - 松手 → onReorder 触发数据重排 → nextTick 用 flipAnimate(WAAPI FLIP)平滑归位
|
|
22
|
+
* - 拖拽结束后捕获抑制下一次 click,避免误触发 tab 切换
|
|
23
|
+
* - 边缘自动滚动串联 useAutoScroll
|
|
24
|
+
*/
|
|
25
|
+
declare function useTabDrag(options: UseTabDragOptions): {
|
|
26
|
+
isDragging: Ref<boolean, boolean>;
|
|
27
|
+
draggingKey: Ref<string, string>;
|
|
28
|
+
getItemProps: (key: string) => TabDragItemProps;
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { TabDragItemProps, UseTabDragOptions, useTabDrag };
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { flipAnimate } from "./flipAnimate.js";
|
|
2
|
+
import { useAutoScroll } from "./useAutoScroll.js";
|
|
3
|
+
import { nextTick, ref } from "vue";
|
|
4
|
+
import { useEventListener } from "@vueuse/core";
|
|
5
|
+
//#region src/components/Tabs/hooks/useTabDrag.ts
|
|
6
|
+
const IGNORE_SELECTOR = "button, a, input, [data-drag-ignore=\"true\"]";
|
|
7
|
+
/** 移动阈值(px),超过才视为拖拽,避免误触影响点击切换 */
|
|
8
|
+
const DRAG_THRESHOLD = 3;
|
|
9
|
+
const SCROLL_RESTORE_FRAMES = 2;
|
|
10
|
+
const DISPLACEMENT_DURATION = 160;
|
|
11
|
+
const DISPLACEMENT_EASING = "cubic-bezier(0.2, 0, 0, 1)";
|
|
12
|
+
function isIgnoredTarget(target) {
|
|
13
|
+
return target instanceof HTMLElement && Boolean(target.closest(IGNORE_SELECTOR));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 页签拖拽排序 —— 精简复刻 dnd-kit 的 useDraggable + sortable 实时让位。
|
|
17
|
+
*
|
|
18
|
+
* - 移动阈值(3px)区分点击与拖拽:未超阈值视为点击,让原生 click 走切换
|
|
19
|
+
* - 拖拽项 `transform: translateX(delta)` 跟随指针
|
|
20
|
+
* - 同组其他 tab 按虚拟插入位置实时 `translate` 让位(CSS translate 属性)
|
|
21
|
+
* - drop 锚点只在 `item.group === dragGroup` 内查找 → 天然不跨级
|
|
22
|
+
* - 松手 → onReorder 触发数据重排 → nextTick 用 flipAnimate(WAAPI FLIP)平滑归位
|
|
23
|
+
* - 拖拽结束后捕获抑制下一次 click,避免误触发 tab 切换
|
|
24
|
+
* - 边缘自动滚动串联 useAutoScroll
|
|
25
|
+
*/
|
|
26
|
+
function useTabDrag(options) {
|
|
27
|
+
const { items, scrollEl, onReorder, enabled } = options;
|
|
28
|
+
const isDragging = ref(false);
|
|
29
|
+
const draggingKey = ref("");
|
|
30
|
+
let dragGroup;
|
|
31
|
+
let dragStartClientX = 0;
|
|
32
|
+
let dragStartScrollLeft = 0;
|
|
33
|
+
let dragPointerOffsetX = 0;
|
|
34
|
+
let pointerClientX = 0;
|
|
35
|
+
let dragEl = null;
|
|
36
|
+
let groupRects = /* @__PURE__ */ new Map();
|
|
37
|
+
let pendingKey = "";
|
|
38
|
+
let pendingStartX = 0;
|
|
39
|
+
const { scroll: scrollOnDragMove, start: startAutoScroll, stop: stopAutoScroll } = useAutoScroll(scrollEl, () => pointerClientX, { onScroll: updateDragPosition });
|
|
40
|
+
function getGroupItems() {
|
|
41
|
+
return items.value.filter((item) => item.group === dragGroup);
|
|
42
|
+
}
|
|
43
|
+
function queryTabEl(key) {
|
|
44
|
+
return scrollEl.value?.querySelector(`[data-key="${key}"]`) ?? null;
|
|
45
|
+
}
|
|
46
|
+
function recordGroupRects() {
|
|
47
|
+
groupRects = /* @__PURE__ */ new Map();
|
|
48
|
+
for (const item of getGroupItems()) {
|
|
49
|
+
const el = queryTabEl(item.key);
|
|
50
|
+
if (!el) continue;
|
|
51
|
+
const rect = el.getBoundingClientRect();
|
|
52
|
+
groupRects.set(item.key, {
|
|
53
|
+
el,
|
|
54
|
+
left: rect.left,
|
|
55
|
+
width: rect.width,
|
|
56
|
+
translate: 0
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function getCurrentScrollOffset() {
|
|
61
|
+
return (scrollEl.value?.scrollLeft ?? 0) - dragStartScrollLeft;
|
|
62
|
+
}
|
|
63
|
+
function getVisualPointerX() {
|
|
64
|
+
return pointerClientX + getCurrentScrollOffset();
|
|
65
|
+
}
|
|
66
|
+
function getDragCenterX() {
|
|
67
|
+
const dragRect = groupRects.get(draggingKey.value);
|
|
68
|
+
if (!dragRect) return getVisualPointerX();
|
|
69
|
+
return getVisualPointerX() - dragPointerOffsetX + dragRect.width / 2;
|
|
70
|
+
}
|
|
71
|
+
function getItemDisplacement(fromIdx, toIdx, dragWidth) {
|
|
72
|
+
const fromRect = groupRects.get(draggingKey.value);
|
|
73
|
+
const nextKey = fromIdx < toIdx ? getGroupItems()[fromIdx + 1]?.key : getGroupItems()[fromIdx - 1]?.key;
|
|
74
|
+
const nextRect = nextKey ? groupRects.get(nextKey) : void 0;
|
|
75
|
+
return dragWidth + (fromRect && nextRect ? Math.max(0, Math.abs(nextRect.left - fromRect.left) - (fromIdx < toIdx ? fromRect.width : nextRect.width)) : 0);
|
|
76
|
+
}
|
|
77
|
+
/** 计算拖拽项应去的目标项索引(匹配 reorderWithinGroup 的 toKey 语义) */
|
|
78
|
+
function computeTargetIdx(dragCenterX) {
|
|
79
|
+
const groupList = getGroupItems();
|
|
80
|
+
if (groupList.findIndex((item) => item.key === draggingKey.value) < 0) return -1;
|
|
81
|
+
const others = groupList.filter((item) => item.key !== draggingKey.value);
|
|
82
|
+
let targetIdx = others.length;
|
|
83
|
+
for (let i = 0; i < others.length; i++) {
|
|
84
|
+
const rect = groupRects.get(others[i].key);
|
|
85
|
+
if (!rect) continue;
|
|
86
|
+
if (dragCenterX < rect.left + rect.width / 2) {
|
|
87
|
+
targetIdx = i;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return targetIdx;
|
|
92
|
+
}
|
|
93
|
+
/** 同组其他 tab 实时让位(区间内项 translate ±dragWidth) */
|
|
94
|
+
function applyDisplacement(targetIdx) {
|
|
95
|
+
const groupList = getGroupItems();
|
|
96
|
+
const fromIdx = groupList.findIndex((item) => item.key === draggingKey.value);
|
|
97
|
+
if (fromIdx < 0) return;
|
|
98
|
+
const dragWidth = groupRects.get(draggingKey.value)?.width ?? 0;
|
|
99
|
+
for (let i = 0; i < groupList.length; i++) {
|
|
100
|
+
const key = groupList[i].key;
|
|
101
|
+
if (key === draggingKey.value) continue;
|
|
102
|
+
const rect = groupRects.get(key);
|
|
103
|
+
if (!rect) continue;
|
|
104
|
+
let translate = 0;
|
|
105
|
+
if (fromIdx < targetIdx && i > fromIdx && i <= targetIdx) translate = -getItemDisplacement(fromIdx, targetIdx, dragWidth);
|
|
106
|
+
else if (fromIdx > targetIdx && i >= targetIdx && i < fromIdx) translate = getItemDisplacement(fromIdx, targetIdx, dragWidth);
|
|
107
|
+
applyTabTranslate(rect, translate);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function applyTabTranslate(rect, nextTranslate) {
|
|
111
|
+
const prevTranslate = rect.translate;
|
|
112
|
+
if (prevTranslate === nextTranslate) return;
|
|
113
|
+
rect.animation?.cancel();
|
|
114
|
+
rect.translate = nextTranslate;
|
|
115
|
+
rect.el.style.translate = nextTranslate ? `${nextTranslate}px` : "";
|
|
116
|
+
if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches || typeof rect.el.animate !== "function") return;
|
|
117
|
+
rect.animation = rect.el.animate({ translate: [`${prevTranslate}px`, `${nextTranslate}px`] }, {
|
|
118
|
+
duration: DISPLACEMENT_DURATION,
|
|
119
|
+
easing: DISPLACEMENT_EASING
|
|
120
|
+
});
|
|
121
|
+
const clearAnimation = () => {
|
|
122
|
+
rect.animation = void 0;
|
|
123
|
+
};
|
|
124
|
+
rect.animation.addEventListener("finish", clearAnimation, { once: true });
|
|
125
|
+
rect.animation.addEventListener("cancel", clearAnimation, { once: true });
|
|
126
|
+
}
|
|
127
|
+
function updateDragPosition() {
|
|
128
|
+
if (!isDragging.value) return;
|
|
129
|
+
if (dragEl) dragEl.style.transform = `translateX(${getVisualPointerX() - dragStartClientX}px)`;
|
|
130
|
+
applyDisplacement(computeTargetIdx(getDragCenterX()));
|
|
131
|
+
}
|
|
132
|
+
function clearInlineStyles(rects = groupRects, activeDragEl = dragEl) {
|
|
133
|
+
for (const rect of rects.values()) {
|
|
134
|
+
rect.animation?.cancel();
|
|
135
|
+
rect.animation = void 0;
|
|
136
|
+
rect.translate = 0;
|
|
137
|
+
const { el } = rect;
|
|
138
|
+
el.style.translate = "";
|
|
139
|
+
}
|
|
140
|
+
if (activeDragEl) activeDragEl.style.transform = "";
|
|
141
|
+
}
|
|
142
|
+
function restoreScrollLeft(scrollLeft, frames = SCROLL_RESTORE_FRAMES) {
|
|
143
|
+
const el = scrollEl.value;
|
|
144
|
+
if (!el) return;
|
|
145
|
+
el.scrollLeft = scrollLeft;
|
|
146
|
+
if (frames <= 0 || typeof requestAnimationFrame === "undefined") return;
|
|
147
|
+
requestAnimationFrame(() => restoreScrollLeft(scrollLeft, frames - 1));
|
|
148
|
+
}
|
|
149
|
+
function beginDrag(clientX) {
|
|
150
|
+
isDragging.value = true;
|
|
151
|
+
dragStartClientX = clientX;
|
|
152
|
+
dragStartScrollLeft = scrollEl.value?.scrollLeft ?? 0;
|
|
153
|
+
pointerClientX = clientX;
|
|
154
|
+
dragEl = queryTabEl(draggingKey.value);
|
|
155
|
+
recordGroupRects();
|
|
156
|
+
const dragRect = groupRects.get(draggingKey.value);
|
|
157
|
+
dragPointerOffsetX = dragRect ? clientX - dragRect.left : 0;
|
|
158
|
+
startAutoScroll();
|
|
159
|
+
}
|
|
160
|
+
/** 捕获阶段抑制拖拽结束后的下一次 click,避免误触发切换 */
|
|
161
|
+
function suppressNextClick() {
|
|
162
|
+
const target = dragEl;
|
|
163
|
+
if (!target) return;
|
|
164
|
+
const suppress = (event) => {
|
|
165
|
+
event.stopPropagation();
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
target.removeEventListener("click", suppress, true);
|
|
168
|
+
};
|
|
169
|
+
target.addEventListener("click", suppress, {
|
|
170
|
+
capture: true,
|
|
171
|
+
once: true
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function handleMousedown(event, item) {
|
|
175
|
+
if (event.button !== 0 || item.disabled) return;
|
|
176
|
+
if (isIgnoredTarget(event.target)) return;
|
|
177
|
+
if (enabled && !enabled.value) return;
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
pendingKey = item.key;
|
|
180
|
+
pendingStartX = event.clientX;
|
|
181
|
+
draggingKey.value = item.key;
|
|
182
|
+
dragGroup = item.group;
|
|
183
|
+
}
|
|
184
|
+
function handleMousemove(event) {
|
|
185
|
+
if (!isDragging.value) {
|
|
186
|
+
if (!pendingKey) return;
|
|
187
|
+
if (Math.abs(event.clientX - pendingStartX) < DRAG_THRESHOLD) return;
|
|
188
|
+
beginDrag(pendingStartX);
|
|
189
|
+
}
|
|
190
|
+
pointerClientX = event.clientX;
|
|
191
|
+
updateDragPosition();
|
|
192
|
+
scrollOnDragMove();
|
|
193
|
+
}
|
|
194
|
+
function endDrag() {
|
|
195
|
+
const targetIdx = computeTargetIdx(getDragCenterX());
|
|
196
|
+
const groupList = getGroupItems();
|
|
197
|
+
const fromIdx = groupList.findIndex((item) => item.key === draggingKey.value);
|
|
198
|
+
const prevRects = /* @__PURE__ */ new Map();
|
|
199
|
+
for (const [key, { el }] of groupRects) prevRects.set(key, el.getBoundingClientRect());
|
|
200
|
+
stopAutoScroll();
|
|
201
|
+
const dragKey = draggingKey.value;
|
|
202
|
+
const targetItem = groupList[targetIdx];
|
|
203
|
+
const hasReorder = targetIdx >= 0 && targetIdx !== fromIdx && targetItem && targetItem.key !== dragKey;
|
|
204
|
+
const currentGroupRects = groupRects;
|
|
205
|
+
const currentDragEl = dragEl;
|
|
206
|
+
const currentScrollLeft = scrollEl.value?.scrollLeft ?? 0;
|
|
207
|
+
if (hasReorder) {
|
|
208
|
+
onReorder(dragKey, targetItem.key);
|
|
209
|
+
nextTick(() => {
|
|
210
|
+
restoreScrollLeft(currentScrollLeft);
|
|
211
|
+
clearInlineStyles(currentGroupRects, currentDragEl);
|
|
212
|
+
flipAnimate(groupList.map((item) => ({
|
|
213
|
+
key: item.key,
|
|
214
|
+
el: queryTabEl(item.key)
|
|
215
|
+
})).filter((entry) => Boolean(entry.el)), prevRects);
|
|
216
|
+
restoreScrollLeft(currentScrollLeft);
|
|
217
|
+
});
|
|
218
|
+
} else clearInlineStyles();
|
|
219
|
+
suppressNextClick();
|
|
220
|
+
isDragging.value = false;
|
|
221
|
+
draggingKey.value = "";
|
|
222
|
+
dragEl = null;
|
|
223
|
+
groupRects = /* @__PURE__ */ new Map();
|
|
224
|
+
dragPointerOffsetX = 0;
|
|
225
|
+
pendingKey = "";
|
|
226
|
+
dragGroup = void 0;
|
|
227
|
+
}
|
|
228
|
+
function handleMouseup() {
|
|
229
|
+
if (isDragging.value) endDrag();
|
|
230
|
+
else if (pendingKey) {
|
|
231
|
+
pendingKey = "";
|
|
232
|
+
draggingKey.value = "";
|
|
233
|
+
dragGroup = void 0;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const documentTarget = typeof document === "undefined" ? void 0 : document;
|
|
237
|
+
useEventListener(documentTarget, "mousemove", handleMousemove);
|
|
238
|
+
useEventListener(documentTarget, "mouseup", handleMouseup);
|
|
239
|
+
function getItemProps(key) {
|
|
240
|
+
const item = items.value.find((entry) => entry.key === key);
|
|
241
|
+
return { onMousedown: (event) => {
|
|
242
|
+
if (item) handleMousedown(event, item);
|
|
243
|
+
} };
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
isDragging,
|
|
247
|
+
draggingKey,
|
|
248
|
+
getItemProps
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
//#endregion
|
|
252
|
+
export { useTabDrag };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LayoutTabsEmits, LayoutTabsProps, LayoutTabsSlots } from "./interface.js";
|
|
2
|
+
import { SlotsType } from "vue";
|
|
3
|
+
|
|
4
|
+
//#region src/components/Tabs/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* 标签栏默认高度(px,首帧兜底值,基于默认主题实测取整)。
|
|
7
|
+
* 真实高度由 LayoutTabs 挂载后用 useElementSize 测量并写回 context,
|
|
8
|
+
* 此常量仅用于测量回填前的占位,避免 fixed 占位首帧塌陷。
|
|
9
|
+
*/
|
|
10
|
+
declare const DEFAULT_TABS_HEIGHT = 41;
|
|
11
|
+
declare const LayoutTabs: import("vue").DefineSetupFnComponent<LayoutTabsProps, LayoutTabsEmits, SlotsType<LayoutTabsSlots>, LayoutTabsProps, import("vue").PublicProps>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { DEFAULT_TABS_HEIGHT, LayoutTabs as default };
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { useLayoutBase } from "../../hooks/useLayoutBase.js";
|
|
2
|
+
import { CONTEXT_MENU_ITEMS, getDisabledState, getMenuItemLabel } from "./contextMenu.js";
|
|
3
|
+
import InnerTabs from "./InnerTabs.js";
|
|
4
|
+
import { Fragment, computed, createVNode, defineComponent, ref, watch } from "vue";
|
|
5
|
+
import { unit } from "@gx-design-vue/pro-provider";
|
|
6
|
+
import { classNames } from "@gx-design-vue/pro-utils";
|
|
7
|
+
import { useElementSize } from "@vueuse/core";
|
|
8
|
+
import { Dropdown } from "antdv-next";
|
|
9
|
+
import { GIcon } from "@gx-design-vue/icon";
|
|
10
|
+
//#region src/components/Tabs/index.tsx
|
|
11
|
+
/**
|
|
12
|
+
* 标签栏默认高度(px,首帧兜底值,基于默认主题实测取整)。
|
|
13
|
+
* 真实高度由 LayoutTabs 挂载后用 useElementSize 测量并写回 context,
|
|
14
|
+
* 此常量仅用于测量回填前的占位,避免 fixed 占位首帧塌陷。
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_TABS_HEIGHT = 41;
|
|
17
|
+
const LayoutTabs = /* @__PURE__ */ defineComponent((props, { slots }) => {
|
|
18
|
+
const { prefixCls, proClasses, tabsState, tabsHeight, setTabsHeight, contentFullscreen, header, isMobile } = useLayoutBase();
|
|
19
|
+
const contextMenuActiveKey = ref("");
|
|
20
|
+
const tabsContentRef = ref();
|
|
21
|
+
const { height: measuredTabsHeight } = useElementSize(tabsContentRef, void 0, { box: "border-box" });
|
|
22
|
+
watch(measuredTabsHeight, (height) => {
|
|
23
|
+
if (height > 0) setTabsHeight?.(height);
|
|
24
|
+
}, { immediate: true });
|
|
25
|
+
const dataSource = computed(() => tabsState?.dataSource?.value ?? []);
|
|
26
|
+
const activeKey = computed(() => tabsState?.activeKey?.value ?? "");
|
|
27
|
+
const checkIsFixed = computed(() => tabsState?.checkIsFixed ?? (() => false));
|
|
28
|
+
const isFixed = computed(() => !!props.config?.fixed && !isMobile.value);
|
|
29
|
+
const tabsClassNames = computed(() => classNames(`${prefixCls.value}-tabs`, proClasses.value.tabs, {
|
|
30
|
+
[`${prefixCls.value}-tabs-${props.config?.type}`]: props.config?.type,
|
|
31
|
+
[`${prefixCls.value}-tabs-fixed`]: isFixed.value
|
|
32
|
+
}));
|
|
33
|
+
const tabsContentStyle = computed(() => {
|
|
34
|
+
const style = {};
|
|
35
|
+
if (isFixed.value) {
|
|
36
|
+
const tabsWidthValue = tabsState?.tabsWidth?.value;
|
|
37
|
+
if (tabsWidthValue) style.width = tabsWidthValue;
|
|
38
|
+
style.top = contentFullscreen.value ? "0" : unit(header.value?.height);
|
|
39
|
+
}
|
|
40
|
+
return style;
|
|
41
|
+
});
|
|
42
|
+
function buildContextMenuItems(targetName) {
|
|
43
|
+
const isFixed = checkIsFixed.value(getRouteByName(targetName));
|
|
44
|
+
return CONTEXT_MENU_ITEMS.map((item) => {
|
|
45
|
+
if (item.key === "divider") return {
|
|
46
|
+
type: "divider",
|
|
47
|
+
key: "divider"
|
|
48
|
+
};
|
|
49
|
+
const menuKey = item.key;
|
|
50
|
+
return {
|
|
51
|
+
key: menuKey,
|
|
52
|
+
icon: typeof item.icon === "function" ? item.icon(menuKey === "fixed" ? isFixed : contentFullscreen.value) : item.icon,
|
|
53
|
+
label: getMenuItemLabel(menuKey, {
|
|
54
|
+
isFixed,
|
|
55
|
+
isFullscreen: contentFullscreen.value
|
|
56
|
+
}),
|
|
57
|
+
disabled: getDisabledState(menuKey, targetName, dataSource.value, isFixed)
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function getRouteByName(name) {
|
|
62
|
+
return dataSource.value.find((item) => item.name === name) ?? {
|
|
63
|
+
name,
|
|
64
|
+
meta: {}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const innerItems = computed(() => dataSource.value.map((route) => ({
|
|
68
|
+
key: route.name,
|
|
69
|
+
label: route.meta?.title,
|
|
70
|
+
closable: !checkIsFixed.value(route) && dataSource.value.length > 1,
|
|
71
|
+
group: checkIsFixed.value(route) ? "fixed" : "normal"
|
|
72
|
+
})));
|
|
73
|
+
function renderTabSlot({ item }) {
|
|
74
|
+
const route = getRouteByName(item.key);
|
|
75
|
+
const routeName = route.name;
|
|
76
|
+
const isFixed = checkIsFixed.value(route);
|
|
77
|
+
const customRender = slots.tabBarItem?.({ route }) ?? props.tabBarItem?.({ route });
|
|
78
|
+
return createVNode(Dropdown, {
|
|
79
|
+
"trigger": ["contextmenu"],
|
|
80
|
+
"menu": {
|
|
81
|
+
items: buildContextMenuItems(routeName),
|
|
82
|
+
onClick: ({ key }) => {
|
|
83
|
+
tabsState?.handleContextMenuAction?.(key, routeName);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"onOpenChange": (open) => {
|
|
87
|
+
contextMenuActiveKey.value = open ? routeName : "";
|
|
88
|
+
}
|
|
89
|
+
}, { default: () => [customRender ?? createVNode("div", { "class": classNames(`${prefixCls.value}-tabs-title`) }, [
|
|
90
|
+
createVNode("span", { "class": `${prefixCls.value}-tabs-title-text` }, [route.meta?.title]),
|
|
91
|
+
isFixed && createVNode(GIcon, {
|
|
92
|
+
"type": "Pin",
|
|
93
|
+
"class": `${prefixCls.value}-tabs-title-pin`,
|
|
94
|
+
"data-drag-ignore": "true"
|
|
95
|
+
}, null),
|
|
96
|
+
dataSource.value.length > 1 && !isFixed && createVNode(GIcon, {
|
|
97
|
+
"type": "CloseOutlined",
|
|
98
|
+
"class": `${prefixCls.value}-tabs-title-close`,
|
|
99
|
+
"data-drag-ignore": "true",
|
|
100
|
+
"onClick": (event) => {
|
|
101
|
+
event.stopPropagation();
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
tabsState?.handleTabRemove?.(routeName);
|
|
104
|
+
}
|
|
105
|
+
}, null)
|
|
106
|
+
])] });
|
|
107
|
+
}
|
|
108
|
+
function renderRightExtra() {
|
|
109
|
+
if (dataSource.value.length === 0) return null;
|
|
110
|
+
return createVNode("div", { "class": classNames(`${prefixCls.value}-tabs-extra`) }, [
|
|
111
|
+
createVNode(GIcon, {
|
|
112
|
+
"type": "ReloadOutlined",
|
|
113
|
+
"class": `${prefixCls.value}-tabs-extra-btn`,
|
|
114
|
+
"onClick": () => {
|
|
115
|
+
tabsState?.handleContextMenuAction?.("reloadPage", activeKey.value);
|
|
116
|
+
}
|
|
117
|
+
}, null),
|
|
118
|
+
createVNode(Dropdown, { "menu": {
|
|
119
|
+
items: buildContextMenuItems(activeKey.value),
|
|
120
|
+
onClick: ({ key }) => {
|
|
121
|
+
tabsState?.handleContextMenuAction?.(key, activeKey.value);
|
|
122
|
+
}
|
|
123
|
+
} }, { default: () => [createVNode(GIcon, {
|
|
124
|
+
"type": "DownOutlined",
|
|
125
|
+
"class": `${prefixCls.value}-tabs-extra-btn`
|
|
126
|
+
}, null)] }),
|
|
127
|
+
createVNode(GIcon, {
|
|
128
|
+
"type": contentFullscreen.value ? "FullscreenExitOutlined" : "FullscreenOutlined",
|
|
129
|
+
"class": `${prefixCls.value}-tabs-extra-btn`,
|
|
130
|
+
"onClick": () => {
|
|
131
|
+
tabsState?.handleContextMenuAction?.("fullScreen", activeKey.value);
|
|
132
|
+
}
|
|
133
|
+
}, null)
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
return () => {
|
|
137
|
+
if (!tabsState || dataSource.value.length === 0) return null;
|
|
138
|
+
return createVNode(Fragment, null, [createVNode("div", { "class": tabsClassNames.value }, [isFixed.value && createVNode("div", {
|
|
139
|
+
"class": `${prefixCls.value}-tabs-placeholder`,
|
|
140
|
+
"style": { height: unit(tabsHeight?.value ?? 41) }
|
|
141
|
+
}, null), createVNode("div", {
|
|
142
|
+
"ref": tabsContentRef,
|
|
143
|
+
"class": classNames(`${prefixCls.value}-tabs-content`),
|
|
144
|
+
"style": tabsContentStyle.value
|
|
145
|
+
}, [createVNode(InnerTabs, {
|
|
146
|
+
"items": innerItems.value,
|
|
147
|
+
"activeKey": activeKey.value,
|
|
148
|
+
"draggable": props.config?.draggable !== false,
|
|
149
|
+
"onChange": (key) => {
|
|
150
|
+
tabsState?.handleTabClick?.(key);
|
|
151
|
+
},
|
|
152
|
+
"onReorder": (fromKey, toKey) => {
|
|
153
|
+
const fromItem = innerItems.value.find((item) => item.key === fromKey);
|
|
154
|
+
if (fromItem?.group) tabsState?.reorderWithinGroup?.(fromKey, toKey, fromItem.group);
|
|
155
|
+
}
|
|
156
|
+
}, {
|
|
157
|
+
tab: renderTabSlot,
|
|
158
|
+
extra: renderRightExtra
|
|
159
|
+
})])])]);
|
|
160
|
+
};
|
|
161
|
+
}, {
|
|
162
|
+
props: {
|
|
163
|
+
config: {
|
|
164
|
+
type: Object,
|
|
165
|
+
required: false
|
|
166
|
+
},
|
|
167
|
+
tabBarItem: {
|
|
168
|
+
type: Function,
|
|
169
|
+
required: false
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
emits: [
|
|
173
|
+
"tabsChange",
|
|
174
|
+
"reloadPage",
|
|
175
|
+
"contentFullscreenChange"
|
|
176
|
+
],
|
|
177
|
+
name: "LayoutTabs",
|
|
178
|
+
inheritAttrs: false
|
|
179
|
+
});
|
|
180
|
+
//#endregion
|
|
181
|
+
export { DEFAULT_TABS_HEIGHT, LayoutTabs as default };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { LayoutMenuRoute, LayoutTabsConfig } from "../../interface.js";
|
|
2
|
+
import { CustomRender } from "@gx-design-vue/pro-utils";
|
|
3
|
+
|
|
4
|
+
//#region src/components/Tabs/interface.d.ts
|
|
5
|
+
/** 页签分组(拖拽排序约束:组内可拖,不跨级) */
|
|
6
|
+
type TabGroup = 'fixed' | 'normal';
|
|
7
|
+
/** 单个页签数据 */
|
|
8
|
+
interface InnerTabItem {
|
|
9
|
+
/** 唯一标识 */
|
|
10
|
+
key: string;
|
|
11
|
+
/** 标题(string / VNode),未提供 #tab slot 时默认渲染 */
|
|
12
|
+
label?: any;
|
|
13
|
+
/** 是否显示关闭按钮,默认 true */
|
|
14
|
+
closable?: boolean;
|
|
15
|
+
/** 是否禁用 */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/** 所属分组(拖拽排序时约束组内,不跨级) */
|
|
18
|
+
group?: TabGroup;
|
|
19
|
+
}
|
|
20
|
+
interface InnerTabsEmits {
|
|
21
|
+
'update:activeKey': (key: string) => any;
|
|
22
|
+
'change': (key: string) => any;
|
|
23
|
+
'close': (key: string) => any;
|
|
24
|
+
/** 拖拽排序:从 fromKey 移到 toKey 位置(同组内) */
|
|
25
|
+
'reorder': (fromKey: string, toKey: string) => any;
|
|
26
|
+
}
|
|
27
|
+
interface InnerTabsEmitsProps {
|
|
28
|
+
'onUpdate:activeKey'?: InnerTabsEmits['update:activeKey'];
|
|
29
|
+
onChange?: InnerTabsEmits['change'];
|
|
30
|
+
onClose?: InnerTabsEmits['close'];
|
|
31
|
+
onReorder?: InnerTabsEmits['reorder'];
|
|
32
|
+
}
|
|
33
|
+
interface InnerTabsProps extends InnerTabsEmitsProps {
|
|
34
|
+
prefixCls?: string;
|
|
35
|
+
/** 页签列表 */
|
|
36
|
+
items?: InnerTabItem[];
|
|
37
|
+
/** 当前激活 key(支持 v-model:activeKey) */
|
|
38
|
+
activeKey?: string;
|
|
39
|
+
/** 是否开启拖拽排序,默认 true */
|
|
40
|
+
draggable?: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface InnerTabsSlots {
|
|
43
|
+
/** 自定义单个页签内容(如上层挂右键菜单 / 图标) */
|
|
44
|
+
tab?: (props: {
|
|
45
|
+
item: InnerTabItem;
|
|
46
|
+
active: boolean;
|
|
47
|
+
}) => any;
|
|
48
|
+
/** 导航栏右侧额外区域 */
|
|
49
|
+
extra?: () => any;
|
|
50
|
+
}
|
|
51
|
+
interface LayoutTabsEmits {
|
|
52
|
+
'tabsChange': (tabs: LayoutMenuRoute[]) => void;
|
|
53
|
+
'reloadPage': () => void;
|
|
54
|
+
'contentFullscreenChange': (fullscreen: boolean) => void;
|
|
55
|
+
}
|
|
56
|
+
interface LayoutTabsEmitsProps {
|
|
57
|
+
onTabsChange?: LayoutTabsEmits['tabsChange'];
|
|
58
|
+
onReloadPage?: LayoutTabsEmits['reloadPage'];
|
|
59
|
+
onContentFullscreenChange?: LayoutTabsEmits['contentFullscreenChange'];
|
|
60
|
+
}
|
|
61
|
+
interface LayoutTabsProps extends LayoutTabsEmitsProps {
|
|
62
|
+
config?: LayoutTabsConfig;
|
|
63
|
+
/** 标签项自定义(等价 #tabBarItem slot) */
|
|
64
|
+
tabBarItem?: (slotProps: {
|
|
65
|
+
route: LayoutMenuRoute;
|
|
66
|
+
}) => CustomRender;
|
|
67
|
+
}
|
|
68
|
+
interface LayoutTabsSlots {
|
|
69
|
+
tabBarItem?: (props: {
|
|
70
|
+
route: LayoutMenuRoute;
|
|
71
|
+
}) => any;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { InnerTabItem, InnerTabsEmits, InnerTabsEmitsProps, InnerTabsProps, InnerTabsSlots, LayoutTabsEmits, LayoutTabsEmitsProps, LayoutTabsProps, LayoutTabsSlots, TabGroup };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|