@sheinx/hooks 3.9.2-beta.1 → 3.9.2-beta.3

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.
@@ -19,6 +19,21 @@ export interface UseCollapseAnimationOptions {
19
19
  * @default 'cubic-bezier(.2,0,0,1)'
20
20
  */
21
21
  timingFunction?: string;
22
+ /**
23
+ * 父元素的 open class 名称
24
+ * 用于在获取 scrollHeight 时临时添加,确保用户依赖此 class 的样式生效
25
+ */
26
+ parentOpenClassName?: string;
27
+ }
28
+ export interface UseCollapseAnimationResult {
29
+ /**
30
+ * 是否应该隐藏元素(动画结束后且 isOpen 为 false 时返回 true)
31
+ */
32
+ shouldHide: boolean;
33
+ /**
34
+ * 是否应该保持 open 状态(动画进行中时返回 true)
35
+ */
36
+ shouldKeepOpen: boolean;
22
37
  }
23
38
  /**
24
39
  * 为元素添加折叠/展开动画的 Hook
@@ -26,10 +41,11 @@ export interface UseCollapseAnimationOptions {
26
41
  * @example
27
42
  * ```tsx
28
43
  * const ref = useRef<HTMLDivElement>(null);
29
- * useCollapseAnimation(ref, { isOpen: true });
44
+ * const { shouldHide, shouldKeepOpen } = useCollapseAnimation(ref, { isOpen: true });
30
45
  *
31
- * return <div ref={ref}>Content</div>;
46
+ * return <div ref={ref} className={shouldHide ? 'hide' : ''}>Content</div>;
32
47
  * ```
48
+ * @returns 动画状态对象
33
49
  */
34
- export declare function useCollapseAnimation<T extends HTMLElement = HTMLElement>(elementRef: React.RefObject<T>, options: UseCollapseAnimationOptions): void;
50
+ export declare function useCollapseAnimation<T extends HTMLElement = HTMLElement>(elementRef: React.RefObject<T>, options: UseCollapseAnimationOptions): UseCollapseAnimationResult;
35
51
  //# sourceMappingURL=use-collapse-animation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-collapse-animation.d.ts","sourceRoot":"","sources":["use-collapse-animation.ts"],"names":[],"mappings":";AAEA,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EACtE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAC9B,OAAO,EAAE,2BAA2B,QAwFrC"}
1
+ {"version":3,"file":"use-collapse-animation.d.ts","sourceRoot":"","sources":["use-collapse-animation.ts"],"names":[],"mappings":";AAEA,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EACtE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAC9B,OAAO,EAAE,2BAA2B,GACnC,0BAA0B,CAoI5B"}
@@ -5,16 +5,23 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.useCollapseAnimation = useCollapseAnimation;
7
7
  var _react = require("react");
8
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
9
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
10
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
11
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
12
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
13
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
8
14
  /**
9
15
  * 为元素添加折叠/展开动画的 Hook
10
16
  *
11
17
  * @example
12
18
  * ```tsx
13
19
  * const ref = useRef<HTMLDivElement>(null);
14
- * useCollapseAnimation(ref, { isOpen: true });
20
+ * const { shouldHide, shouldKeepOpen } = useCollapseAnimation(ref, { isOpen: true });
15
21
  *
16
- * return <div ref={ref}>Content</div>;
22
+ * return <div ref={ref} className={shouldHide ? 'hide' : ''}>Content</div>;
17
23
  * ```
24
+ * @returns 动画状态对象
18
25
  */
