@bifrostui/react 2.0.0-alpha.17 → 2.0.0-alpha.18

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.
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
2
  import { DatePickerProps } from './DatePicker.types';
3
- declare const DatePicker: React.ForwardRefExoticComponent<Omit<DatePickerProps<"div", Omit<import("../Picker").PickerProps<"div", import("..").DrawerProps>, "value" | "defaultValue" | "onChange" | "onClose" | "onConfirm">>, "ref"> & React.RefAttributes<HTMLDivElement>>;
3
+ declare const DatePicker: React.ForwardRefExoticComponent<Omit<DatePickerProps<"div", Omit<import("../Picker").PickerProps<"div", import("..").DrawerProps>, "defaultValue" | "onChange" | "value" | "onClose" | "onConfirm">>, "ref"> & React.RefAttributes<HTMLDivElement>>;
4
4
  export default DatePicker;
@@ -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
+ }, keyof import("@bifrostui/types").ICommonProps | "open" | "onClose" | "container" | "disableScrollLock" | "disablePortal" | "hideBackdrop" | "BackdropProps" | "keepMounted">, "ref"> & React.RefAttributes<HTMLDivElement>>;
16
16
  export default Modal;
@@ -43,23 +43,26 @@ const TabIndicator = ({
43
43
  registrationVersion
44
44
  }) => {
45
45
  const indicatorRef = (0, import_react.useRef)(null);
46
+ const isFirstRender = (0, import_react.useRef)(true);
46
47
  const getActiveTabElement = (0, import_utils.useEventCallback)(
47
48
  (activeValue) => {
48
49
  const tabRef = registeredTabs.current[activeValue];
49
50
  return tabRef == null ? void 0 : tabRef.current;
50
51
  }
51
52
  );
52
- const scrollIntoView = (0, import_utils.useEventCallback)((activeTab) => {
53
- const tabsEl = tabsContainerRef.current;
54
- if (!tabsEl || !activeTab) {
55
- return;
53
+ const scrollIntoView = (0, import_utils.useEventCallback)(
54
+ (activeTab, animate2 = true) => {
55
+ const tabsEl = tabsContainerRef.current;
56
+ if (!tabsEl || !activeTab) {
57
+ return;
58
+ }
59
+ (0, import_scroll.default)(
60
+ tabsEl,
61
+ activeTab.offsetLeft - (tabsEl.offsetWidth - activeTab.offsetWidth) / 2,
62
+ animate2 ? duration : 0
63
+ );
56
64
  }
57
- (0, import_scroll.default)(
58
- tabsEl,
59
- activeTab.offsetLeft - (tabsEl.offsetWidth - activeTab.offsetWidth) / 2,
60
- duration
61
- );
62
- });
65
+ );
63
66
  const animate = (0, import_utils.useEventCallback)(() => {
64
67
  const tabsEl = tabsContainerRef.current;
65
68
  if (!tabsEl)
@@ -79,7 +82,15 @@ const TabIndicator = ({
79
82
  indicator.style.visibility = "visible";
80
83
  const maxScrollDistance = containerScrollWidth - containerWidth;
81
84
  if (maxScrollDistance > 0 && !import_utils.isMini) {
82
- scrollIntoView(activeTab);
85
+ scrollIntoView(activeTab, !isFirstRender.current);
86
+ }
87
+ if (isFirstRender.current) {
88
+ isFirstRender.current = false;
89
+ requestAnimationFrame(() => {
90
+ if (indicator) {
91
+ indicator.style.transition = "transform 0.3s ease-in-out";
92
+ }
93
+ });
83
94
  }
84
95
  } else {
85
96
  indicator.style.visibility = "hidden";
@@ -103,7 +114,7 @@ const TabIndicator = ({
103
114
  ref: indicatorRef,
104
115
  className: (0, import_clsx.default)(`${rootClass}-indicator`),
105
116
  style: {
106
- transition: "transform 0.3s ease-in-out",
117
+ transition: "none",
107
118
  transform: "translate(0px, 0px)",
108
119
  visibility: "hidden"
109
120
  },
@@ -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);
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`,
@@ -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,21 @@ 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 isFirstRender = (0, import_react.useRef)(true);
78
+ const isInitializingRef = (0, import_react.useRef)(false);
79
+ const initVersionRef = (0, import_react.useRef)(0);
80
+ const isMountedRef = (0, import_react.useRef)(true);
75
81
  const updateIndicatorPosition = (0, import_utils.useEventCallback)(() => {
76
- if (!currentValue || !registeredTabValues.includes(currentValue)) {
77
- setVisibility("hidden");
82
+ const indicator = indicatorRef.current;
83
+ if (!indicator)
84
+ return;
85
+ if (!isValidTabValue(currentValue) || !registeredTabValues.includes(currentValue)) {
86
+ indicator.style.visibility = "hidden";
78
87
  return;
79
88
  }
80
89
  const cachedPosition = positionCacheRef.current.get(currentValue);
81
90
  const containerInfo = containerInfoRef.current;
82
- if (!cachedPosition || !containerInfo) {
91
+ if (!cachedPosition || !containerInfo || !isInitializedRef.current) {
83
92
  if (!isInitializedRef.current) {
84
93
  initializePositions();
85
94
  }
@@ -89,16 +98,41 @@ const TabIndicator = ({
89
98
  const activeTabWidth = cachedPosition.width;
90
99
  const indicatorWidth = indicatorWidthRef.current;
91
100
  const x = activeTabLeft + (activeTabWidth - indicatorWidth) / 2;
92
- setTransform(`translate(${x}px, 0px)`);
93
- setVisibility("visible");
101
+ indicator.style.transform = `translate(${x}px, 0px)`;
102
+ if (isFirstRender.current) {
103
+ import_taro.default.nextTick(() => {
104
+ if (!isMountedRef.current || !indicatorRef.current)
105
+ return;
106
+ indicatorRef.current.style.visibility = "visible";
107
+ import_taro.default.nextTick(() => {
108
+ if (!isMountedRef.current || !indicatorRef.current)
109
+ return;
110
+ indicatorRef.current.style.transition = "transform 0.3s ease-in-out";
111
+ });
112
+ });
113
+ isFirstRender.current = false;
114
+ } else {
115
+ indicator.style.visibility = "visible";
116
+ }
94
117
  });
95
118
  const initializePositions = (0, import_utils.useEventCallback)(() => __async(void 0, null, function* () {
119
+ if (!isMountedRef.current)
120
+ return;
121
+ if (isInitializingRef.current)
122
+ return;
123
+ isInitializingRef.current = true;
124
+ initVersionRef.current += 1;
125
+ const currentVersion = initVersionRef.current;
96
126
  try {
97
127
  const result = yield (0, import_queryBatch.batchQueryTabs)({
98
128
  scrollViewId,
99
129
  wrapperId,
100
130
  tabValues: registeredTabValues
101
131
  });
132
+ if (!isMountedRef.current)
133
+ return;
134
+ if (currentVersion !== initVersionRef.current)
135
+ return;
102
136
  const { scrollView, scrollFields, wrapper, indicator, tabs } = result;
103
137
  if (!scrollView || !wrapper || !indicator || !scrollFields) {
104
138
  if (initRetryCountRef.current < maxInitRetries) {
@@ -107,8 +141,30 @@ const TabIndicator = ({
107
141
  clearTimeout(animationTimerRef.current);
108
142
  }
109
143
  animationTimerRef.current = setTimeout(() => {
144
+ if (!isMountedRef.current)
145
+ return;
146
+ isInitializingRef.current = false;
110
147
  initializePositions();
111
148
  }, 100);
149
+ } else {
150
+ isInitializingRef.current = false;
151
+ }
152
+ return;
153
+ }
154
+ if (!indicator.width || indicator.width <= 0) {
155
+ if (initRetryCountRef.current < maxInitRetries) {
156
+ initRetryCountRef.current += 1;
157
+ if (animationTimerRef.current) {
158
+ clearTimeout(animationTimerRef.current);
159
+ }
160
+ animationTimerRef.current = setTimeout(() => {
161
+ if (!isMountedRef.current)
162
+ return;
163
+ isInitializingRef.current = false;
164
+ initializePositions();
165
+ }, 100);
166
+ } else {
167
+ isInitializingRef.current = false;
112
168
  }
113
169
  return;
114
170
  }
@@ -121,9 +177,8 @@ const TabIndicator = ({
121
177
  registeredTabValues.forEach((value, index) => {
122
178
  const tabRect = tabs[index];
123
179
  if (tabRect && tabRect.width > 0) {
124
- const relativeLeft = tabRect.left - wrapper.left;
125
180
  newCache.set(value, {
126
- left: relativeLeft,
181
+ left: tabRect.left - wrapper.left,
127
182
  width: tabRect.width
128
183
  });
129
184
  }
@@ -135,40 +190,74 @@ const TabIndicator = ({
135
190
  clearTimeout(animationTimerRef.current);
136
191
  }
137
192
  animationTimerRef.current = setTimeout(() => {
193
+ if (!isMountedRef.current)
194
+ return;
195
+ isInitializingRef.current = false;
138
196
  initializePositions();
139
197
  }, 100);
198
+ } else {
199
+ isInitializingRef.current = false;
140
200
  }
141
201
  return;
142
202
  }
203
+ if (currentVersion !== initVersionRef.current || !isMountedRef.current) {
204
+ return;
205
+ }
143
206
  initRetryCountRef.current = 0;
144
207
  positionCacheRef.current = newCache;
145
208
  isInitializedRef.current = true;
209
+ isInitializingRef.current = false;
146
210
  updateIndicatorPosition();
147
211
  } catch (error) {
148
212
  console.error("[TabIndicator] \u6279\u91CF\u67E5\u8BE2\u5931\u8D25:", error);
213
+ isInitializingRef.current = false;
214
+ if (isMountedRef.current && initRetryCountRef.current < maxInitRetries) {
215
+ initRetryCountRef.current += 1;
216
+ if (animationTimerRef.current) {
217
+ clearTimeout(animationTimerRef.current);
218
+ }
219
+ animationTimerRef.current = setTimeout(() => {
220
+ if (!isMountedRef.current)
221
+ return;
222
+ initializePositions();
223
+ }, 100);
224
+ }
149
225
  }
150
226
  }));
151
227
  (0, import_react.useEffect)(() => {
152
228
  if (registeredTabValues.length === 0) {
229
+ if (indicatorRef.current) {
230
+ indicatorRef.current.style.visibility = "hidden";
231
+ }
153
232
  return void 0;
154
233
  }
234
+ initVersionRef.current += 1;
155
235
  if (animationTimerRef.current) {
156
236
  clearTimeout(animationTimerRef.current);
237
+ animationTimerRef.current = null;
157
238
  }
158
239
  isInitializedRef.current = false;
240
+ isInitializingRef.current = false;
159
241
  initRetryCountRef.current = 0;
160
242
  import_taro.default.nextTick(() => {
243
+ if (!isMountedRef.current)
244
+ return;
161
245
  initializePositions();
162
246
  });
163
247
  return () => {
164
248
  if (animationTimerRef.current) {
165
249
  clearTimeout(animationTimerRef.current);
250
+ animationTimerRef.current = null;
166
251
  }
252
+ initVersionRef.current += 1;
253
+ isInitializingRef.current = false;
167
254
  };
168
255
  }, [registrationVersion, initializePositions]);
169
256
  (0, import_react.useEffect)(() => {
170
- if (!currentValue) {
171
- setVisibility("hidden");
257
+ if (!isValidTabValue(currentValue)) {
258
+ if (indicatorRef.current) {
259
+ indicatorRef.current.style.visibility = "hidden";
260
+ }
172
261
  return;
173
262
  }
174
263
  updateIndicatorPosition();
@@ -176,10 +265,20 @@ const TabIndicator = ({
176
265
  (0, import_react.useEffect)(() => {
177
266
  var _a, _b;
178
267
  const handleResize = () => {
268
+ if (!isMountedRef.current)
269
+ return;
270
+ initVersionRef.current += 1;
179
271
  isInitializedRef.current = false;
272
+ isInitializingRef.current = false;
180
273
  initRetryCountRef.current = 0;
181
274
  positionCacheRef.current.clear();
275
+ if (animationTimerRef.current) {
276
+ clearTimeout(animationTimerRef.current);
277
+ animationTimerRef.current = null;
278
+ }
182
279
  import_taro.default.nextTick(() => {
280
+ if (!isMountedRef.current)
281
+ return;
183
282
  initializePositions();
184
283
  });
185
284
  };
@@ -189,15 +288,26 @@ const TabIndicator = ({
189
288
  (_b2 = (_a2 = import_taro.default).offWindowResize) == null ? void 0 : _b2.call(_a2, handleResize);
190
289
  };
191
290
  }, [initializePositions]);
291
+ (0, import_react.useEffect)(() => {
292
+ isMountedRef.current = true;
293
+ return () => {
294
+ isMountedRef.current = false;
295
+ if (animationTimerRef.current) {
296
+ clearTimeout(animationTimerRef.current);
297
+ animationTimerRef.current = null;
298
+ }
299
+ };
300
+ }, []);
192
301
  return /* @__PURE__ */ import_react.default.createElement(
193
302
  "div",
194
303
  {
304
+ ref: indicatorRef,
195
305
  id: `${wrapperId}-indicator`,
196
306
  className: (0, import_clsx.default)(import_classes.tabIndicatorClass),
197
307
  style: {
198
- transition: "transform 0.3s ease-in-out",
199
- transform,
200
- visibility
308
+ transition: "none",
309
+ transform: "translate(0px, 0px)",
310
+ visibility: "hidden"
201
311
  }
202
312
  }
203
313
  );
@@ -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
+ }, "open" | "container" | keyof import("@bifrostui/types").ICommonProps | "disablePortal" | "hideBackdrop" | "BackdropProps" | "onClose" | "disableScrollLock" | "keepMounted">, "ref"> & React.RefAttributes<HTMLDivElement>>;
16
16
  export default Modal;
@@ -11,23 +11,26 @@ const TabIndicator = ({
11
11
  registrationVersion
12
12
  }) => {
13
13
  const indicatorRef = useRef(null);
14
+ const isFirstRender = useRef(true);
14
15
  const getActiveTabElement = useEventCallback(
15
16
  (activeValue) => {
16
17
  const tabRef = registeredTabs.current[activeValue];
17
18
  return tabRef == null ? void 0 : tabRef.current;
18
19
  }
19
20
  );
20
- const scrollIntoView = useEventCallback((activeTab) => {
21
- const tabsEl = tabsContainerRef.current;
22
- if (!tabsEl || !activeTab) {
23
- return;
21
+ const scrollIntoView = useEventCallback(
22
+ (activeTab, animate2 = true) => {
23
+ const tabsEl = tabsContainerRef.current;
24
+ if (!tabsEl || !activeTab) {
25
+ return;
26
+ }
27
+ scrollLeftTo(
28
+ tabsEl,
29
+ activeTab.offsetLeft - (tabsEl.offsetWidth - activeTab.offsetWidth) / 2,
30
+ animate2 ? duration : 0
31
+ );
24
32
  }
25
- scrollLeftTo(
26
- tabsEl,
27
- activeTab.offsetLeft - (tabsEl.offsetWidth - activeTab.offsetWidth) / 2,
28
- duration
29
- );
30
- });
33
+ );
31
34
  const animate = useEventCallback(() => {
32
35
  const tabsEl = tabsContainerRef.current;
33
36
  if (!tabsEl)
@@ -47,7 +50,15 @@ const TabIndicator = ({
47
50
  indicator.style.visibility = "visible";
48
51
  const maxScrollDistance = containerScrollWidth - containerWidth;
49
52
  if (maxScrollDistance > 0 && !isMini) {
50
- scrollIntoView(activeTab);
53
+ scrollIntoView(activeTab, !isFirstRender.current);
54
+ }
55
+ if (isFirstRender.current) {
56
+ isFirstRender.current = false;
57
+ requestAnimationFrame(() => {
58
+ if (indicator) {
59
+ indicator.style.transition = "transform 0.3s ease-in-out";
60
+ }
61
+ });
51
62
  }
52
63
  } else {
53
64
  indicator.style.visibility = "hidden";
@@ -71,7 +82,7 @@ const TabIndicator = ({
71
82
  ref: indicatorRef,
72
83
  className: clsx(`${rootClass}-indicator`),
73
84
  style: {
74
- transition: "transform 0.3s ease-in-out",
85
+ transition: "none",
75
86
  transform: "translate(0px, 0px)",
76
87
  visibility: "hidden"
77
88
  },
@@ -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;
@@ -1,59 +1,18 @@
1
- import React, { useEffect, useMemo, useRef } from "react";
1
+ import React from "react";
2
2
  import clsx from "clsx";
3
- import { throttle } from "@bifrostui/utils";
4
- const rootClass = "bui-tabs";
5
- const TabMask = ({ tabsContainerRef, position }) => {
6
- const maskRef = useRef(null);
7
- const updateMaskOpacity = useMemo(
8
- () => throttle(
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
- [tabsContainerRef, position]
33
- );
34
- useEffect(() => {
35
- const tabsEl = tabsContainerRef.current;
36
- if (!tabsEl)
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.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
- if (process.env.NODE_ENV !== "production") {
121
- console.count("Tabs render......");
122
- }
123
- return /* @__PURE__ */ React.createElement("div", __spreadProps(__spreadValues({ className: clsx(tabsRootClass, className) }, others), { ref }), /* @__PURE__ */ React.createElement(TabMask, { tabsContainerRef: tabsRef, position: "left" }), /* @__PURE__ */ React.createElement(TabMask, { tabsContainerRef: tabsRef, position: "right" }), /* @__PURE__ */ React.createElement(
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`,
@@ -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, useState } from "react";
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 [transform, setTransform] = useState("translate(0px, 0px)");
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,21 @@ const TabIndicator = ({
40
42
  const isInitializedRef = useRef(false);
41
43
  const initRetryCountRef = useRef(0);
42
44
  const maxInitRetries = 5;
45
+ const isFirstRender = useRef(true);
46
+ const isInitializingRef = useRef(false);
47
+ const initVersionRef = useRef(0);
48
+ const isMountedRef = useRef(true);
43
49
  const updateIndicatorPosition = useEventCallback(() => {
44
- if (!currentValue || !registeredTabValues.includes(currentValue)) {
45
- setVisibility("hidden");
50
+ const indicator = indicatorRef.current;
51
+ if (!indicator)
52
+ return;
53
+ if (!isValidTabValue(currentValue) || !registeredTabValues.includes(currentValue)) {
54
+ indicator.style.visibility = "hidden";
46
55
  return;
47
56
  }
48
57
  const cachedPosition = positionCacheRef.current.get(currentValue);
49
58
  const containerInfo = containerInfoRef.current;
50
- if (!cachedPosition || !containerInfo) {
59
+ if (!cachedPosition || !containerInfo || !isInitializedRef.current) {
51
60
  if (!isInitializedRef.current) {
52
61
  initializePositions();
53
62
  }
@@ -57,16 +66,41 @@ const TabIndicator = ({
57
66
  const activeTabWidth = cachedPosition.width;
58
67
  const indicatorWidth = indicatorWidthRef.current;
59
68
  const x = activeTabLeft + (activeTabWidth - indicatorWidth) / 2;
60
- setTransform(`translate(${x}px, 0px)`);
61
- setVisibility("visible");
69
+ indicator.style.transform = `translate(${x}px, 0px)`;
70
+ if (isFirstRender.current) {
71
+ Taro.nextTick(() => {
72
+ if (!isMountedRef.current || !indicatorRef.current)
73
+ return;
74
+ indicatorRef.current.style.visibility = "visible";
75
+ Taro.nextTick(() => {
76
+ if (!isMountedRef.current || !indicatorRef.current)
77
+ return;
78
+ indicatorRef.current.style.transition = "transform 0.3s ease-in-out";
79
+ });
80
+ });
81
+ isFirstRender.current = false;
82
+ } else {
83
+ indicator.style.visibility = "visible";
84
+ }
62
85
  });
63
86
  const initializePositions = useEventCallback(() => __async(void 0, null, function* () {
87
+ if (!isMountedRef.current)
88
+ return;
89
+ if (isInitializingRef.current)
90
+ return;
91
+ isInitializingRef.current = true;
92
+ initVersionRef.current += 1;
93
+ const currentVersion = initVersionRef.current;
64
94
  try {
65
95
  const result = yield batchQueryTabs({
66
96
  scrollViewId,
67
97
  wrapperId,
68
98
  tabValues: registeredTabValues
69
99
  });
100
+ if (!isMountedRef.current)
101
+ return;
102
+ if (currentVersion !== initVersionRef.current)
103
+ return;
70
104
  const { scrollView, scrollFields, wrapper, indicator, tabs } = result;
71
105
  if (!scrollView || !wrapper || !indicator || !scrollFields) {
72
106
  if (initRetryCountRef.current < maxInitRetries) {
@@ -75,8 +109,30 @@ const TabIndicator = ({
75
109
  clearTimeout(animationTimerRef.current);
76
110
  }
77
111
  animationTimerRef.current = setTimeout(() => {
112
+ if (!isMountedRef.current)
113
+ return;
114
+ isInitializingRef.current = false;
78
115
  initializePositions();
79
116
  }, 100);
117
+ } else {
118
+ isInitializingRef.current = false;
119
+ }
120
+ return;
121
+ }
122
+ if (!indicator.width || indicator.width <= 0) {
123
+ if (initRetryCountRef.current < maxInitRetries) {
124
+ initRetryCountRef.current += 1;
125
+ if (animationTimerRef.current) {
126
+ clearTimeout(animationTimerRef.current);
127
+ }
128
+ animationTimerRef.current = setTimeout(() => {
129
+ if (!isMountedRef.current)
130
+ return;
131
+ isInitializingRef.current = false;
132
+ initializePositions();
133
+ }, 100);
134
+ } else {
135
+ isInitializingRef.current = false;
80
136
  }
81
137
  return;
82
138
  }
@@ -89,9 +145,8 @@ const TabIndicator = ({
89
145
  registeredTabValues.forEach((value, index) => {
90
146
  const tabRect = tabs[index];
91
147
  if (tabRect && tabRect.width > 0) {
92
- const relativeLeft = tabRect.left - wrapper.left;
93
148
  newCache.set(value, {
94
- left: relativeLeft,
149
+ left: tabRect.left - wrapper.left,
95
150
  width: tabRect.width
96
151
  });
97
152
  }
@@ -103,40 +158,74 @@ const TabIndicator = ({
103
158
  clearTimeout(animationTimerRef.current);
104
159
  }
105
160
  animationTimerRef.current = setTimeout(() => {
161
+ if (!isMountedRef.current)
162
+ return;
163
+ isInitializingRef.current = false;
106
164
  initializePositions();
107
165
  }, 100);
166
+ } else {
167
+ isInitializingRef.current = false;
108
168
  }
109
169
  return;
110
170
  }
171
+ if (currentVersion !== initVersionRef.current || !isMountedRef.current) {
172
+ return;
173
+ }
111
174
  initRetryCountRef.current = 0;
112
175
  positionCacheRef.current = newCache;
113
176
  isInitializedRef.current = true;
177
+ isInitializingRef.current = false;
114
178
  updateIndicatorPosition();
115
179
  } catch (error) {
116
180
  console.error("[TabIndicator] \u6279\u91CF\u67E5\u8BE2\u5931\u8D25:", error);
181
+ isInitializingRef.current = false;
182
+ if (isMountedRef.current && initRetryCountRef.current < maxInitRetries) {
183
+ initRetryCountRef.current += 1;
184
+ if (animationTimerRef.current) {
185
+ clearTimeout(animationTimerRef.current);
186
+ }
187
+ animationTimerRef.current = setTimeout(() => {
188
+ if (!isMountedRef.current)
189
+ return;
190
+ initializePositions();
191
+ }, 100);
192
+ }
117
193
  }
118
194
  }));
119
195
  useEffect(() => {
120
196
  if (registeredTabValues.length === 0) {
197
+ if (indicatorRef.current) {
198
+ indicatorRef.current.style.visibility = "hidden";
199
+ }
121
200
  return void 0;
122
201
  }
202
+ initVersionRef.current += 1;
123
203
  if (animationTimerRef.current) {
124
204
  clearTimeout(animationTimerRef.current);
205
+ animationTimerRef.current = null;
125
206
  }
126
207
  isInitializedRef.current = false;
208
+ isInitializingRef.current = false;
127
209
  initRetryCountRef.current = 0;
128
210
  Taro.nextTick(() => {
211
+ if (!isMountedRef.current)
212
+ return;
129
213
  initializePositions();
130
214
  });
131
215
  return () => {
132
216
  if (animationTimerRef.current) {
133
217
  clearTimeout(animationTimerRef.current);
218
+ animationTimerRef.current = null;
134
219
  }
220
+ initVersionRef.current += 1;
221
+ isInitializingRef.current = false;
135
222
  };
136
223
  }, [registrationVersion, initializePositions]);
137
224
  useEffect(() => {
138
- if (!currentValue) {
139
- setVisibility("hidden");
225
+ if (!isValidTabValue(currentValue)) {
226
+ if (indicatorRef.current) {
227
+ indicatorRef.current.style.visibility = "hidden";
228
+ }
140
229
  return;
141
230
  }
142
231
  updateIndicatorPosition();
@@ -144,10 +233,20 @@ const TabIndicator = ({
144
233
  useEffect(() => {
145
234
  var _a, _b;
146
235
  const handleResize = () => {
236
+ if (!isMountedRef.current)
237
+ return;
238
+ initVersionRef.current += 1;
147
239
  isInitializedRef.current = false;
240
+ isInitializingRef.current = false;
148
241
  initRetryCountRef.current = 0;
149
242
  positionCacheRef.current.clear();
243
+ if (animationTimerRef.current) {
244
+ clearTimeout(animationTimerRef.current);
245
+ animationTimerRef.current = null;
246
+ }
150
247
  Taro.nextTick(() => {
248
+ if (!isMountedRef.current)
249
+ return;
151
250
  initializePositions();
152
251
  });
153
252
  };
@@ -157,15 +256,26 @@ const TabIndicator = ({
157
256
  (_b2 = (_a2 = Taro).offWindowResize) == null ? void 0 : _b2.call(_a2, handleResize);
158
257
  };
159
258
  }, [initializePositions]);
259
+ useEffect(() => {
260
+ isMountedRef.current = true;
261
+ return () => {
262
+ isMountedRef.current = false;
263
+ if (animationTimerRef.current) {
264
+ clearTimeout(animationTimerRef.current);
265
+ animationTimerRef.current = null;
266
+ }
267
+ };
268
+ }, []);
160
269
  return /* @__PURE__ */ React.createElement(
161
270
  "div",
162
271
  {
272
+ ref: indicatorRef,
163
273
  id: `${wrapperId}-indicator`,
164
274
  className: clsx(tabIndicatorClass),
165
275
  style: {
166
- transition: "transform 0.3s ease-in-out",
167
- transform,
168
- visibility
276
+ transition: "none",
277
+ transform: "translate(0px, 0px)",
278
+ visibility: "hidden"
169
279
  }
170
280
  }
171
281
  );
@@ -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 "./TabMask";
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.useEffect(() => {
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: true,
205
+ scrollWithAnimation,
229
206
  scrollLeft,
230
- scrollAnimationDuration: "200",
231
207
  onScroll: handleScroll,
232
208
  enhanced: true,
233
209
  showScrollbar: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bifrostui/react",
3
- "version": "2.0.0-alpha.17",
3
+ "version": "2.0.0-alpha.18",
4
4
  "description": "React components for building mobile application",
5
5
  "homepage": "http://bui.taopiaopiao.com",
6
6
  "license": "MIT",
@@ -43,10 +43,10 @@
43
43
  "clsx": "^2.1.1",
44
44
  "dayjs": "^1.11.7",
45
45
  "swiper": "^8.1.5",
46
- "@bifrostui/styles": "2.0.0-alpha.17",
47
- "@bifrostui/types": "2.0.0-alpha.17",
48
- "@bifrostui/icons": "2.0.0-alpha.17",
49
- "@bifrostui/utils": "2.0.0-alpha.17"
46
+ "@bifrostui/icons": "2.0.0-alpha.18",
47
+ "@bifrostui/types": "2.0.0-alpha.18",
48
+ "@bifrostui/utils": "2.0.0-alpha.18",
49
+ "@bifrostui/styles": "2.0.0-alpha.18"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@tarojs/components": "^3.0.0",
@@ -1,13 +0,0 @@
1
- import React from 'react';
2
- export interface TabMaskProps {
3
- /** 位置:左侧或右侧 */
4
- position: 'left' | 'right';
5
- /** 当前滚动位置 */
6
- scrollLeft: number;
7
- /** 容器宽度 */
8
- containerWidth: number;
9
- /** 滚动区域总宽度 */
10
- scrollWidth: number;
11
- }
12
- declare const _default: React.NamedExoticComponent<TabMaskProps>;
13
- export default _default;
@@ -1,66 +0,0 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
- var TabMask_exports = {};
29
- __export(TabMask_exports, {
30
- default: () => TabMask_default
31
- });
32
- module.exports = __toCommonJS(TabMask_exports);
33
- var import_react = __toESM(require("react"));
34
- var import_clsx = __toESM(require("clsx"));
35
- var import_classes = require("../classes");
36
- const TabMask = ({
37
- position,
38
- scrollLeft,
39
- containerWidth,
40
- scrollWidth
41
- }) => {
42
- const shouldShow = import_react.default.useMemo(() => {
43
- if (position === "left") {
44
- return scrollLeft > 0;
45
- }
46
- const rightRange = Math.abs(scrollWidth - (scrollLeft + containerWidth));
47
- return rightRange > 1;
48
- }, [position, scrollLeft, containerWidth, scrollWidth]);
49
- return /* @__PURE__ */ import_react.default.createElement(
50
- "div",
51
- {
52
- className: (0, import_clsx.default)(
53
- import_classes.tabMaskClass,
54
- position === "left" ? import_classes.tabMaskLeftClass : import_classes.tabMaskRightClass
55
- ),
56
- style: {
57
- opacity: shouldShow ? 1 : 0,
58
- transition: "opacity 0.1s ease-in-out"
59
- }
60
- }
61
- );
62
- };
63
- TabMask.displayName = "BuiTabsMask";
64
- var TabMask_default = /* @__PURE__ */ import_react.default.memo(TabMask, (prevProps, nextProps) => {
65
- return prevProps.position === nextProps.position && prevProps.scrollLeft === nextProps.scrollLeft && prevProps.containerWidth === nextProps.containerWidth && prevProps.scrollWidth === nextProps.scrollWidth;
66
- });
@@ -1,13 +0,0 @@
1
- import React from 'react';
2
- export interface TabMaskProps {
3
- /** 位置:左侧或右侧 */
4
- position: 'left' | 'right';
5
- /** 当前滚动位置 */
6
- scrollLeft: number;
7
- /** 容器宽度 */
8
- containerWidth: number;
9
- /** 滚动区域总宽度 */
10
- scrollWidth: number;
11
- }
12
- declare const _default: React.NamedExoticComponent<TabMaskProps>;
13
- export default _default;
@@ -1,37 +0,0 @@
1
- import React from "react";
2
- import clsx from "clsx";
3
- import { tabMaskClass, tabMaskLeftClass, tabMaskRightClass } from "../classes";
4
- const TabMask = ({
5
- position,
6
- scrollLeft,
7
- containerWidth,
8
- scrollWidth
9
- }) => {
10
- const shouldShow = React.useMemo(() => {
11
- if (position === "left") {
12
- return scrollLeft > 0;
13
- }
14
- const rightRange = Math.abs(scrollWidth - (scrollLeft + containerWidth));
15
- return rightRange > 1;
16
- }, [position, scrollLeft, containerWidth, scrollWidth]);
17
- return /* @__PURE__ */ React.createElement(
18
- "div",
19
- {
20
- className: clsx(
21
- tabMaskClass,
22
- position === "left" ? tabMaskLeftClass : tabMaskRightClass
23
- ),
24
- style: {
25
- opacity: shouldShow ? 1 : 0,
26
- transition: "opacity 0.1s ease-in-out"
27
- }
28
- }
29
- );
30
- };
31
- TabMask.displayName = "BuiTabsMask";
32
- var TabMask_default = /* @__PURE__ */ React.memo(TabMask, (prevProps, nextProps) => {
33
- return prevProps.position === nextProps.position && prevProps.scrollLeft === nextProps.scrollLeft && prevProps.containerWidth === nextProps.containerWidth && prevProps.scrollWidth === nextProps.scrollWidth;
34
- });
35
- export {
36
- TabMask_default as default
37
- };