@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.
@@ -12,5 +12,5 @@ declare const Modal: React.ForwardRefExoticComponent<Omit<ViewProps & {
12
12
  keepMounted?: boolean;
13
13
  } & import("@bifrostui/types").ICommonProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
14
14
  ref?: React.Ref<HTMLDivElement>;
15
- }, keyof import("@bifrostui/types").ICommonProps | "open" | "container" | "onClose" | "disablePortal" | "hideBackdrop" | "BackdropProps" | "disableScrollLock" | "keepMounted">, "ref"> & React.RefAttributes<HTMLDivElement>>;
15
+ }, "open" | "container" | keyof import("@bifrostui/types").ICommonProps | "disablePortal" | "hideBackdrop" | "BackdropProps" | "onClose" | "disableScrollLock" | "keepMounted">, "ref"> & React.RefAttributes<HTMLDivElement>>;
16
16
  export default Modal;
@@ -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;
@@ -36,77 +36,123 @@ var import_utils = require("@bifrostui/utils");
36
36
  var import_scroll = __toESM(require("./utils/scroll"));
37
37
  const rootClass = "bui-tabs";
38
38
  const duration = 300;
39
+ const DEFAULT_INDICATOR_WIDTH = 24;
39
40
  const TabIndicator = ({
40
41
  currentValue,
41
42
  registeredTabs,
42
- tabsContainerRef,
43
- registrationVersion
43
+ tabsContainerRef
44
44
  }) => {
45
45
  const indicatorRef = (0, import_react.useRef)(null);
46
+ const hasRenderedOnce = (0, import_react.useRef)(false);
47
+ const indicatorWidthCache = (0, import_react.useRef)(null);
48
+ const [indicatorStyle, setIndicatorStyle] = (0, import_react.useState)(
49
+ null
50
+ );
46
51
  const getActiveTabElement = (0, import_utils.useEventCallback)(
47
52
  (activeValue) => {
48
53
  const tabRef = registeredTabs.current[activeValue];
49
54
  return tabRef == null ? void 0 : tabRef.current;
50
55
  }
51
56
  );
52
- const scrollIntoView = (0, import_utils.useEventCallback)((activeTab) => {
53
- const tabsEl = tabsContainerRef.current;
54
- if (!tabsEl || !activeTab) {
55
- return;
57
+ const scrollIntoView = (0, import_utils.useEventCallback)(
58
+ (activeTab, animate = true) => {
59
+ const tabsEl = tabsContainerRef.current;
60
+ if (!tabsEl || !activeTab) {
61
+ return;
62
+ }
63
+ (0, import_scroll.default)(
64
+ tabsEl,
65
+ activeTab.offsetLeft - (tabsEl.offsetWidth - activeTab.offsetWidth) / 2,
66
+ animate ? duration : 0
67
+ );
68
+ }
69
+ );
70
+ const getIndicatorWidth = (0, import_utils.useEventCallback)(() => {
71
+ if (indicatorWidthCache.current !== null) {
72
+ return indicatorWidthCache.current;
56
73
  }
57
- (0, import_scroll.default)(
58
- tabsEl,
59
- activeTab.offsetLeft - (tabsEl.offsetWidth - activeTab.offsetWidth) / 2,
60
- duration
61
- );
62
- });
63
- const animate = (0, import_utils.useEventCallback)(() => {
64
- const tabsEl = tabsContainerRef.current;
65
- if (!tabsEl)
66
- return;
67
74
  const indicator = indicatorRef.current;
68
75
  if (!indicator)
76
+ return DEFAULT_INDICATOR_WIDTH;
77
+ const cssValue = getComputedStyle(indicator).getPropertyValue(
78
+ "--bui-tabs-indicator-width"
79
+ );
80
+ const parsed = Number.parseFloat(cssValue);
81
+ const width = Number.isNaN(parsed) ? DEFAULT_INDICATOR_WIDTH : parsed;
82
+ indicatorWidthCache.current = width;
83
+ return width;
84
+ });
85
+ const getTabsMeta = (0, import_utils.useEventCallback)(() => {
86
+ const tabsNode = tabsContainerRef.current;
87
+ let tabsMeta = null;
88
+ if (tabsNode) {
89
+ const rect = tabsNode.getBoundingClientRect();
90
+ tabsMeta = {
91
+ clientWidth: tabsNode.clientWidth,
92
+ scrollLeft: tabsNode.scrollLeft,
93
+ scrollWidth: tabsNode.scrollWidth,
94
+ left: rect.left
95
+ };
96
+ }
97
+ let tabMeta = null;
98
+ const activeTab = getActiveTabElement(currentValue);
99
+ if (activeTab) {
100
+ tabMeta = activeTab.getBoundingClientRect();
101
+ }
102
+ return { tabsMeta, tabMeta };
103
+ });
104
+ const updateIndicatorState = (0, import_utils.useEventCallback)(() => {
105
+ const { tabsMeta, tabMeta } = getTabsMeta();
106
+ if (!tabMeta || !tabsMeta) {
107
+ setIndicatorStyle(null);
69
108
  return;
109
+ }
110
+ const tabLeft = tabMeta.left - tabsMeta.left + tabsMeta.scrollLeft;
111
+ const tabWidth = tabMeta.width;
112
+ const indicatorWidth = getIndicatorWidth();
113
+ const leftPosition = tabLeft + (tabWidth - indicatorWidth) / 2;
114
+ const newIndicatorStyle = {
115
+ left: leftPosition
116
+ };
117
+ if (indicatorStyle === null) {
118
+ setIndicatorStyle(newIndicatorStyle);
119
+ } else {
120
+ const dLeft = Math.abs(indicatorStyle.left - newIndicatorStyle.left);
121
+ if (dLeft >= 1) {
122
+ setIndicatorStyle(newIndicatorStyle);
123
+ }
124
+ }
70
125
  const activeTab = getActiveTabElement(currentValue);
71
126
  if (activeTab) {
72
- const activeTabLeft = activeTab.offsetLeft;
73
- const activeTabWidth = activeTab.offsetWidth;
74
- const containerWidth = tabsEl.offsetWidth;
75
- const containerScrollWidth = tabsEl.scrollWidth;
76
- const activeLineWidth = indicator.offsetWidth;
77
- const x = activeTabLeft + (activeTabWidth - activeLineWidth) / 2;
78
- indicator.style.transform = `translate(${x}px, 0px)`;
79
- indicator.style.visibility = "visible";
80
- const maxScrollDistance = containerScrollWidth - containerWidth;
127
+ const maxScrollDistance = tabsMeta.scrollWidth - tabsMeta.clientWidth;
81
128
  if (maxScrollDistance > 0 && !import_utils.isMini) {
82
- scrollIntoView(activeTab);
129
+ scrollIntoView(activeTab, hasRenderedOnce.current);
83
130
  }
84
- } else {
85
- indicator.style.visibility = "hidden";
131
+ hasRenderedOnce.current = true;
86
132
  }
87
133
  });
88
134
  (0, import_react.useEffect)(() => {
89
- animate();
90
- }, [animate, currentValue, registrationVersion]);
135
+ updateIndicatorState();
136
+ });
91
137
  (0, import_react.useEffect)(() => {
92
138
  const handleResize = (0, import_utils.debounce)(() => {
93
- animate();
139
+ indicatorWidthCache.current = null;
140
+ updateIndicatorState();
94
141
  }, 100);
95
142
  window.addEventListener("resize", handleResize);
96
143
  return () => {
97
144
  window.removeEventListener("resize", handleResize);
98
145
  };
99
- }, [animate]);
146
+ }, [updateIndicatorState]);
147
+ if (!indicatorStyle) {
148
+ return null;
149
+ }
100
150
  return /* @__PURE__ */ import_react.default.createElement(
101
151
  "div",
102
152
  {
103
153
  ref: indicatorRef,
104
154
  className: (0, import_clsx.default)(`${rootClass}-indicator`),
105
- style: {
106
- transition: "transform 0.3s ease-in-out",
107
- transform: "translate(0px, 0px)",
108
- visibility: "hidden"
109
- },
155
+ style: indicatorStyle,
110
156
  "aria-hidden": "true"
111
157
  }
112
158
  );