19
26
  function useCollapseAnimation(elementRef, options) {
20
27
  var isOpen = options.isOpen,
@@ -23,8 +30,17 @@ function useCollapseAnimation(elementRef, options) {
23
30
  _options$disabled = options.disabled,
24
31
  disabled = _options$disabled === void 0 ? false : _options$disabled,
25
32
  _options$timingFuncti = options.timingFunction,
26
- timingFunction = _options$timingFuncti === void 0 ? 'cubic-bezier(.2,0,0,1)' : _options$timingFuncti;
33
+ timingFunction = _options$timingFuncti === void 0 ? 'cubic-bezier(.2,0,0,1)' : _options$timingFuncti,
34
+ parentOpenClassName = options.parentOpenClassName;
27
35
  var isFirstRenderRef = (0, _react.useRef)(true);
36
+ var _useState = (0, _react.useState)(!isOpen),
37
+ _useState2 = _slicedToArray(_useState, 2),
38
+ shouldHide = _useState2[0],
39
+ setShouldHide = _useState2[1];
40
+ var _useState3 = (0, _react.useState)(false),
41
+ _useState4 = _slicedToArray(_useState3, 2),
42
+ isAnimating = _useState4[0],
43
+ setIsAnimating = _useState4[1];
28
44
 
29
45
  // 当 disabled 状态变化时,重置首次渲染标记
30
46
  (0, _react.useEffect)(function () {
@@ -32,6 +48,8 @@ function useCollapseAnimation(elementRef, options) {
32
48
  isFirstRenderRef.current = true;
33
49
  }
34
50
  }, [disabled]);
51
+
52
+ // 使用 useLayoutEffect 确保动画状态在 DOM 更新前同步设置
35
53
  (0, _react.useEffect)(function () {
36
54
  if (!elementRef.current) return;
37
55
  var el = elementRef.current;
@@ -46,9 +64,6 @@ function useCollapseAnimation(elementRef, options) {
46
64
  }
47
65
  var timer = null;
48
66
 
49
- // 设置 display: block,让元素始终可见,由高度控制折叠
50
- el.style.display = 'block';
51
-
52
67
  // 首次渲染时,直接设置初始状态,不做动画
53
68
  if (isFirstRenderRef.current) {
54
69
  isFirstRenderRef.current = false;
@@ -59,9 +74,13 @@ function useCollapseAnimation(elementRef, options) {
59
74
  return;
60
75
  }
61
76
  if (isOpen) {
62
- // 展开动画
77
+ // 展开动画 - 先显示元素
78
+ setShouldHide(false);
79
+ setIsAnimating(true);
80
+ el.style.display = 'block';
63
81
  el.style.height = '0px';
64
82
  el.style.overflow = 'hidden';
83
+ el.style.opacity = '0';
65
84
 
66
85
  // 强制重绘
67
86
  void el.offsetHeight;
@@ -71,8 +90,9 @@ function useCollapseAnimation(elementRef, options) {
71
90
 
72
91
  // 启动动画
73
92
  requestAnimationFrame(function () {
74
- el.style.transition = "height ".concat(duration, "ms ").concat(timingFunction);
93
+ el.style.transition = "height ".concat(duration, "ms ").concat(timingFunction, ", opacity ").concat(duration, "ms ").concat(timingFunction);
75
94
  el.style.height = "".concat(scrollHeight, "px");
95
+ el.style.opacity = '1';
76
96
 
77
97
  // 动画结束后恢复 auto
78
98
  timer = setTimeout(function () {
@@ -80,20 +100,45 @@ function useCollapseAnimation(elementRef, options) {
80
100
  el.style.height = '';
81
101
  el.style.overflow = '';
82
102
  el.style.transition = '';
103
+ el.style.opacity = '';
104
+ setIsAnimating(false);
83
105
  }
84
106
  }, duration);
85
107
  });
86
108
  } else {
87
109
  // 收起动画
110
+ setIsAnimating(true);
111
+
112
+ // 临时添加父元素的 open class,确保用户依赖此 class 的布局样式在获取高度时生效
113
+ var parentElement = el.parentElement;
114
+ var needTempClass = parentOpenClassName && parentElement;
115
+ if (needTempClass) {
116
+ parentElement.classList.add(parentOpenClassName);
117
+ }
118
+
119
+ // 强制重绘,确保临时 class 生效
120
+ void el.offsetHeight;
88
121
  var _scrollHeight = el.scrollHeight;
89
122
  el.style.height = "".concat(_scrollHeight, "px");
90
123
  el.style.overflow = 'hidden';
124
+ el.style.opacity = '1';
91
125
 
92
126
  // 强制重绘
93
127
  void el.offsetHeight;
94
128
  requestAnimationFrame(function () {
95
- el.style.transition = "height ".concat(duration, "ms ").concat(timingFunction);
129
+ el.style.transition = "height ".concat(duration, "ms ").concat(timingFunction, ", opacity ").concat(duration, "ms ").concat(timingFunction);
96
130
  el.style.height = '0px';
131
+ el.style.opacity = '0';
132
+
133
+ // 动画结束后隐藏元素
134
+ timer = setTimeout(function () {
135
+ // 移除临时 class
136
+ if (needTempClass) {
137
+ parentElement.classList.remove(parentOpenClassName);
138
+ }
139
+ setShouldHide(true);
140
+ setIsAnimating(false);
141
+ }, duration);
97
142
  });
98
143
  }
99
144
 
@@ -104,4 +149,14 @@ function useCollapseAnimation(elementRef, options) {
104
149
  }
105
150
  };
106
151
  }, [isOpen, disabled, duration, timingFunction]);
152
+ if (disabled) {
153
+ return {
154
+ shouldHide: !isOpen,
155
+ shouldKeepOpen: false
156
+ };
157
+ }
158
+ return {
159
+ shouldHide: shouldHide,
160
+ shouldKeepOpen: isAnimating && !isOpen
161
+ };
107
162
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-popup.d.ts","sourceRoot":"","sources":["use-popup.ts"],"names":[],"mappings":"AAAA,OAAO,KAA4E,MAAM,OAAO,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAOhE,QAAA,MAAM,QAAQ,UAAW,cAAc;;;;;;0BAqHK;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;0BAa9B;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;;;;;qBAnC7B;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAvEzC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;8BAOvC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;2BAyJxD,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;6BAMxC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;;CA8BlE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"use-popup.d.ts","sourceRoot":"","sources":["use-popup.ts"],"names":[],"mappings":"AAAA,OAAO,KAA4E,MAAM,OAAO,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAOhE,QAAA,MAAM,QAAQ,UAAW,cAAc;;;;;;0BAsHK;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;0BAa9B;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;;;;;qBAnC7B;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAxEzC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;8BAOvC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;2BA0JxD,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;6BAMxC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;;CA8BlE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -64,9 +64,10 @@ var usePopup = function usePopup(props) {
64
64
  var changeOpen = function changeOpen(openIn, delay) {
65
65
  if (context.triggerTimer) clearTimeout(context.triggerTimer);
66
66
  context.triggerTimer = setTimeout(function () {
67
- var _props$onCollapse;
68
- if (open === openIn) return;
69
- (_props$onCollapse = props.onCollapse) === null || _props$onCollapse === void 0 || _props$onCollapse.call(props, openIn);
67
+ if (open !== openIn) {
68
+ var _props$onCollapse;
69
+ (_props$onCollapse = props.onCollapse) === null || _props$onCollapse === void 0 || _props$onCollapse.call(props, openIn);
70
+ }
70
71
  if (props.open === undefined) {
71
72
  setOpenState(openIn);
72
73
  }
@@ -19,6 +19,21 @@ export interface UseCollapseAnimationOptions {
19
19
  * @default 'cubic-bezier(.2,0,0,1)'
20
20
  */
21
21
  timingFunction?: string;
22
+ /**
23
+ * 父元素的 open class 名称
24
+ * 用于在获取 scrollHeight 时临时添加,确保用户依赖此 class 的样式生效
25
+ */
26
+ parentOpenClassName?: string;
27
+ }
28
+ export interface UseCollapseAnimationResult {
29
+ /**
30
+ * 是否应该隐藏元素(动画结束后且 isOpen 为 false 时返回 true)
31
+ */
32
+ shouldHide: boolean;
33
+ /**
34
+ * 是否应该保持 open 状态(动画进行中时返回 true)
35
+ */
36
+ shouldKeepOpen: boolean;
22
37
  }
23
38
  /**
24
39
  * 为元素添加折叠/展开动画的 Hook
@@ -26,10 +41,11 @@ export interface UseCollapseAnimationOptions {
26
41
  * @example
27
42
  * ```tsx
28
43
  * const ref = useRef<HTMLDivElement>(null);
29
- * useCollapseAnimation(ref, { isOpen: true });
44
+ * const { shouldHide, shouldKeepOpen } = useCollapseAnimation(ref, { isOpen: true });
30
45
  *
31
- * return <div ref={ref}>Content</div>;
46
+ * return <div ref={ref} className={shouldHide ? 'hide' : ''}>Content</div>;
32
47
  * ```
48
+ * @returns 动画状态对象
33
49
  */
34
- export declare function useCollapseAnimation<T extends HTMLElement = HTMLElement>(elementRef: React.RefObject<T>, options: UseCollapseAnimationOptions): void;
50
+ export declare function useCollapseAnimation<T extends HTMLElement = HTMLElement>(elementRef: React.RefObject<T>, options: UseCollapseAnimationOptions): UseCollapseAnimationResult;
35
51
  //# sourceMappingURL=use-collapse-animation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-collapse-animation.d.ts","sourceRoot":"","sources":["use-collapse-animation.ts"],"names":[],"mappings":";AAEA,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EACtE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAC9B,OAAO,EAAE,2BAA2B,QAwFrC"}
1
+ {"version":3,"file":"use-collapse-animation.d.ts","sourceRoot":"","sources":["use-collapse-animation.ts"],"names":[],"mappings":";AAEA,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EACtE,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAC9B,OAAO,EAAE,2BAA2B,GACnC,0BAA0B,CAoI5B"}
@@ -1,14 +1,21 @@
1
- import { useEffect, useRef } from 'react';
1
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
2
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
5
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
+ import { useEffect, useRef, useState } from 'react';
2
8
  /**
3
9
  * 为元素添加折叠/展开动画的 Hook
4
10
  *
5
11
  * @example
6
12
  * ```tsx
7
13
  * const ref = useRef<HTMLDivElement>(null);
8
- * useCollapseAnimation(ref, { isOpen: true });
14
+ * const { shouldHide, shouldKeepOpen } = useCollapseAnimation(ref, { isOpen: true });
9
15
  *
10
- * return <div ref={ref}>Content</div>;
16
+ * return <div ref={ref} className={shouldHide ? 'hide' : ''}>Content</div>;
11
17
  * ```
18
+ * @returns 动画状态对象
12
19
  */
13
20
  export function useCollapseAnimation(elementRef, options) {
14
21
  var isOpen = options.isOpen,
@@ -17,8 +24,17 @@ export function useCollapseAnimation(elementRef, options) {
17
24
  _options$disabled = options.disabled,
18
25
  disabled = _options$disabled === void 0 ? false : _options$disabled,
19
26
  _options$timingFuncti = options.timingFunction,
20
- timingFunction = _options$timingFuncti === void 0 ? 'cubic-bezier(.2,0,0,1)' : _options$timingFuncti;
27
+ timingFunction = _options$timingFuncti === void 0 ? 'cubic-bezier(.2,0,0,1)' : _options$timingFuncti,
28
+ parentOpenClassName = options.parentOpenClassName;
21
29
  var isFirstRenderRef = useRef(true);
30
+ var _useState = useState(!isOpen),
31
+ _useState2 = _slicedToArray(_useState, 2),
32
+ shouldHide = _useState2[0],
33
+ setShouldHide = _useState2[1];
34
+ var _useState3 = useState(false),
35
+ _useState4 = _slicedToArray(_useState3, 2),
36
+ isAnimating = _useState4[0],
37
+ setIsAnimating = _useState4[1];
22
38
 
23
39
  // 当 disabled 状态变化时,重置首次渲染标记
24
40
  useEffect(function () {
@@ -26,6 +42,8 @@ export function useCollapseAnimation(elementRef, options) {
26
42
  isFirstRenderRef.current = true;
27
43
  }
28
44
  }, [disabled]);
45
+
46
+ // 使用 useLayoutEffect 确保动画状态在 DOM 更新前同步设置
29
47
  useEffect(function () {
30
48
  if (!elementRef.current) return;
31
49
  var el = elementRef.current;
@@ -40,9 +58,6 @@ export function useCollapseAnimation(elementRef, options) {
40
58
  }
41
59
  var timer = null;
42
60
 
43
- // 设置 display: block,让元素始终可见,由高度控制折叠
44
- el.style.display = 'block';
45
-
46
61
  // 首次渲染时,直接设置初始状态,不做动画
47
62
  if (isFirstRenderRef.current) {
48
63
  isFirstRenderRef.current = false;
@@ -53,9 +68,13 @@ export function useCollapseAnimation(elementRef, options) {
53
68
  return;
54
69
  }
55
70
  if (isOpen) {
56
- // 展开动画
71
+ // 展开动画 - 先显示元素
72
+ setShouldHide(false);
73
+ setIsAnimating(true);
74
+ el.style.display = 'block';
57
75
  el.style.height = '0px';
58
76
  el.style.overflow = 'hidden';
77
+ el.style.opacity = '0';
59
78
 
60
79
  // 强制重绘
61
80
  void el.offsetHeight;
@@ -65,8 +84,9 @@ export function useCollapseAnimation(elementRef, options) {
65
84
 
66
85
  // 启动动画
67
86
  requestAnimationFrame(function () {
68
- el.style.transition = "height ".concat(duration, "ms ").concat(timingFunction);
87
+ el.style.transition = "height ".concat(duration, "ms ").concat(timingFunction, ", opacity ").concat(duration, "ms ").concat(timingFunction);
69
88
  el.style.height = "".concat(scrollHeight, "px");
89
+ el.style.opacity = '1';
70
90
 
71
91
  // 动画结束后恢复 auto
72
92
  timer = setTimeout(function () {
@@ -74,20 +94,45 @@ export function useCollapseAnimation(elementRef, options) {
74
94
  el.style.height = '';
75
95
  el.style.overflow = '';
76
96
  el.style.transition = '';
97
+ el.style.opacity = '';
98
+ setIsAnimating(false);
77
99
  }
78
100
  }, duration);
79
101
  });
80
102
  } else {
81
103
  // 收起动画
104
+ setIsAnimating(true);
105
+
106
+ // 临时添加父元素的 open class,确保用户依赖此 class 的布局样式在获取高度时生效
107
+ var parentElement = el.parentElement;
108
+ var needTempClass = parentOpenClassName && parentElement;
109
+ if (needTempClass) {
110
+ parentElement.classList.add(parentOpenClassName);
111
+ }
112
+
113
+ // 强制重绘,确保临时 class 生效
114
+ void el.offsetHeight;
82
115
  var _scrollHeight = el.scrollHeight;
83
116
  el.style.height = "".concat(_scrollHeight, "px");
84
117
  el.style.overflow = 'hidden';
118
+ el.style.opacity = '1';
85
119
 
86
120
  // 强制重绘
87
121
  void el.offsetHeight;
88
122
  requestAnimationFrame(function () {
89
- el.style.transition = "height ".concat(duration, "ms ").concat(timingFunction);
123
+ el.style.transition = "height ".concat(duration, "ms ").concat(timingFunction, ", opacity ").concat(duration, "ms ").concat(timingFunction);
90
124
  el.style.height = '0px';
125
+ el.style.opacity = '0';
126
+
127
+ // 动画结束后隐藏元素
128
+ timer = setTimeout(function () {
129
+ // 移除临时 class
130
+ if (needTempClass) {
131
+ parentElement.classList.remove(parentOpenClassName);
132
+ }
133
+ setShouldHide(true);
134
+ setIsAnimating(false);
135
+ }, duration);
91
136
  });
92
137
  }
93
138
 
@@ -98,4 +143,14 @@ export function useCollapseAnimation(elementRef, options) {
98
143
  }
99
144
  };
100
145
  }, [isOpen, disabled, duration, timingFunction]);
146
+ if (disabled) {
147
+ return {
148
+ shouldHide: !isOpen,
149
+ shouldKeepOpen: false
150
+ };
151
+ }
152
+ return {
153
+ shouldHide: shouldHide,
154
+ shouldKeepOpen: isAnimating && !isOpen
155
+ };
101
156
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-popup.d.ts","sourceRoot":"","sources":["use-popup.ts"],"names":[],"mappings":"AAAA,OAAO,KAA4E,MAAM,OAAO,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAOhE,QAAA,MAAM,QAAQ,UAAW,cAAc;;;;;;0BAqHK;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;0BAa9B;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;;;;;qBAnC7B;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAvEzC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;8BAOvC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;2BAyJxD,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;6BAMxC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;;CA8BlE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"use-popup.d.ts","sourceRoot":"","sources":["use-popup.ts"],"names":[],"mappings":"AAAA,OAAO,KAA4E,MAAM,OAAO,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAOhE,QAAA,MAAM,QAAQ,UAAW,cAAc;;;;;;0BAsHK;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;0BAa9B;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;;;;;qBAnC7B;YAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAAA;SAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAxEzC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;8BAOvC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;2BA0JxD,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;6BAMxC,MAAM,gBAAgB,CAAC,WAAW,GAAG,IAAI,CAAC;;CA8BlE,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -57,9 +57,10 @@ var usePopup = function usePopup(props) {
57
57
  var changeOpen = function changeOpen(openIn, delay) {
58
58
  if (context.triggerTimer) clearTimeout(context.triggerTimer);
59
59
  context.triggerTimer = setTimeout(function () {
60
- var _props$onCollapse;
61
- if (open === openIn) return;
62
- (_props$onCollapse = props.onCollapse) === null || _props$onCollapse === void 0 || _props$onCollapse.call(props, openIn);
60
+ if (open !== openIn) {
61
+ var _props$onCollapse;
62
+ (_props$onCollapse = props.onCollapse) === null || _props$onCollapse === void 0 || _props$onCollapse.call(props, openIn);
63
+ }
63
64
  if (props.open === undefined) {
64
65
  setOpenState(openIn);
65
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sheinx/hooks",
3
- "version": "3.9.2-beta.1",
3
+ "version": "3.9.2-beta.3",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "license": "MIT",