@bifrostui/react 2.0.0-alpha.17 → 2.0.0-alpha.19
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/Modal/Modal.miniapp.d.ts +1 -1
- package/dist/Tabs/TabIndicator.d.ts +0 -2
- package/dist/Tabs/TabIndicator.js +83 -37
- package/dist/Tabs/TabMask.d.ts +2 -4
- package/dist/Tabs/TabMask.js +12 -53
- package/dist/Tabs/Tabs.css +1 -1
- package/dist/Tabs/Tabs.js +17 -6
- package/dist/Tabs/index.css +1 -1
- package/dist/Tabs/miniapp/TabIndicator.js +109 -14
- package/dist/Tabs/miniapp/Tabs.js +12 -36
- package/es/Modal/Modal.miniapp.d.ts +1 -1
- package/es/Tabs/TabIndicator.d.ts +0 -2
- package/es/Tabs/TabIndicator.js +84 -38
- package/es/Tabs/TabMask.d.ts +2 -4
- package/es/Tabs/TabMask.js +13 -54
- package/es/Tabs/Tabs.css +1 -1
- package/es/Tabs/Tabs.js +18 -7
- package/es/Tabs/index.css +1 -1
- package/es/Tabs/miniapp/TabIndicator.js +110 -15
- package/es/Tabs/miniapp/Tabs.js +12 -36
- package/package.json +5 -5
- package/dist/Tabs/miniapp/TabMask.d.ts +0 -13
- package/dist/Tabs/miniapp/TabMask.js +0 -66
- package/es/Tabs/miniapp/TabMask.d.ts +0 -13
- package/es/Tabs/miniapp/TabMask.js +0 -37
|
@@ -6,8 +6,6 @@ export interface TabIndicatorProps {
|
|
|
6
6
|
registeredTabs: React.MutableRefObject<Record<string, React.RefObject<HTMLElement>>>;
|
|
7
7
|
/** tabs 容器的引用 */
|
|
8
8
|
tabsContainerRef: React.RefObject<HTMLDivElement>;
|
|
9
|
-
/** 注册版本号,每次 tab 注册/取消注册时递增 */
|
|
10
|
-
registrationVersion: number;
|
|
11
9
|
}
|
|
12
10
|
declare const TabIndicator: React.FC<TabIndicatorProps>;
|
|
13
11
|
export default TabIndicator;
|
package/es/Tabs/TabIndicator.js
CHANGED
|
@@ -1,80 +1,126 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from "react";
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import clsx from "clsx";
|
|
3
3
|
import { debounce, isMini, useEventCallback } from "@bifrostui/utils";
|
|
4
4
|
import scrollLeftTo from "./utils/scroll";
|
|
5
5
|
const rootClass = "bui-tabs";
|
|
6
6
|
const duration = 300;
|
|
7
|
+
const DEFAULT_INDICATOR_WIDTH = 24;
|
|
7
8
|
const TabIndicator = ({
|
|
8
9
|
currentValue,
|
|
9
10
|
registeredTabs,
|
|
10
|
-
tabsContainerRef
|
|
11
|
-
registrationVersion
|
|
11
|
+
tabsContainerRef
|
|
12
12
|
}) => {
|
|
13
13
|
const indicatorRef = useRef(null);
|
|
14
|
+
const hasRenderedOnce = useRef(false);
|
|
15
|
+
const indicatorWidthCache = useRef(null);
|
|
16
|
+
const [indicatorStyle, setIndicatorStyle] = useState(
|
|
17
|
+
null
|
|
18
|
+
);
|
|
14
19
|
const getActiveTabElement = useEventCallback(
|
|
15
20
|
(activeValue) => {
|
|
16
21
|
const tabRef = registeredTabs.current[activeValue];
|
|
17
22
|
return tabRef == null ? void 0 : tabRef.current;
|
|
18
23
|
}
|
|
19
24
|
);
|
|
20
|
-
const scrollIntoView = useEventCallback(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
const scrollIntoView = useEventCallback(
|
|
26
|
+
(activeTab, animate = true) => {
|
|
27
|
+
const tabsEl = tabsContainerRef.current;
|
|
28
|
+
if (!tabsEl || !activeTab) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
scrollLeftTo(
|
|
32
|
+
tabsEl,
|
|
33
|
+
activeTab.offsetLeft - (tabsEl.offsetWidth - activeTab.offsetWidth) / 2,
|
|
34
|
+
animate ? duration : 0
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
const getIndicatorWidth = useEventCallback(() => {
|
|
39
|
+
if (indicatorWidthCache.current !== null) {
|
|
40
|
+
return indicatorWidthCache.current;
|
|
24
41
|
}
|
|
25
|
-
scrollLeftTo(
|
|
26
|
-
tabsEl,
|
|
27
|
-
activeTab.offsetLeft - (tabsEl.offsetWidth - activeTab.offsetWidth) / 2,
|
|
28
|
-
duration
|
|
29
|
-
);
|
|
30
|
-
});
|
|
31
|
-
const animate = useEventCallback(() => {
|
|
32
|
-
const tabsEl = tabsContainerRef.current;
|
|
33
|
-
if (!tabsEl)
|
|
34
|
-
return;
|
|
35
42
|
const indicator = indicatorRef.current;
|
|
36
43
|
if (!indicator)
|
|
44
|
+
return DEFAULT_INDICATOR_WIDTH;
|
|
45
|
+
const cssValue = getComputedStyle(indicator).getPropertyValue(
|
|
46
|
+
"--bui-tabs-indicator-width"
|
|
47
|
+
);
|
|
48
|
+
const parsed = Number.parseFloat(cssValue);
|
|
49
|
+
const width = Number.isNaN(parsed) ? DEFAULT_INDICATOR_WIDTH : parsed;
|
|
50
|
+
indicatorWidthCache.current = width;
|
|
51
|
+
return width;
|
|
52
|
+
});
|
|
53
|
+
const getTabsMeta = useEventCallback(() => {
|
|
54
|
+
const tabsNode = tabsContainerRef.current;
|
|
55
|
+
let tabsMeta = null;
|
|
56
|
+
if (tabsNode) {
|
|
57
|
+
const rect = tabsNode.getBoundingClientRect();
|
|
58
|
+
tabsMeta = {
|
|
59
|
+
clientWidth: tabsNode.clientWidth,
|
|
60
|
+
scrollLeft: tabsNode.scrollLeft,
|
|
61
|
+
scrollWidth: tabsNode.scrollWidth,
|
|
62
|
+
left: rect.left
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
let tabMeta = null;
|
|
66
|
+
const activeTab = getActiveTabElement(currentValue);
|
|
67
|
+
if (activeTab) {
|
|
68
|
+
tabMeta = activeTab.getBoundingClientRect();
|
|
69
|
+
}
|
|
70
|
+
return { tabsMeta, tabMeta };
|
|
71
|
+
});
|
|
72
|
+
const updateIndicatorState = useEventCallback(() => {
|
|
73
|
+
const { tabsMeta, tabMeta } = getTabsMeta();
|
|
74
|
+
if (!tabMeta || !tabsMeta) {
|
|
75
|
+
setIndicatorStyle(null);
|
|
37
76
|
return;
|
|
77
|
+
}
|
|
78
|
+
const tabLeft = tabMeta.left - tabsMeta.left + tabsMeta.scrollLeft;
|
|
79
|
+
const tabWidth = tabMeta.width;
|
|
80
|
+
const indicatorWidth = getIndicatorWidth();
|
|
81
|
+
const leftPosition = tabLeft + (tabWidth - indicatorWidth) / 2;
|
|
82
|
+
const newIndicatorStyle = {
|
|
83
|
+
left: leftPosition
|
|
84
|
+
};
|
|
85
|
+
if (indicatorStyle === null) {
|
|
86
|
+
setIndicatorStyle(newIndicatorStyle);
|
|
87
|
+
} else {
|
|
88
|
+
const dLeft = Math.abs(indicatorStyle.left - newIndicatorStyle.left);
|
|
89
|
+
if (dLeft >= 1) {
|
|
90
|
+
setIndicatorStyle(newIndicatorStyle);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
38
93
|
const activeTab = getActiveTabElement(currentValue);
|
|
39
94
|
if (activeTab) {
|
|
40
|
-
const
|
|
41
|
-
const activeTabWidth = activeTab.offsetWidth;
|
|
42
|
-
const containerWidth = tabsEl.offsetWidth;
|
|
43
|
-
const containerScrollWidth = tabsEl.scrollWidth;
|
|
44
|
-
const activeLineWidth = indicator.offsetWidth;
|
|
45
|
-
const x = activeTabLeft + (activeTabWidth - activeLineWidth) / 2;
|
|
46
|
-
indicator.style.transform = `translate(${x}px, 0px)`;
|
|
47
|
-
indicator.style.visibility = "visible";
|
|
48
|
-
const maxScrollDistance = containerScrollWidth - containerWidth;
|
|
95
|
+
const maxScrollDistance = tabsMeta.scrollWidth - tabsMeta.clientWidth;
|
|
49
96
|
if (maxScrollDistance > 0 && !isMini) {
|
|
50
|
-
scrollIntoView(activeTab);
|
|
97
|
+
scrollIntoView(activeTab, hasRenderedOnce.current);
|
|
51
98
|
}
|
|
52
|
-
|
|
53
|
-
indicator.style.visibility = "hidden";
|
|
99
|
+
hasRenderedOnce.current = true;
|
|
54
100
|
}
|
|
55
101
|
});
|
|
56
102
|
useEffect(() => {
|
|
57
|
-
|
|
58
|
-
}
|
|
103
|
+
updateIndicatorState();
|
|
104
|
+
});
|
|
59
105
|
useEffect(() => {
|
|
60
106
|
const handleResize = debounce(() => {
|
|
61
|
-
|
|
107
|
+
indicatorWidthCache.current = null;
|
|
108
|
+
updateIndicatorState();
|
|
62
109
|
}, 100);
|
|
63
110
|
window.addEventListener("resize", handleResize);
|
|
64
111
|
return () => {
|
|
65
112
|
window.removeEventListener("resize", handleResize);
|
|
66
113
|
};
|
|
67
|
-
}, [
|
|
114
|
+
}, [updateIndicatorState]);
|
|
115
|
+
if (!indicatorStyle) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
68
118
|
return /* @__PURE__ */ React.createElement(
|
|
69
119
|
"div",
|
|
70
120
|
{
|
|
71
121
|
ref: indicatorRef,
|
|
72
122
|
className: clsx(`${rootClass}-indicator`),
|
|
73
|
-
style:
|
|
74
|
-
transition: "transform 0.3s ease-in-out",
|
|
75
|
-
transform: "translate(0px, 0px)",
|
|
76
|
-
visibility: "hidden"
|
|
77
|
-
},
|
|
123
|
+
style: indicatorStyle,
|
|
78
124
|
"aria-hidden": "true"
|
|
79
125
|
}
|
|
80
126
|
);
|
package/es/Tabs/TabMask.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export interface TabMaskProps {
|
|
3
|
-
/** Tabs 容器的引用,用于监听滚动 */
|
|
4
|
-
tabsContainerRef: React.RefObject<HTMLDivElement>;
|
|
5
3
|
/** 位置:左侧或右侧 */
|
|
6
4
|
position: 'left' | 'right';
|
|
7
5
|
}
|
|
8
|
-
declare const
|
|
9
|
-
export default
|
|
6
|
+
declare const _default: React.NamedExoticComponent<TabMaskProps>;
|
|
7
|
+
export default _default;
|
package/es/Tabs/TabMask.js
CHANGED
|
@@ -1,59 +1,18 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import clsx from "clsx";
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const tabsEl = tabsContainerRef.current;
|
|
11
|
-
const mask = maskRef.current;
|
|
12
|
-
if (!tabsEl || !mask)
|
|
13
|
-
return;
|
|
14
|
-
const { scrollLeft, scrollWidth, offsetWidth } = tabsEl;
|
|
15
|
-
let shouldShow = false;
|
|
16
|
-
if (position === "left") {
|
|
17
|
-
shouldShow = scrollLeft > 0;
|
|
18
|
-
} else {
|
|
19
|
-
const rightRange = Math.abs(
|
|
20
|
-
scrollWidth - (scrollLeft + offsetWidth)
|
|
21
|
-
);
|
|
22
|
-
shouldShow = rightRange > 1;
|
|
23
|
-
}
|
|
24
|
-
mask.style.opacity = shouldShow ? "1" : "0";
|
|
25
|
-
},
|
|
26
|
-
100,
|
|
27
|
-
{
|
|
28
|
-
trailing: true,
|
|
29
|
-
leading: true
|
|
30
|
-
}
|
|
3
|
+
import { tabMaskClass, tabMaskLeftClass, tabMaskRightClass } from "./classes";
|
|
4
|
+
const TabMask = ({ position }) => /* @__PURE__ */ React.createElement(
|
|
5
|
+
"div",
|
|
6
|
+
{
|
|
7
|
+
className: clsx(
|
|
8
|
+
tabMaskClass,
|
|
9
|
+
position === "left" ? tabMaskLeftClass : tabMaskRightClass
|
|
31
10
|
),
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return void 0;
|
|
38
|
-
updateMaskOpacity();
|
|
39
|
-
tabsEl.addEventListener("scroll", updateMaskOpacity);
|
|
40
|
-
return () => {
|
|
41
|
-
tabsEl.removeEventListener("scroll", updateMaskOpacity);
|
|
42
|
-
};
|
|
43
|
-
}, [tabsContainerRef, updateMaskOpacity]);
|
|
44
|
-
return /* @__PURE__ */ React.createElement(
|
|
45
|
-
"div",
|
|
46
|
-
{
|
|
47
|
-
ref: maskRef,
|
|
48
|
-
className: clsx(`${rootClass}-mask`, `${rootClass}-mask-${position}`),
|
|
49
|
-
style: {
|
|
50
|
-
opacity: 0
|
|
51
|
-
},
|
|
52
|
-
"aria-hidden": "true"
|
|
53
|
-
}
|
|
54
|
-
);
|
|
55
|
-
};
|
|
56
|
-
var TabMask_default = TabMask;
|
|
11
|
+
"aria-hidden": "true"
|
|
12
|
+
}
|
|
13
|
+
);
|
|
14
|
+
TabMask.displayName = "BuiTabsMask";
|
|
15
|
+
var TabMask_default = /* @__PURE__ */ React.memo(TabMask);
|
|
57
16
|
export {
|
|
58
17
|
TabMask_default as default
|
|
59
18
|
};
|
package/es/Tabs/Tabs.css
CHANGED
|
@@ -49,7 +49,6 @@ xhs-page {
|
|
|
49
49
|
position: absolute;
|
|
50
50
|
top: unset;
|
|
51
51
|
bottom: var(--bui-tabs-indicator-bottom, 0);
|
|
52
|
-
left: 0;
|
|
53
52
|
width: var(--bui-tabs-indicator-width);
|
|
54
53
|
height: var(--bui-tabs-indicator-height);
|
|
55
54
|
color: var(--bui-color-primary);
|
|
@@ -58,6 +57,7 @@ xhs-page {
|
|
|
58
57
|
box-shadow: var(--bui-tabs-indicator-box-shadow);
|
|
59
58
|
z-index: 1;
|
|
60
59
|
pointer-events: none;
|
|
60
|
+
transition: left 0.3s ease-in-out;
|
|
61
61
|
}
|
|
62
62
|
.bui-tabs-content {
|
|
63
63
|
padding: var(--bui-spacing-lg);
|
package/es/Tabs/Tabs.js
CHANGED
|
@@ -29,7 +29,7 @@ var __objRest = (source, exclude) => {
|
|
|
29
29
|
}
|
|
30
30
|
return target;
|
|
31
31
|
};
|
|
32
|
-
import React, { useMemo, useRef, useState } from "react";
|
|
32
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
33
33
|
import clsx from "clsx";
|
|
34
34
|
import { useValue, useEventCallback } from "@bifrostui/utils";
|
|
35
35
|
import Tab from "./Tab";
|
|
@@ -73,6 +73,7 @@ const Tabs = /* @__PURE__ */ React.forwardRef((props, ref) => {
|
|
|
73
73
|
{}
|
|
74
74
|
);
|
|
75
75
|
const [registrationVersion, setRegistrationVersion] = useState(0);
|
|
76
|
+
const [isScrollable, setIsScrollable] = useState(false);
|
|
76
77
|
if (process.env.NODE_ENV !== "production") {
|
|
77
78
|
if (tabs.length > 0 && React.Children.count(children) > 0) {
|
|
78
79
|
console.warn(
|
|
@@ -117,10 +118,21 @@ const Tabs = /* @__PURE__ */ React.forwardRef((props, ref) => {
|
|
|
117
118
|
}
|
|
118
119
|
return children;
|
|
119
120
|
}, [tabs, children]);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
const tabsEl = tabsRef.current;
|
|
123
|
+
if (!tabsEl)
|
|
124
|
+
return;
|
|
125
|
+
const checkScrollable = () => {
|
|
126
|
+
setIsScrollable(tabsEl.scrollWidth > tabsEl.offsetWidth);
|
|
127
|
+
};
|
|
128
|
+
checkScrollable();
|
|
129
|
+
const resizeObserver = new ResizeObserver(checkScrollable);
|
|
130
|
+
resizeObserver.observe(tabsEl);
|
|
131
|
+
return () => {
|
|
132
|
+
resizeObserver.disconnect();
|
|
133
|
+
};
|
|
134
|
+
}, [registrationVersion]);
|
|
135
|
+
return /* @__PURE__ */ React.createElement("div", __spreadProps(__spreadValues({ className: clsx(tabsRootClass, className) }, others), { ref }), isScrollable && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TabMask, { position: "left" }), /* @__PURE__ */ React.createElement(TabMask, { position: "right" })), /* @__PURE__ */ React.createElement(
|
|
124
136
|
"div",
|
|
125
137
|
{
|
|
126
138
|
className: `${tabsRootClass}-tabs`,
|
|
@@ -133,8 +145,7 @@ const Tabs = /* @__PURE__ */ React.forwardRef((props, ref) => {
|
|
|
133
145
|
{
|
|
134
146
|
currentValue,
|
|
135
147
|
registeredTabs,
|
|
136
|
-
tabsContainerRef: tabsRef
|
|
137
|
-
registrationVersion
|
|
148
|
+
tabsContainerRef: tabsRef
|
|
138
149
|
}
|
|
139
150
|
),
|
|
140
151
|
/* @__PURE__ */ React.createElement(TabsContextProvider, { value: contextValue }, renderedTabs)
|
package/es/Tabs/index.css
CHANGED
|
@@ -49,7 +49,6 @@ xhs-page {
|
|
|
49
49
|
position: absolute;
|
|
50
50
|
top: unset;
|
|
51
51
|
bottom: var(--bui-tabs-indicator-bottom, 0);
|
|
52
|
-
left: 0;
|
|
53
52
|
width: var(--bui-tabs-indicator-width);
|
|
54
53
|
height: var(--bui-tabs-indicator-height);
|
|
55
54
|
color: var(--bui-color-primary);
|
|
@@ -58,6 +57,7 @@ xhs-page {
|
|
|
58
57
|
box-shadow: var(--bui-tabs-indicator-box-shadow);
|
|
59
58
|
z-index: 1;
|
|
60
59
|
pointer-events: none;
|
|
60
|
+
transition: left 0.3s ease-in-out;
|
|
61
61
|
}
|
|
62
62
|
.bui-tabs-content {
|
|
63
63
|
padding: var(--bui-spacing-lg);
|
|
@@ -18,12 +18,15 @@ var __async = (__this, __arguments, generator) => {
|
|
|
18
18
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
19
19
|
});
|
|
20
20
|
};
|
|
21
|
-
import React, { useEffect, useRef
|
|
21
|
+
import React, { useEffect, useRef } from "react";
|
|
22
22
|
import clsx from "clsx";
|
|
23
23
|
import Taro from "@tarojs/taro";
|
|
24
24
|
import { useEventCallback } from "@bifrostui/utils";
|
|
25
25
|
import { tabIndicatorClass } from "../classes";
|
|
26
26
|
import { batchQueryTabs } from "./utils/queryBatch";
|
|
27
|
+
const isValidTabValue = (value) => {
|
|
28
|
+
return value !== void 0 && value !== null;
|
|
29
|
+
};
|
|
27
30
|
const TabIndicator = ({
|
|
28
31
|
currentValue,
|
|
29
32
|
registeredTabValues,
|
|
@@ -31,8 +34,7 @@ const TabIndicator = ({
|
|
|
31
34
|
scrollViewId,
|
|
32
35
|
registrationVersion
|
|
33
36
|
}) => {
|
|
34
|
-
const
|
|
35
|
-
const [visibility, setVisibility] = useState("hidden");
|
|
37
|
+
const indicatorRef = useRef(null);
|
|
36
38
|
const animationTimerRef = useRef(null);
|
|
37
39
|
const positionCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
38
40
|
const containerInfoRef = useRef(null);
|
|
@@ -40,14 +42,20 @@ const TabIndicator = ({
|
|
|
40
42
|
const isInitializedRef = useRef(false);
|
|
41
43
|
const initRetryCountRef = useRef(0);
|
|
42
44
|
const maxInitRetries = 5;
|
|
45
|
+
const isInitializingRef = useRef(false);
|
|
46
|
+
const initVersionRef = useRef(0);
|
|
47
|
+
const isMountedRef = useRef(true);
|
|
43
48
|
const updateIndicatorPosition = useEventCallback(() => {
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
const indicator = indicatorRef.current;
|
|
50
|
+
if (!indicator)
|
|
51
|
+
return;
|
|
52
|
+
if (!isValidTabValue(currentValue) || !registeredTabValues.includes(currentValue)) {
|
|
53
|
+
indicator.style.opacity = "0";
|
|
46
54
|
return;
|
|
47
55
|
}
|
|
48
56
|
const cachedPosition = positionCacheRef.current.get(currentValue);
|
|
49
57
|
const containerInfo = containerInfoRef.current;
|
|
50
|
-
if (!cachedPosition || !containerInfo) {
|
|
58
|
+
if (!cachedPosition || !containerInfo || !isInitializedRef.current) {
|
|
51
59
|
if (!isInitializedRef.current) {
|
|
52
60
|
initializePositions();
|
|
53
61
|
}
|
|
@@ -57,16 +65,27 @@ const TabIndicator = ({
|
|
|
57
65
|
const activeTabWidth = cachedPosition.width;
|
|
58
66
|
const indicatorWidth = indicatorWidthRef.current;
|
|
59
67
|
const x = activeTabLeft + (activeTabWidth - indicatorWidth) / 2;
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
indicator.style.transform = `translate(${x}px, 0px)`;
|
|
69
|
+
indicator.style.opacity = "1";
|
|
62
70
|
});
|
|
63
71
|
const initializePositions = useEventCallback(() => __async(void 0, null, function* () {
|
|
72
|
+
if (!isMountedRef.current)
|
|
73
|
+
return;
|
|
74
|
+
if (isInitializingRef.current)
|
|
75
|
+
return;
|
|
76
|
+
isInitializingRef.current = true;
|
|
77
|
+
initVersionRef.current += 1;
|
|
78
|
+
const currentVersion = initVersionRef.current;
|
|
64
79
|
try {
|
|
65
80
|
const result = yield batchQueryTabs({
|
|
66
81
|
scrollViewId,
|
|
67
82
|
wrapperId,
|
|
68
83
|
tabValues: registeredTabValues
|
|
69
84
|
});
|
|
85
|
+
if (!isMountedRef.current)
|
|
86
|
+
return;
|
|
87
|
+
if (currentVersion !== initVersionRef.current)
|
|
88
|
+
return;
|
|
70
89
|
const { scrollView, scrollFields, wrapper, indicator, tabs } = result;
|
|
71
90
|
if (!scrollView || !wrapper || !indicator || !scrollFields) {
|
|
72
91
|
if (initRetryCountRef.current < maxInitRetries) {
|
|
@@ -75,8 +94,30 @@ const TabIndicator = ({
|
|
|
75
94
|
clearTimeout(animationTimerRef.current);
|
|
76
95
|
}
|
|
77
96
|
animationTimerRef.current = setTimeout(() => {
|
|
97
|
+
if (!isMountedRef.current)
|
|
98
|
+
return;
|
|
99
|
+
isInitializingRef.current = false;
|
|
100
|
+
initializePositions();
|
|
101
|
+
}, 100);
|
|
102
|
+
} else {
|
|
103
|
+
isInitializingRef.current = false;
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (!indicator.width || indicator.width <= 0) {
|
|
108
|
+
if (initRetryCountRef.current < maxInitRetries) {
|
|
109
|
+
initRetryCountRef.current += 1;
|
|
110
|
+
if (animationTimerRef.current) {
|
|
111
|
+
clearTimeout(animationTimerRef.current);
|
|
112
|
+
}
|
|
113
|
+
animationTimerRef.current = setTimeout(() => {
|
|
114
|
+
if (!isMountedRef.current)
|
|
115
|
+
return;
|
|
116
|
+
isInitializingRef.current = false;
|
|
78
117
|
initializePositions();
|
|
79
118
|
}, 100);
|
|
119
|
+
} else {
|
|
120
|
+
isInitializingRef.current = false;
|
|
80
121
|
}
|
|
81
122
|
return;
|
|
82
123
|
}
|
|
@@ -89,9 +130,8 @@ const TabIndicator = ({
|
|
|
89
130
|
registeredTabValues.forEach((value, index) => {
|
|
90
131
|
const tabRect = tabs[index];
|
|
91
132
|
if (tabRect && tabRect.width > 0) {
|
|
92
|
-
const relativeLeft = tabRect.left - wrapper.left;
|
|
93
133
|
newCache.set(value, {
|
|
94
|
-
left:
|
|
134
|
+
left: tabRect.left - wrapper.left,
|
|
95
135
|
width: tabRect.width
|
|
96
136
|
});
|
|
97
137
|
}
|
|
@@ -103,40 +143,74 @@ const TabIndicator = ({
|
|
|
103
143
|
clearTimeout(animationTimerRef.current);
|
|
104
144
|
}
|
|
105
145
|
animationTimerRef.current = setTimeout(() => {
|
|
146
|
+
if (!isMountedRef.current)
|
|
147
|
+
return;
|
|
148
|
+
isInitializingRef.current = false;
|
|
106
149
|
initializePositions();
|
|
107
150
|
}, 100);
|
|
151
|
+
} else {
|
|
152
|
+
isInitializingRef.current = false;
|
|
108
153
|
}
|
|
109
154
|
return;
|
|
110
155
|
}
|
|
156
|
+
if (currentVersion !== initVersionRef.current || !isMountedRef.current) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
111
159
|
initRetryCountRef.current = 0;
|
|
112
160
|
positionCacheRef.current = newCache;
|
|
113
161
|
isInitializedRef.current = true;
|
|
162
|
+
isInitializingRef.current = false;
|
|
114
163
|
updateIndicatorPosition();
|
|
115
164
|
} catch (error) {
|
|
116
165
|
console.error("[TabIndicator] \u6279\u91CF\u67E5\u8BE2\u5931\u8D25:", error);
|
|
166
|
+
isInitializingRef.current = false;
|
|
167
|
+
if (isMountedRef.current && initRetryCountRef.current < maxInitRetries) {
|
|
168
|
+
initRetryCountRef.current += 1;
|
|
169
|
+
if (animationTimerRef.current) {
|
|
170
|
+
clearTimeout(animationTimerRef.current);
|
|
171
|
+
}
|
|
172
|
+
animationTimerRef.current = setTimeout(() => {
|
|
173
|
+
if (!isMountedRef.current)
|
|
174
|
+
return;
|
|
175
|
+
initializePositions();
|
|
176
|
+
}, 100);
|
|
177
|
+
}
|
|
117
178
|
}
|
|
118
179
|
}));
|
|
119
180
|
useEffect(() => {
|
|
120
181
|
if (registeredTabValues.length === 0) {
|
|
182
|
+
if (indicatorRef.current) {
|
|
183
|
+
indicatorRef.current.style.opacity = "0";
|
|
184
|
+
}
|
|
121
185
|
return void 0;
|
|
122
186
|
}
|
|
187
|
+
initVersionRef.current += 1;
|
|
123
188
|
if (animationTimerRef.current) {
|
|
124
189
|
clearTimeout(animationTimerRef.current);
|
|
190
|
+
animationTimerRef.current = null;
|
|
125
191
|
}
|
|
126
192
|
isInitializedRef.current = false;
|
|
193
|
+
isInitializingRef.current = false;
|
|
127
194
|
initRetryCountRef.current = 0;
|
|
128
195
|
Taro.nextTick(() => {
|
|
196
|
+
if (!isMountedRef.current)
|
|
197
|
+
return;
|
|
129
198
|
initializePositions();
|
|
130
199
|
});
|
|
131
200
|
return () => {
|
|
132
201
|
if (animationTimerRef.current) {
|
|
133
202
|
clearTimeout(animationTimerRef.current);
|
|
203
|
+
animationTimerRef.current = null;
|
|
134
204
|
}
|
|
205
|
+
initVersionRef.current += 1;
|
|
206
|
+
isInitializingRef.current = false;
|
|
135
207
|
};
|
|
136
208
|
}, [registrationVersion, initializePositions]);
|
|
137
209
|
useEffect(() => {
|
|
138
|
-
if (!currentValue) {
|
|
139
|
-
|
|
210
|
+
if (!isValidTabValue(currentValue)) {
|
|
211
|
+
if (indicatorRef.current) {
|
|
212
|
+
indicatorRef.current.style.opacity = "0";
|
|
213
|
+
}
|
|
140
214
|
return;
|
|
141
215
|
}
|
|
142
216
|
updateIndicatorPosition();
|
|
@@ -144,10 +218,20 @@ const TabIndicator = ({
|
|
|
144
218
|
useEffect(() => {
|
|
145
219
|
var _a, _b;
|
|
146
220
|
const handleResize = () => {
|
|
221
|
+
if (!isMountedRef.current)
|
|
222
|
+
return;
|
|
223
|
+
initVersionRef.current += 1;
|
|
147
224
|
isInitializedRef.current = false;
|
|
225
|
+
isInitializingRef.current = false;
|
|
148
226
|
initRetryCountRef.current = 0;
|
|
149
227
|
positionCacheRef.current.clear();
|
|
228
|
+
if (animationTimerRef.current) {
|
|
229
|
+
clearTimeout(animationTimerRef.current);
|
|
230
|
+
animationTimerRef.current = null;
|
|
231
|
+
}
|
|
150
232
|
Taro.nextTick(() => {
|
|
233
|
+
if (!isMountedRef.current)
|
|
234
|
+
return;
|
|
151
235
|
initializePositions();
|
|
152
236
|
});
|
|
153
237
|
};
|
|
@@ -157,15 +241,26 @@ const TabIndicator = ({
|
|
|
157
241
|
(_b2 = (_a2 = Taro).offWindowResize) == null ? void 0 : _b2.call(_a2, handleResize);
|
|
158
242
|
};
|
|
159
243
|
}, [initializePositions]);
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
isMountedRef.current = true;
|
|
246
|
+
return () => {
|
|
247
|
+
isMountedRef.current = false;
|
|
248
|
+
if (animationTimerRef.current) {
|
|
249
|
+
clearTimeout(animationTimerRef.current);
|
|
250
|
+
animationTimerRef.current = null;
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}, []);
|
|
160
254
|
return /* @__PURE__ */ React.createElement(
|
|
161
255
|
"div",
|
|
162
256
|
{
|
|
257
|
+
ref: indicatorRef,
|
|
163
258
|
id: `${wrapperId}-indicator`,
|
|
164
259
|
className: clsx(tabIndicatorClass),
|
|
165
260
|
style: {
|
|
166
|
-
transition: "transform 0.3s ease-in-out",
|
|
167
|
-
transform,
|
|
168
|
-
|
|
261
|
+
transition: "transform 0.3s ease-in-out, opacity 0.3s ease-in-out",
|
|
262
|
+
transform: "translate(0px, 0px)",
|
|
263
|
+
opacity: 0
|
|
169
264
|
}
|
|
170
265
|
}
|
|
171
266
|
);
|
package/es/Tabs/miniapp/Tabs.js
CHANGED
|
@@ -25,7 +25,7 @@ import { ScrollView } from "@tarojs/components";
|
|
|
25
25
|
import { useValue, useEventCallback } from "@bifrostui/utils";
|
|
26
26
|
import Tab from "./Tab";
|
|
27
27
|
import TabIndicator from "./TabIndicator";
|
|
28
|
-
import TabMask from "
|
|
28
|
+
import TabMask from "../TabMask";
|
|
29
29
|
import { TabsContextProvider } from "./TabsContext";
|
|
30
30
|
import {
|
|
31
31
|
tabsRootClass,
|
|
@@ -72,8 +72,10 @@ const Tabs = /* @__PURE__ */ React.forwardRef((props, ref) => {
|
|
|
72
72
|
const [scrollLeft, setScrollLeft] = useState(0);
|
|
73
73
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
74
74
|
const [scrollWidth, setScrollWidth] = useState(0);
|
|
75
|
-
const scrollLeftUpdateTimerRef = React.useRef(null);
|
|
76
75
|
const lastScrollLeftRef = React.useRef(0);
|
|
76
|
+
const isFirstScroll = React.useRef(true);
|
|
77
|
+
const [scrollWithAnimation, setScrollWithAnimation] = React.useState(false);
|
|
78
|
+
const isScrollable = scrollWidth > containerWidth;
|
|
77
79
|
if (process.env.NODE_ENV !== "production") {
|
|
78
80
|
if (tabs.length > 0 && React.Children.count(children) > 0) {
|
|
79
81
|
console.warn(
|
|
@@ -113,14 +115,6 @@ const Tabs = /* @__PURE__ */ React.forwardRef((props, ref) => {
|
|
|
113
115
|
(e) => {
|
|
114
116
|
const { scrollLeft: newScrollLeft, scrollWidth: newScrollWidth } = e.detail;
|
|
115
117
|
lastScrollLeftRef.current = newScrollLeft;
|
|
116
|
-
if (scrollLeftUpdateTimerRef.current) {
|
|
117
|
-
clearTimeout(scrollLeftUpdateTimerRef.current);
|
|
118
|
-
}
|
|
119
|
-
scrollLeftUpdateTimerRef.current = setTimeout(() => {
|
|
120
|
-
if (Math.abs(lastScrollLeftRef.current - scrollLeft) > 1) {
|
|
121
|
-
setScrollLeft(lastScrollLeftRef.current);
|
|
122
|
-
}
|
|
123
|
-
}, 150);
|
|
124
118
|
if (newScrollWidth && newScrollWidth !== scrollWidth) {
|
|
125
119
|
setScrollWidth(newScrollWidth);
|
|
126
120
|
}
|
|
@@ -163,6 +157,12 @@ const Tabs = /* @__PURE__ */ React.forwardRef((props, ref) => {
|
|
|
163
157
|
);
|
|
164
158
|
setScrollLeft(finalScrollLeft);
|
|
165
159
|
lastScrollLeftRef.current = finalScrollLeft;
|
|
160
|
+
if (isFirstScroll.current) {
|
|
161
|
+
Taro.nextTick(() => {
|
|
162
|
+
setScrollWithAnimation(true);
|
|
163
|
+
});
|
|
164
|
+
isFirstScroll.current = false;
|
|
165
|
+
}
|
|
166
166
|
}));
|
|
167
167
|
React.useEffect(() => {
|
|
168
168
|
if (!currentValue || registeredTabValues.length === 0) {
|
|
@@ -196,38 +196,14 @@ const Tabs = /* @__PURE__ */ React.forwardRef((props, ref) => {
|
|
|
196
196
|
}
|
|
197
197
|
return children;
|
|
198
198
|
}, [tabs, children]);
|
|
199
|
-
React.
|
|
200
|
-
return () => {
|
|
201
|
-
if (scrollLeftUpdateTimerRef.current) {
|
|
202
|
-
clearTimeout(scrollLeftUpdateTimerRef.current);
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
}, []);
|
|
206
|
-
return /* @__PURE__ */ React.createElement("div", { className: clsx(tabsRootClass, className), style, ref }, /* @__PURE__ */ React.createElement(
|
|
207
|
-
TabMask,
|
|
208
|
-
{
|
|
209
|
-
position: "left",
|
|
210
|
-
scrollLeft,
|
|
211
|
-
containerWidth,
|
|
212
|
-
scrollWidth
|
|
213
|
-
}
|
|
214
|
-
), /* @__PURE__ */ React.createElement(
|
|
215
|
-
TabMask,
|
|
216
|
-
{
|
|
217
|
-
position: "right",
|
|
218
|
-
scrollLeft,
|
|
219
|
-
containerWidth,
|
|
220
|
-
scrollWidth
|
|
221
|
-
}
|
|
222
|
-
), /* @__PURE__ */ React.createElement(
|
|
199
|
+
return /* @__PURE__ */ React.createElement("div", { className: clsx(tabsRootClass, className), style, ref }, isScrollable && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TabMask, { position: "left" }), /* @__PURE__ */ React.createElement(TabMask, { position: "right" })), /* @__PURE__ */ React.createElement(
|
|
223
200
|
ScrollView,
|
|
224
201
|
{
|
|
225
202
|
id: scrollViewId,
|
|
226
203
|
className: tabsScrollClass,
|
|
227
204
|
scrollX: true,
|
|
228
|
-
scrollWithAnimation
|
|
205
|
+
scrollWithAnimation,
|
|
229
206
|
scrollLeft,
|
|
230
|
-
scrollAnimationDuration: "200",
|
|
231
207
|
onScroll: handleScroll,
|
|
232
208
|
enhanced: true,
|
|
233
209
|
showScrollbar: false,
|