@@ -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 TabMask: React.FC<TabMaskProps>;
9
- export default TabMask;
6
+ declare const _default: React.NamedExoticComponent<TabMaskProps>;
7
+ export default _default;
@@ -32,57 +32,16 @@ __export(TabMask_exports, {
32
32
  module.exports = __toCommonJS(TabMask_exports);
33
33
  var import_react = __toESM(require("react"));
34
34
  var import_clsx = __toESM(require("clsx"));
35
- var import_utils = require("@bifrostui/utils");
36
- const rootClass = "bui-tabs";
37
- const TabMask = ({ tabsContainerRef, position }) => {
38
- const maskRef = (0, import_react.useRef)(null);
39
- const updateMaskOpacity = (0, import_react.useMemo)(
40
- () => (0, import_utils.throttle)(
41
- () => {
42
- const tabsEl = tabsContainerRef.current;
43
- const mask = maskRef.current;
44
- if (!tabsEl || !mask)
45
- return;
46
- const { scrollLeft, scrollWidth, offsetWidth } = tabsEl;
47
- let shouldShow = false;
48
- if (position === "left") {
49
- shouldShow = scrollLeft > 0;
50
- } else {
51
- const rightRange = Math.abs(
52
- scrollWidth - (scrollLeft + offsetWidth)
53
- );
54
- shouldShow = rightRange > 1;
55
- }
56
- mask.style.opacity = shouldShow ? "1" : "0";
57
- },
58
- 100,
59
- {
60
- trailing: true,
61
- leading: true
62
- }
35
+ var import_classes = require("./classes");
36
+ const TabMask = ({ position }) => /* @__PURE__ */ import_react.default.createElement(
37
+ "div",
38
+ {
39
+ className: (0, import_clsx.default)(
40
+ import_classes.tabMaskClass,
41
+ position === "left" ? import_classes.tabMaskLeftClass : import_classes.tabMaskRightClass
63
42
  ),
64
- [tabsContainerRef, position]
65
- );
66
- (0, import_react.useEffect)(() => {
67
- const tabsEl = tabsContainerRef.current;
68
- if (!tabsEl)
69
- return void 0;
70
- updateMaskOpacity();
71
- tabsEl.addEventListener("scroll", updateMaskOpacity);
72
- return () => {
73
- tabsEl.removeEventListener("scroll", updateMaskOpacity);
74
- };
75
- }, [tabsContainerRef, updateMaskOpacity]);
76
- return /* @__PURE__ */ import_react.default.createElement(
77
- "div",
78
- {
79
- ref: maskRef,
80
- className: (0, import_clsx.default)(`${rootClass}-mask`, `${rootClass}-mask-${position}`),
81
- style: {
82
- opacity: 0
83
- },
84
- "aria-hidden": "true"
85
- }
86
- );
87
- };
88
- var TabMask_default = TabMask;
43
+ "aria-hidden": "true"
44
+ }
45
+ );
46
+ TabMask.displayName = "BuiTabsMask";
47
+ var TabMask_default = /* @__PURE__ */ import_react.default.memo(TabMask);
@@ -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/dist/Tabs/Tabs.js CHANGED
@@ -103,6 +103,7 @@ const Tabs = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
103
103
  {}
104
104
  );
105
105
  const [registrationVersion, setRegistrationVersion] = (0, import_react.useState)(0);
106
+ const [isScrollable, setIsScrollable] = (0, import_react.useState)(false);
106
107
  if (process.env.NODE_ENV !== "production") {
107
108
  if (tabs.length > 0 && import_react.default.Children.count(children) > 0) {
108
109
  console.warn(
@@ -147,10 +148,21 @@ const Tabs = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
147
148
  }
148
149
  return children;
149
150
  }, [tabs, children]);
150
- if (process.env.NODE_ENV !== "production") {
151
- console.count("Tabs render......");
152
- }
153
- return /* @__PURE__ */ import_react.default.createElement("div", __spreadProps(__spreadValues({ className: (0, import_clsx.default)(import_classes.tabsRootClass, className) }, others), { ref }), /* @__PURE__ */ import_react.default.createElement(import_TabMask.default, { tabsContainerRef: tabsRef, position: "left" }), /* @__PURE__ */ import_react.default.createElement(import_TabMask.default, { tabsContainerRef: tabsRef, position: "right" }), /* @__PURE__ */ import_react.default.createElement(
151
+ (0, import_react.useEffect)(() => {
152
+ const tabsEl = tabsRef.current;
153
+ if (!tabsEl)
154
+ return;
155
+ const checkScrollable = () => {
156
+ setIsScrollable(tabsEl.scrollWidth > tabsEl.offsetWidth);
157
+ };
158
+ checkScrollable();
159
+ const resizeObserver = new ResizeObserver(checkScrollable);
160
+ resizeObserver.observe(tabsEl);
161
+ return () => {
162
+ resizeObserver.disconnect();
163
+ };
164
+ }, [registrationVersion]);
165
+ return /* @__PURE__ */ import_react.default.createElement("div", __spreadProps(__spreadValues({ className: (0, import_clsx.default)(import_classes.tabsRootClass, className) }, others), { ref }), isScrollable && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_TabMask.default, { position: "left" }), /* @__PURE__ */ import_react.default.createElement(import_TabMask.default, { position: "right" })), /* @__PURE__ */ import_react.default.createElement(
154
166
  "div",
155
167
  {
156
168
  className: `${import_classes.tabsRootClass}-tabs`,
@@ -163,8 +175,7 @@ const Tabs = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
163
175
  {
164
176
  currentValue,
165
177
  registeredTabs,
166
- tabsContainerRef: tabsRef,
167
- registrationVersion
178
+ tabsContainerRef: tabsRef
168
179
  }
169
180
  ),
170
181
  /* @__PURE__ */ import_react.default.createElement(import_TabsContext.TabsContextProvider, { value: contextValue }, renderedTabs)
@@ -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);
@@ -56,6 +56,9 @@ var import_taro = __toESM(require("@tarojs/taro"));
56
56
  var import_utils = require("@bifrostui/utils");
57
57
  var import_classes = require("../classes");
58
58
  var import_queryBatch = require("./utils/queryBatch");
59
+ const isValidTabValue = (value) => {
60
+ return value !== void 0 && value !== null;
61
+ };
59
62
  const TabIndicator = ({
60
63
  currentValue,
61
64
  registeredTabValues,
@@ -63,8 +66,7 @@ const TabIndicator = ({
63
66
  scrollViewId,
64
67
  registrationVersion
65
68
  }) => {
66
- const [transform, setTransform] = (0, import_react.useState)("translate(0px, 0px)");
67
- const [visibility, setVisibility] = (0, import_react.useState)("hidden");
69
+ const indicatorRef = (0, import_react.useRef)(null);
68
70
  const animationTimerRef = (0, import_react.useRef)(null);
69
71
  const positionCacheRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
70
72
  const containerInfoRef = (0, import_react.useRef)(null);
@@ -72,14 +74,20 @@ const TabIndicator = ({
72
74
  const isInitializedRef = (0, import_react.useRef)(false);
73
75
  const initRetryCountRef = (0, import_react.useRef)(0);
74
76
  const maxInitRetries = 5;
77
+ const isInitializingRef = (0, import_react.useRef)(false);
78
+ const initVersionRef = (0, import_react.useRef)(0);
79
+ const isMountedRef = (0, import_react.useRef)(true);
75
80
  const updateIndicatorPosition = (0, import_utils.useEventCallback)(() => {
76
- if (!currentValue || !registeredTabValues.includes(currentValue)) {
77
- setVisibility("hidden");
81
+ const indicator = indicatorRef.current;
82
+ if (!indicator)
83
+ return;
84
+ if (!isValidTabValue(currentValue) || !registeredTabValues.includes(currentValue)) {
85
+ indicator.style.opacity = "0";
78
86
  return;
79
87
  }
80
88
  const cachedPosition = positionCacheRef.current.get(currentValue);
81
89
  const containerInfo = containerInfoRef.current;
82
- if (!cachedPosition || !containerInfo) {
90
+ if (!cachedPosition || !containerInfo || !isInitializedRef.current) {
83
91
  if (!isInitializedRef.current) {
84
92
  initializePositions();
85
93
  }
@@ -89,16 +97,27 @@ const TabIndicator = ({
89
97
  const activeTabWidth = cachedPosition.width;
90
98
  const indicatorWidth = indicatorWidthRef.current;
91
99
  const x = activeTabLeft + (activeTabWidth - indicatorWidth) / 2;
92
- setTransform(`translate(${x}px, 0px)`);
93
- setVisibility("visible");
100
+ indicator.style.transform = `translate(${x}px, 0px)`;
101
+ indicator.style.opacity = "1";
94
102
  });
95
103
  const initializePositions = (0, import_utils.useEventCallback)(() => __async(void 0, null, function* () {
104
+ if (!isMountedRef.current)
105
+ return;
106
+ if (isInitializingRef.current)
107
+ return;
108
+ isInitializingRef.current = true;
109
+ initVersionRef.current += 1;
110
+ const currentVersion = initVersionRef.current;
96
111
  try {
97
112
  const result = yield (0, import_queryBatch.batchQueryTabs)({
98
113
  scrollViewId,
99
114
  wrapperId,
100
115
  tabValues: registeredTabValues
101
116
  });
117
+ if (!isMountedRef.current)
118
+ return;
119
+ if (currentVersion !== initVersionRef.current)
120
+ return;
102
121
  const { scrollView, scrollFields, wrapper, indicator, tabs } = result;
103
122
  if (!scrollView || !wrapper || !indicator || !scrollFields) {
104
123
  if (initRetryCountRef.current < maxInitRetries) {
@@ -107,8 +126,30 @@ const TabIndicator = ({
107
126
  clearTimeout(animationTimerRef.current);
108
127
  }
109
128
  animationTimerRef.current = setTimeout(() => {
129
+ if (!isMountedRef.current)
130
+ return;
131
+ isInitializingRef.current = false;
132
+ initializePositions();
133
+ }, 100);
134
+ } else {
135
+ isInitializingRef.current = false;
136
+ }
137
+ return;
138
+ }
139
+ if (!indicator.width || indicator.width <= 0) {
140
+ if (initRetryCountRef.current < maxInitRetries) {
141
+ initRetryCountRef.current += 1;
142
+ if (animationTimerRef.current) {
143
+ clearTimeout(animationTimerRef.current);
144
+ }
145
+ animationTimerRef.current = setTimeout(() => {
146
+ if (!isMountedRef.current)
147
+ return;
148
+ isInitializingRef.current = false;
110
149
  initializePositions();
111
150
  }, 100);
151
+ } else {
152
+ isInitializingRef.current = false;
112
153
  }
113
154
  return;
114
155
  }
@@ -121,9 +162,8 @@ const TabIndicator = ({
121
162
  registeredTabValues.forEach((value, index) => {
122
163
  const tabRect = tabs[index];
123
164
  if (tabRect && tabRect.width > 0) {
124
- const relativeLeft = tabRect.left - wrapper.left;
125
165
  newCache.set(value, {
126
- left: relativeLeft,
166
+ left: tabRect.left - wrapper.left,
127
167
  width: tabRect.width
128
168
  });
129
169
  }
@@ -135,40 +175,74 @@ const TabIndicator = ({
135
175
  clearTimeout(animationTimerRef.current);
136
176
  }
137
177
  animationTimerRef.current = setTimeout(() => {
178
+ if (!isMountedRef.current)
179
+ return;
180
+ isInitializingRef.current = false;
138
181
  initializePositions();
139
182
  }, 100);
183
+ } else {
184
+ isInitializingRef.current = false;
140
185
  }
141
186
  return;
142
187
  }
188
+ if (currentVersion !== initVersionRef.current || !isMountedRef.current) {
189
+ return;
190
+ }
143
191
  initRetryCountRef.current = 0;
144
192
  positionCacheRef.current = newCache;
145
193
  isInitializedRef.current = true;
194
+ isInitializingRef.current = false;
146
195
  updateIndicatorPosition();
147
196
  } catch (error) {
148
197
  console.error("[TabIndicator] \u6279\u91CF\u67E5\u8BE2\u5931\u8D25:", error);
198
+ isInitializingRef.current = false;
199
+ if (isMountedRef.current && initRetryCountRef.current < maxInitRetries) {
200
+ initRetryCountRef.current += 1;
201
+ if (animationTimerRef.current) {
202
+ clearTimeout(animationTimerRef.current);
203
+ }
204
+ animationTimerRef.current = setTimeout(() => {
205
+ if (!isMountedRef.current)
206
+ return;
207
+ initializePositions();
208
+ }, 100);
209
+ }
149
210
  }
150
211
  }));
151
212
  (0, import_react.useEffect)(() => {
152
213
  if (registeredTabValues.length === 0) {
214
+ if (indicatorRef.current) {
215
+ indicatorRef.current.style.opacity = "0";
216
+ }
153
217
  return void 0;
154
218
  }
219
+ initVersionRef.current += 1;
155
220
  if (animationTimerRef.current) {
156
221
  clearTimeout(animationTimerRef.current);
222
+ animationTimerRef.current = null;
157
223
  }
158
224
  isInitializedRef.current = false;
225
+ isInitializingRef.current = false;
159
226
  initRetryCountRef.current = 0;
160
227
  import_taro.default.nextTick(() => {
228
+ if (!isMountedRef.current)
229
+ return;
161
230
  initializePositions();
162
231
  });
163
232
  return () => {
164
233
  if (animationTimerRef.current) {
165
234
  clearTimeout(animationTimerRef.current);
235
+ animationTimerRef.current = null;
166
236
  }
237
+ initVersionRef.current += 1;
238
+ isInitializingRef.current = false;
167
239
  };
168
240
  }, [registrationVersion, initializePositions]);
169
241
  (0, import_react.useEffect)(() => {
170
- if (!currentValue) {
171
- setVisibility("hidden");
242
+ if (!isValidTabValue(currentValue)) {
243
+ if (indicatorRef.current) {
244
+ indicatorRef.current.style.opacity = "0";
245
+ }
172
246
  return;
173
247
  }
174
248
  updateIndicatorPosition();
@@ -176,10 +250,20 @@ const TabIndicator = ({
176
250
  (0, import_react.useEffect)(() => {
177
251
  var _a, _b;
178
252
  const handleResize = () => {
253
+ if (!isMountedRef.current)
254
+ return;
255
+ initVersionRef.current += 1;
179
256
  isInitializedRef.current = false;
257
+ isInitializingRef.current = false;
180
258
  initRetryCountRef.current = 0;
181
259
  positionCacheRef.current.clear();
260
+ if (animationTimerRef.current) {
261
+ clearTimeout(animationTimerRef.current);
262
+ animationTimerRef.current = null;
263
+ }
182
264
  import_taro.default.nextTick(() => {
265
+ if (!isMountedRef.current)
266
+ return;
183
267
  initializePositions();
184
268
  });
185
269
  };
@@ -189,15 +273,26 @@ const TabIndicator = ({
189
273
  (_b2 = (_a2 = import_taro.default).offWindowResize) == null ? void 0 : _b2.call(_a2, handleResize);
190
274
  };
191
275
  }, [initializePositions]);
276
+ (0, import_react.useEffect)(() => {
277
+ isMountedRef.current = true;
278
+ return () => {
279
+ isMountedRef.current = false;
280
+ if (animationTimerRef.current) {
281
+ clearTimeout(animationTimerRef.current);
282
+ animationTimerRef.current = null;
283
+ }
284
+ };
285
+ }, []);
192
286
  return /* @__PURE__ */ import_react.default.createElement(
193
287
  "div",
194
288
  {
289
+ ref: indicatorRef,
195
290
  id: `${wrapperId}-indicator`,
196
291
  className: (0, import_clsx.default)(import_classes.tabIndicatorClass),
197
292
  style: {
198
- transition: "transform 0.3s ease-in-out",
199
- transform,
200
- visibility
293
+ transition: "transform 0.3s ease-in-out, opacity 0.3s ease-in-out",
294
+ transform: "translate(0px, 0px)",
295
+ opacity: 0
201
296
  }
202
297
  }
203
298
  );
@@ -57,7 +57,7 @@ var import_components = require("@tarojs/components");
57
57
  var import_utils = require("@bifrostui/utils");
58
58
  var import_Tab = __toESM(require("./Tab"));
59
59
  var import_TabIndicator = __toESM(require("./TabIndicator"));
60
- var import_TabMask = __toESM(require("./TabMask"));
60
+ var import_TabMask = __toESM(require("../TabMask"));
61
61
  var import_TabsContext = require("./TabsContext");
62
62
  var import_classes = require("../classes");
63
63
  var import_queryBatch = require("./utils/queryBatch");
@@ -97,8 +97,10 @@ const Tabs = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
97
97
  const [scrollLeft, setScrollLeft] = (0, import_react.useState)(0);
98
98
  const [containerWidth, setContainerWidth] = (0, import_react.useState)(0);
99
99
  const [scrollWidth, setScrollWidth] = (0, import_react.useState)(0);
100
- const scrollLeftUpdateTimerRef = import_react.default.useRef(null);
101
100
  const lastScrollLeftRef = import_react.default.useRef(0);
101
+ const isFirstScroll = import_react.default.useRef(true);
102
+ const [scrollWithAnimation, setScrollWithAnimation] = import_react.default.useState(false);
103
+ const isScrollable = scrollWidth > containerWidth;
102
104
  if (process.env.NODE_ENV !== "production") {
103
105
  if (tabs.length > 0 && import_react.default.Children.count(children) > 0) {
104
106
  console.warn(
@@ -138,14 +140,6 @@ const Tabs = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
138
140
  (e) => {
139
141
  const { scrollLeft: newScrollLeft, scrollWidth: newScrollWidth } = e.detail;
140
142
  lastScrollLeftRef.current = newScrollLeft;
141
- if (scrollLeftUpdateTimerRef.current) {
142
- clearTimeout(scrollLeftUpdateTimerRef.current);
143
- }
144
- scrollLeftUpdateTimerRef.current = setTimeout(() => {
145
- if (Math.abs(lastScrollLeftRef.current - scrollLeft) > 1) {
146
- setScrollLeft(lastScrollLeftRef.current);
147
- }
148
- }, 150);
149
143
  if (newScrollWidth && newScrollWidth !== scrollWidth) {
150
144
  setScrollWidth(newScrollWidth);
151
145
  }
@@ -188,6 +182,12 @@ const Tabs = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
188
182
  );
189
183
  setScrollLeft(finalScrollLeft);
190
184
  lastScrollLeftRef.current = finalScrollLeft;
185
+ if (isFirstScroll.current) {
186
+ import_taro.default.nextTick(() => {
187
+ setScrollWithAnimation(true);
188
+ });
189
+ isFirstScroll.current = false;
190
+ }
191
191
  }));
192
192
  import_react.default.useEffect(() => {
193
193
  if (!currentValue || registeredTabValues.length === 0) {
@@ -221,38 +221,14 @@ const Tabs = /* @__PURE__ */ import_react.default.forwardRef((props, ref) => {
221
221
  }
222
222
  return children;
223
223
  }, [tabs, children]);
224
- import_react.default.useEffect(() => {
225
- return () => {
226
- if (scrollLeftUpdateTimerRef.current) {
227
- clearTimeout(scrollLeftUpdateTimerRef.current);
228
- }
229
- };
230
- }, []);
231
- return /* @__PURE__ */ import_react.default.createElement("div", { className: (0, import_clsx.default)(import_classes.tabsRootClass, className), style, ref }, /* @__PURE__ */ import_react.default.createElement(
232
- import_TabMask.default,
233
- {
234
- position: "left",
235
- scrollLeft,
236
- containerWidth,
237
- scrollWidth
238
- }
239
- ), /* @__PURE__ */ import_react.default.createElement(
240
- import_TabMask.default,
241
- {
242
- position: "right",
243
- scrollLeft,
244
- containerWidth,
245
- scrollWidth
246
- }
247
- ), /* @__PURE__ */ import_react.default.createElement(
224
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: (0, import_clsx.default)(import_classes.tabsRootClass, className), style, ref }, isScrollable && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_TabMask.default, { position: "left" }), /* @__PURE__ */ import_react.default.createElement(import_TabMask.default, { position: "right" })), /* @__PURE__ */ import_react.default.createElement(
248
225
  import_components.ScrollView,
249
226
  {
250
227
  id: scrollViewId,
251
228
  className: import_classes.tabsScrollClass,
252
229
  scrollX: true,
253
- scrollWithAnimation: true,
230
+ scrollWithAnimation,
254
231
  scrollLeft,
255
- scrollAnimationDuration: "200",
256
232
  onScroll: handleScroll,
257
233
  enhanced: true,
258
234
  showScrollbar: false,
@@ -12,5 +12,5 @@ declare const Modal: React.ForwardRefExoticComponent<Omit<ViewProps & {
12
12
  keepMounted?: boolean;
13
13
  } & import("@bifrostui/types").ICommonProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
14
14
  ref?: React.Ref<HTMLDivElement>;
15
- }, keyof import("@bifrostui/types").ICommonProps | "container" | "open" | "disablePortal" | "hideBackdrop" | "BackdropProps" | "onClose" | "disableScrollLock" | "keepMounted">, "ref"> & React.RefAttributes<HTMLDivElement>>;
15
+ }, keyof import("@bifrostui/types").ICommonProps | "keepMounted" | "container" | "disablePortal" | "open" | "hideBackdrop" | "BackdropProps" | "onClose" | "disableScrollLock">, "ref"> & React.RefAttributes<HTMLDivElement>>;
16
16
  export default Modal;