@lobehub/ui 2.16.1 → 2.16.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.
@@ -1,12 +1,7 @@
1
1
  'use client';
2
2
 
3
+ var _excluded = ["body", "className", "classNames", "defaultExpand", "defaultWidth", "expand", "expandable", "footer", "header", "maxWidth", "minWidth", "onExpandChange", "onWidthChange", "onWidthDragging", "placement", "resizable", "showBorder", "showHandle", "showHandleWhenCollapsed", "showHandleHighlight", "backgroundColor", "styles", "width"];
3
4
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4
- var _excluded = ["animation", "children", "className", "classNames", "defaultExpand", "defaultWidth", "expand", "expandable", "footer", "header", "maxWidth", "minWidth", "onExpandChange", "onWidthChange", "onWidthDragging", "placement", "resizable", "showBorder", "showHandle", "showHandleWhenCollapsed", "showHandleHighlight", "backgroundColor", "styles", "width"];
5
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
- function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
9
- function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10
5
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
11
6
  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."); }
12
7
  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); }
@@ -15,11 +10,15 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
15
10
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
16
11
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
17
12
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
13
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
16
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
17
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
18
18
  import { useHover } from 'ahooks';
19
- import { AnimatePresence, motion } from 'framer-motion';
20
19
  import { ChevronLeft, ChevronRight } from 'lucide-react';
21
20
  import { Resizable } from 're-resizable';
22
- import { memo, useEffect, useMemo, useRef, useState } from 'react';
21
+ import { memo, useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
23
22
  import { Center, Flexbox } from 'react-layout-kit';
24
23
  import useControlledState from 'use-merge-value';
25
24
  import Icon from "../Icon";
@@ -29,94 +28,120 @@ import { jsxs as _jsxs } from "react/jsx-runtime";
29
28
  var DEFAULT_MIN_WIDTH = 64; // 最小宽度即折叠宽度
30
29
  var DEFAULT_EXPAND = true;
31
30
  var DEFAULT_EXPANDED_WIDTH = 280;
31
+ var ANIMATION_DURATION = 300;
32
+ var COLLAPSE_ANIMATION_DELAY = 200;
32
33
 
33
- // Animation variants generator based on fade and blur settings
34
- var getAnimationVariants = function getAnimationVariants(fade, blur) {
35
- var enterProps = {
36
- transition: {
37
- duration: 0.3,
38
- ease: [0.22, 1, 0.36, 1]
39
- }
40
- };
41
- var exitProps = {
42
- transition: {
43
- duration: 0.2,
44
- ease: [0.4, 0, 1, 1]
45
- }
46
- };
47
- if (fade) {
48
- enterProps.opacity = 1;
49
- exitProps.opacity = 0;
50
- }
51
- if (blur) {
52
- enterProps.filter = 'blur(0px)';
53
- exitProps.filter = 'blur(4px)';
54
- }
55
- return {
56
- enter: enterProps,
57
- exit: exitProps
58
- };
34
+ // Pre-define static objects to avoid recreating
35
+ var RESIZE_DISABLED = {
36
+ bottom: false,
37
+ bottomLeft: false,
38
+ bottomRight: false,
39
+ left: false,
40
+ right: false,
41
+ top: false,
42
+ topLeft: false,
43
+ topRight: false
59
44
  };
60
45
 
61
- // Wrapper component for content animation
62
- var AnimationWrapper = /*#__PURE__*/memo(function (_ref) {
63
- var blur = _ref.blur,
64
- children = _ref.children,
65
- enabled = _ref.enabled,
46
+ // State reducer for better state management
47
+
48
+ function sideNavReducer(state, action) {
49
+ switch (action.type) {
50
+ case 'START_RESIZE':
51
+ {
52
+ return _objectSpread(_objectSpread({}, state), {}, {
53
+ isResizing: true
54
+ });
55
+ }
56
+ case 'STOP_RESIZE':
57
+ {
58
+ return _objectSpread(_objectSpread({}, state), {}, {
59
+ isResizing: false
60
+ });
61
+ }
62
+ case 'START_ANIMATION':
63
+ {
64
+ return _objectSpread(_objectSpread({}, state), {}, {
65
+ isAnimating: true
66
+ });
67
+ }
68
+ case 'STOP_ANIMATION':
69
+ {
70
+ return _objectSpread(_objectSpread({}, state), {}, {
71
+ isAnimating: false
72
+ });
73
+ }
74
+ case 'SET_WIDTH':
75
+ {
76
+ return _objectSpread(_objectSpread({}, state), {}, {
77
+ internalWidth: action.payload
78
+ });
79
+ }
80
+ case 'SET_EXPANDED_WIDTH':
81
+ {
82
+ return _objectSpread(_objectSpread({}, state), {}, {
83
+ expandedWidth: action.payload
84
+ });
85
+ }
86
+ case 'SET_RENDER_EXPAND':
87
+ {
88
+ return _objectSpread(_objectSpread({}, state), {}, {
89
+ renderExpand: action.payload
90
+ });
91
+ }
92
+ case 'ANIMATE_EXPAND':
93
+ {
94
+ return _objectSpread(_objectSpread({}, state), {}, {
95
+ internalWidth: action.payload,
96
+ renderExpand: true
97
+ });
98
+ }
99
+ case 'ANIMATE_COLLAPSE':
100
+ {
101
+ return _objectSpread(_objectSpread({}, state), {}, {
102
+ internalWidth: action.payload
103
+ });
104
+ }
105
+ default:
106
+ {
107
+ return state;
108
+ }
109
+ }
110
+ }
111
+ var DraggableSideNav = /*#__PURE__*/memo(function (_ref) {
112
+ var body = _ref.body,
113
+ className = _ref.className,
114
+ classNames = _ref.classNames,
115
+ _ref$defaultExpand = _ref.defaultExpand,
116
+ defaultExpand = _ref$defaultExpand === void 0 ? DEFAULT_EXPAND : _ref$defaultExpand,
117
+ defaultWidth = _ref.defaultWidth,
66
118
  expand = _ref.expand,
67
- fade = _ref.fade,
68
- id = _ref.id;
69
- if (!enabled) return children;
70
- var variants = getAnimationVariants(fade, blur);
71
- return /*#__PURE__*/_jsx(AnimatePresence, {
72
- initial: false,
73
- mode: "wait",
74
- children: /*#__PURE__*/_jsx(motion.div, {
75
- animate: "enter",
76
- exit: "exit",
77
- initial: "exit",
78
- variants: variants,
79
- children: children
80
- }, "".concat(id, "-").concat(expand ? 'expanded' : 'collapsed'))
81
- });
82
- });
83
- AnimationWrapper.displayName = 'AnimationWrapper';
84
- var DraggableSideNav = /*#__PURE__*/memo(function (_ref2) {
85
- var _animation$blur, _animation$fade, _animation$blur2, _animation$fade2, _animation$blur3, _animation$fade3;
86
- var animation = _ref2.animation,
87
- children = _ref2.children,
88
- className = _ref2.className,
89
- classNames = _ref2.classNames,
90
- _ref2$defaultExpand = _ref2.defaultExpand,
91
- defaultExpand = _ref2$defaultExpand === void 0 ? DEFAULT_EXPAND : _ref2$defaultExpand,
92
- defaultWidth = _ref2.defaultWidth,
93
- expand = _ref2.expand,
94
- _ref2$expandable = _ref2.expandable,
95
- expandable = _ref2$expandable === void 0 ? true : _ref2$expandable,
96
- footer = _ref2.footer,
97
- header = _ref2.header,
98
- maxWidth = _ref2.maxWidth,
99
- _ref2$minWidth = _ref2.minWidth,
100
- minWidth = _ref2$minWidth === void 0 ? DEFAULT_MIN_WIDTH : _ref2$minWidth,
101
- onExpandChange = _ref2.onExpandChange,
102
- onWidthChange = _ref2.onWidthChange,
103
- onWidthDragging = _ref2.onWidthDragging,
104
- _ref2$placement = _ref2.placement,
105
- placement = _ref2$placement === void 0 ? 'left' : _ref2$placement,
106
- _ref2$resizable = _ref2.resizable,
107
- resizable = _ref2$resizable === void 0 ? true : _ref2$resizable,
108
- _ref2$showBorder = _ref2.showBorder,
109
- showBorder = _ref2$showBorder === void 0 ? true : _ref2$showBorder,
110
- _ref2$showHandle = _ref2.showHandle,
111
- showHandle = _ref2$showHandle === void 0 ? true : _ref2$showHandle,
112
- _ref2$showHandleWhenC = _ref2.showHandleWhenCollapsed,
113
- showHandleWhenCollapsed = _ref2$showHandleWhenC === void 0 ? false : _ref2$showHandleWhenC,
114
- _ref2$showHandleHighl = _ref2.showHandleHighlight,
115
- showHandleHighlight = _ref2$showHandleHighl === void 0 ? false : _ref2$showHandleHighl,
116
- backgroundColor = _ref2.backgroundColor,
117
- customStyles = _ref2.styles,
118
- width = _ref2.width,
119
- rest = _objectWithoutProperties(_ref2, _excluded);
119
+ _ref$expandable = _ref.expandable,
120
+ expandable = _ref$expandable === void 0 ? true : _ref$expandable,
121
+ footer = _ref.footer,
122
+ header = _ref.header,
123
+ maxWidth = _ref.maxWidth,
124
+ _ref$minWidth = _ref.minWidth,
125
+ minWidth = _ref$minWidth === void 0 ? DEFAULT_MIN_WIDTH : _ref$minWidth,
126
+ onExpandChange = _ref.onExpandChange,
127
+ onWidthChange = _ref.onWidthChange,
128
+ onWidthDragging = _ref.onWidthDragging,
129
+ _ref$placement = _ref.placement,
130
+ placement = _ref$placement === void 0 ? 'left' : _ref$placement,
131
+ _ref$resizable = _ref.resizable,
132
+ resizable = _ref$resizable === void 0 ? true : _ref$resizable,
133
+ _ref$showBorder = _ref.showBorder,
134
+ showBorder = _ref$showBorder === void 0 ? true : _ref$showBorder,
135
+ _ref$showHandle = _ref.showHandle,
136
+ showHandle = _ref$showHandle === void 0 ? true : _ref$showHandle,
137
+ _ref$showHandleWhenCo = _ref.showHandleWhenCollapsed,
138
+ showHandleWhenCollapsed = _ref$showHandleWhenCo === void 0 ? false : _ref$showHandleWhenCo,
139
+ _ref$showHandleHighli = _ref.showHandleHighlight,
140
+ showHandleHighlight = _ref$showHandleHighli === void 0 ? false : _ref$showHandleHighli,
141
+ backgroundColor = _ref.backgroundColor,
142
+ customStyles = _ref.styles,
143
+ width = _ref.width,
144
+ rest = _objectWithoutProperties(_ref, _excluded);
120
145
  var _useStyles = useStyles({
121
146
  backgroundColor: backgroundColor,
122
147
  showBorder: showBorder
@@ -134,52 +159,59 @@ var DraggableSideNav = /*#__PURE__*/memo(function (_ref2) {
134
159
  _useControlledState2 = _slicedToArray(_useControlledState, 2),
135
160
  isExpand = _useControlledState2[0],
136
161
  setIsExpand = _useControlledState2[1];
137
- var _useState = useState(false),
138
- _useState2 = _slicedToArray(_useState, 2),
139
- isResizing = _useState2[0],
140
- setIsResizing = _useState2[1];
141
- var _useState3 = useState(false),
142
- _useState4 = _slicedToArray(_useState3, 2),
143
- isAnimating = _useState4[0],
144
- setIsAnimating = _useState4[1];
145
-
146
- // Compute default expanded width
147
- var computedDefaultExpandedWidth = defaultWidth || DEFAULT_EXPANDED_WIDTH;
148
-
149
- // 内部宽度状态,用于平滑动画
150
- var _useState5 = useState(isExpand ? width !== null && width !== void 0 ? width : computedDefaultExpandedWidth : minWidth),
151
- _useState6 = _slicedToArray(_useState5, 2),
152
- internalWidth = _useState6[0],
153
- setInternalWidth = _useState6[1];
154
-
155
- // 记住展开时的宽度
156
- var _useState7 = useState(width !== null && width !== void 0 ? width : computedDefaultExpandedWidth),
157
- _useState8 = _slicedToArray(_useState7, 2),
158
- expandedWidth = _useState8[0],
159
- setExpandedWidth = _useState8[1];
160
-
161
- // 用于渲染的 expand 状态 - 延迟切换以匹配动画时机
162
- var _useState9 = useState(isExpand),
163
- _useState10 = _slicedToArray(_useState9, 2),
164
- renderExpand = _useState10[0],
165
- setRenderExpand = _useState10[1];
162
+
163
+ // Use refs for animation timeouts to avoid memory leaks
164
+ var animationTimeoutRef = useRef(undefined);
165
+ var collapseTimeoutRef = useRef(undefined);
166
+
167
+ // Compute default expanded width - memoize to avoid recalculation
168
+ var computedDefaultExpandedWidth = useMemo(function () {
169
+ return defaultWidth || DEFAULT_EXPANDED_WIDTH;
170
+ }, [defaultWidth]);
171
+
172
+ // Initialize state with useReducer for better performance
173
+ var initialState = {
174
+ expandedWidth: width !== null && width !== void 0 ? width : computedDefaultExpandedWidth,
175
+ internalWidth: isExpand ? width !== null && width !== void 0 ? width : computedDefaultExpandedWidth : minWidth,
176
+ isAnimating: false,
177
+ isResizing: false,
178
+ renderExpand: isExpand
179
+ };
180
+ var _useReducer = useReducer(sideNavReducer, initialState),
181
+ _useReducer2 = _slicedToArray(_useReducer, 2),
182
+ state = _useReducer2[0],
183
+ dispatch = _useReducer2[1];
166
184
 
167
185
  // 计算折叠阈值:展开最小宽度和折叠宽度的中间值
168
186
  var collapseThreshold = useMemo(function () {
169
- return minWidth + (expandedWidth - minWidth) / 3;
170
- }, [minWidth, expandedWidth]);
187
+ return minWidth + (state.expandedWidth - minWidth) / 3;
188
+ }, [minWidth, state.expandedWidth]);
171
189
 
172
190
  // Toggle expand state with smooth animation
173
- var toggleExpand = function toggleExpand() {
191
+ var toggleExpand = useCallback(function () {
174
192
  if (!expandable) return;
175
- setIsAnimating(true);
193
+
194
+ // 在动画或拖拽期间阻止新的切换操作,避免状态混乱
195
+ if (state.isAnimating || state.isResizing) return;
196
+
197
+ // 清除之前的动画
198
+ if (animationTimeoutRef.current) {
199
+ clearTimeout(animationTimeoutRef.current);
200
+ }
201
+
202
+ // 立即设置动画状态,避免其他 useEffect 干扰
203
+ dispatch({
204
+ type: 'START_ANIMATION'
205
+ });
176
206
  setIsExpand(!isExpand);
177
207
 
178
208
  // 动画完成后重置状态 - 与宽度动画时长一致
179
- setTimeout(function () {
180
- setIsAnimating(false);
181
- }, 400);
182
- };
209
+ animationTimeoutRef.current = setTimeout(function () {
210
+ dispatch({
211
+ type: 'STOP_ANIMATION'
212
+ });
213
+ }, ANIMATION_DURATION);
214
+ }, [expandable, isExpand, setIsExpand, state.isAnimating, state.isResizing]);
183
215
 
184
216
  // 用于跟踪上一次的 expand 状态,以检测外部变化
185
217
  var prevExpandRef = useRef(isExpand);
@@ -187,123 +219,189 @@ var DraggableSideNav = /*#__PURE__*/memo(function (_ref2) {
187
219
  // 监听外部 expand prop 变化,触发动画
188
220
  useEffect(function () {
189
221
  // 检测到 expand 状态变化,且不在拖拽和动画中
190
- if (prevExpandRef.current !== isExpand && !isResizing && !isAnimating) {
191
- setIsAnimating(true);
192
- setTimeout(function () {
193
- setIsAnimating(false);
194
- }, 400);
222
+ if (prevExpandRef.current !== isExpand && !state.isResizing && !state.isAnimating) {
223
+ if (animationTimeoutRef.current) {
224
+ clearTimeout(animationTimeoutRef.current);
225
+ }
226
+
227
+ // 立即设置动画状态,避免其他 useEffect 干扰
228
+ dispatch({
229
+ type: 'START_ANIMATION'
230
+ });
231
+ animationTimeoutRef.current = setTimeout(function () {
232
+ dispatch({
233
+ type: 'STOP_ANIMATION'
234
+ });
235
+ }, ANIMATION_DURATION);
236
+ prevExpandRef.current = isExpand;
195
237
  }
196
- prevExpandRef.current = isExpand;
197
- }, [isExpand, isResizing, isAnimating]);
238
+ }, [isExpand, state.isResizing, state.isAnimating]);
198
239
 
199
240
  // 处理展开/折叠状态变化时的宽度动画和内容切换时机
200
241
  useEffect(function () {
201
- if (isAnimating) {
242
+ if (state.isAnimating) {
202
243
  // 使用 requestAnimationFrame 确保动画平滑
203
- requestAnimationFrame(function () {
244
+ var rafId = requestAnimationFrame(function () {
204
245
  if (isExpand) {
205
246
  // 展开动画:立即切换内容(先切换内容,再开始宽度动画)
206
- setRenderExpand(true);
207
- setInternalWidth(expandedWidth);
247
+ dispatch({
248
+ payload: state.expandedWidth,
249
+ type: 'ANIMATE_EXPAND'
250
+ });
208
251
  } else {
209
252
  // 折叠动画:延迟切换内容,在动画接近结束时才切换(300ms,略早于动画结束)
210
- setInternalWidth(minWidth);
211
- setTimeout(function () {
212
- setRenderExpand(false);
213
- }, 300);
253
+ dispatch({
254
+ payload: minWidth,
255
+ type: 'ANIMATE_COLLAPSE'
256
+ });
257
+ if (collapseTimeoutRef.current) {
258
+ clearTimeout(collapseTimeoutRef.current);
259
+ }
260
+ collapseTimeoutRef.current = setTimeout(function () {
261
+ dispatch({
262
+ payload: false,
263
+ type: 'SET_RENDER_EXPAND'
264
+ });
265
+ }, COLLAPSE_ANIMATION_DELAY);
214
266
  }
215
267
  });
268
+ return function () {
269
+ cancelAnimationFrame(rafId);
270
+ };
216
271
  }
217
- }, [isExpand, isAnimating, minWidth, expandedWidth]);
272
+ }, [isExpand, state.isAnimating, minWidth, state.expandedWidth]);
218
273
 
219
274
  // 同步非动画期间的 renderExpand 状态(如拖拽)
275
+ // 使用 ref 追踪上一次的 isResizing 状态,只在拖拽结束时同步
276
+ var prevIsResizingRef = useRef(state.isResizing);
220
277
  useEffect(function () {
221
- if (!isAnimating && !isResizing) {
222
- setRenderExpand(isExpand);
278
+ var wasResizing = prevIsResizingRef.current;
279
+ prevIsResizingRef.current = state.isResizing;
280
+
281
+ // 只在拖拽刚结束时同步 renderExpand,避免干扰正常的展开/折叠动画
282
+ if (wasResizing && !state.isResizing && !state.isAnimating) {
283
+ dispatch({
284
+ payload: isExpand,
285
+ type: 'SET_RENDER_EXPAND'
286
+ });
223
287
  }
224
- }, [isExpand, isAnimating, isResizing]);
288
+ }, [isExpand, state.isAnimating, state.isResizing]);
225
289
 
226
290
  // 处理外部 width prop 变化
227
291
  // width 表示展开时的宽度,实际显示宽度根据 isExpand 状态决定
228
292
  useEffect(function () {
229
- if (width !== undefined && !isResizing && !isAnimating) {
293
+ if (width !== undefined && !state.isResizing && !state.isAnimating) {
230
294
  // 更新展开宽度记录
231
- setExpandedWidth(width);
295
+ dispatch({
296
+ payload: width,
297
+ type: 'SET_EXPANDED_WIDTH'
298
+ });
232
299
  // 根据当前状态设置实际宽度
233
300
  if (isExpand) {
234
- setInternalWidth(width);
301
+ dispatch({
302
+ payload: width,
303
+ type: 'SET_WIDTH'
304
+ });
235
305
  }
236
306
  // 如果是折叠状态,保持 minWidth,不改变 internalWidth
237
307
  }
238
- }, [width, isResizing, isAnimating, isExpand]);
308
+ }, [width, state.isResizing, state.isAnimating, isExpand]);
239
309
 
240
- // 计算当前的 children(支持函数和静态值)- 使用 renderExpand
241
- var currentChildren = useMemo(function () {
242
- return typeof children === 'function' ? children(renderExpand) : children;
243
- }, [children, renderExpand]);
310
+ // 计算当前的 body 内容 - 使用 renderExpand
311
+ var currentBody = useMemo(function () {
312
+ return body(state.renderExpand);
313
+ }, [body, state.renderExpand]);
244
314
 
245
315
  // 计算当前的 header(支持函数和静态值)- 使用 renderExpand
246
316
  var currentHeader = useMemo(function () {
247
- return typeof header === 'function' ? header(renderExpand) : header;
248
- }, [header, renderExpand]);
317
+ return typeof header === 'function' ? header(state.renderExpand) : header;
318
+ }, [header, state.renderExpand]);
249
319
 
250
320
  // 计算当前的 footer(支持函数和静态值)- 使用 renderExpand
251
321
  var currentFooter = useMemo(function () {
252
- return typeof footer === 'function' ? footer(renderExpand) : footer;
253
- }, [footer, renderExpand]);
322
+ return typeof footer === 'function' ? footer(state.renderExpand) : footer;
323
+ }, [footer, state.renderExpand]);
254
324
 
255
- // Handle resize
256
- var handleResize = function handleResize(_, _direction, reference_, delta) {
257
- var currentWidth = reference_.offsetWidth;
258
- setInternalWidth(currentWidth);
325
+ // Handle resize - memoize to prevent recreating on every render
326
+ var handleResize = useCallback(function (_, __, ref, delta) {
327
+ var currentWidth = ref.offsetWidth;
328
+ dispatch({
329
+ payload: currentWidth,
330
+ type: 'SET_WIDTH'
331
+ });
259
332
  onWidthDragging === null || onWidthDragging === void 0 || onWidthDragging(delta, currentWidth);
260
- };
261
- var handleResizeStart = function handleResizeStart() {
262
- setIsResizing(true);
263
- };
264
- var handleResizeStop = function handleResizeStop(_, _direction, reference_, delta) {
265
- setIsResizing(false);
266
- var currentWidth = reference_.offsetWidth;
333
+ }, [onWidthDragging]);
334
+ var handleResizeStart = useCallback(function () {
335
+ dispatch({
336
+ type: 'START_RESIZE'
337
+ });
338
+ }, []);
339
+ var handleResizeStop = useCallback(function (_, __, ref, delta) {
340
+ dispatch({
341
+ type: 'STOP_RESIZE'
342
+ });
343
+ var currentWidth = ref.offsetWidth;
344
+
345
+ // 清除之前的动画
346
+ if (animationTimeoutRef.current) {
347
+ clearTimeout(animationTimeoutRef.current);
348
+ }
267
349
 
268
350
  // 根据拖拽后的宽度决定是折叠还是展开
269
351
  if (expandable) {
270
- if (currentWidth <= minWidth) {
271
- // 拖拽到最小宽度,保持折叠
272
- setIsAnimating(true);
273
- setIsExpand(false);
274
- setInternalWidth(minWidth);
275
- setTimeout(function () {
276
- return setIsAnimating(false);
277
- }, 400);
278
- } else if (currentWidth < collapseThreshold) {
279
- // 拖拽到阈值以下,自动折叠
280
- setIsAnimating(true);
281
- setIsExpand(false);
282
- setInternalWidth(minWidth);
283
- setTimeout(function () {
284
- return setIsAnimating(false);
285
- }, 400);
286
- } else if (!isExpand && currentWidth > minWidth) {
287
- // 从折叠状态拖拽出来,自动展开
288
- setIsAnimating(true);
289
- setIsExpand(true);
290
- setExpandedWidth(currentWidth);
291
- setInternalWidth(currentWidth);
292
- setTimeout(function () {
293
- return setIsAnimating(false);
294
- }, 400);
352
+ var shouldCollapse = currentWidth <= minWidth || currentWidth < collapseThreshold;
353
+ var shouldExpand = !isExpand && currentWidth > minWidth && currentWidth >= collapseThreshold;
354
+ if (shouldCollapse || shouldExpand) {
355
+ // 立即设置动画状态
356
+ dispatch({
357
+ type: 'START_ANIMATION'
358
+ });
359
+ if (shouldCollapse) {
360
+ setIsExpand(false);
361
+ dispatch({
362
+ payload: minWidth,
363
+ type: 'SET_WIDTH'
364
+ });
365
+ } else {
366
+ setIsExpand(true);
367
+ dispatch({
368
+ payload: currentWidth,
369
+ type: 'SET_EXPANDED_WIDTH'
370
+ });
371
+ dispatch({
372
+ payload: currentWidth,
373
+ type: 'SET_WIDTH'
374
+ });
375
+ }
376
+ animationTimeoutRef.current = setTimeout(function () {
377
+ dispatch({
378
+ type: 'STOP_ANIMATION'
379
+ });
380
+ }, ANIMATION_DURATION);
295
381
  } else if (isExpand) {
296
382
  // 展开状态下正常拖拽,记住宽度
297
- setExpandedWidth(currentWidth);
298
- setInternalWidth(currentWidth);
383
+ dispatch({
384
+ payload: currentWidth,
385
+ type: 'SET_EXPANDED_WIDTH'
386
+ });
387
+ dispatch({
388
+ payload: currentWidth,
389
+ type: 'SET_WIDTH'
390
+ });
299
391
  }
300
392
  } else {
301
393
  // 如果不可折叠,仅更新宽度
302
- setExpandedWidth(currentWidth);
303
- setInternalWidth(currentWidth);
394
+ dispatch({
395
+ payload: currentWidth,
396
+ type: 'SET_EXPANDED_WIDTH'
397
+ });
398
+ dispatch({
399
+ payload: currentWidth,
400
+ type: 'SET_WIDTH'
401
+ });
304
402
  }
305
403
  onWidthChange === null || onWidthChange === void 0 || onWidthChange(delta, currentWidth);
306
- };
404
+ }, [expandable, minWidth, collapseThreshold, isExpand, onWidthChange, setIsExpand]);
307
405
 
308
406
  // Arrow icon based on placement and expand state
309
407
  var ArrowIcon = useMemo(function () {
@@ -315,47 +413,48 @@ var DraggableSideNav = /*#__PURE__*/memo(function (_ref2) {
315
413
  return ChevronRight;
316
414
  }, [placement]);
317
415
 
318
- // Toggle handle with smooth transitions
319
- var handle = showHandle && expandable && /*#__PURE__*/_jsx(motion.div, {
320
- animate: {
416
+ // Memoize handle styles to prevent recreation
417
+ var handleRootStyle = useMemo(function () {
418
+ return {
419
+ display: 'flex',
321
420
  opacity: !isExpand && showHandleWhenCollapsed ? 1 : isHovering ? 1 : 0,
322
- scale: isHovering ? 1.05 : 1
323
- },
324
- className: cx(styles.toggleRoot, placement === 'left' ? styles.toggleLeft : styles.toggleRight),
325
- style: {
326
- display: 'flex'
327
- },
328
- transition: {
329
- duration: 0.25,
330
- ease: [0.22, 1, 0.36, 1]
331
- },
332
- children: /*#__PURE__*/_jsx(Center, {
333
- className: classNames === null || classNames === void 0 ? void 0 : classNames.handle,
334
- onClick: toggleExpand,
335
- style: _objectSpread(_objectSpread({}, customStyles === null || customStyles === void 0 ? void 0 : customStyles.handle), {}, {
336
- cursor: 'pointer',
337
- transition: 'transform 0.2s ease-out'
338
- }),
339
- children: /*#__PURE__*/_jsx(motion.div, {
340
- animate: {
341
- rotate: isExpand ? 0 : 180
342
- },
343
- style: {
344
- marginLeft: placement === 'right' ? 4 : 0,
345
- marginRight: placement === 'left' ? 4 : 0
346
- },
347
- transition: {
348
- duration: 0.3,
349
- ease: [0.22, 1, 0.36, 1]
350
- },
351
- children: /*#__PURE__*/_jsx(Icon, {
352
- className: styles.handlerIcon,
353
- icon: ArrowIcon,
354
- size: 16
421
+ transition: 'opacity 0.25s ease'
422
+ };
423
+ }, [isExpand, showHandleWhenCollapsed, isHovering]);
424
+ var handleCenterStyle = useMemo(function () {
425
+ return _objectSpread(_objectSpread({}, customStyles === null || customStyles === void 0 ? void 0 : customStyles.handle), {}, {
426
+ cursor: 'pointer'
427
+ });
428
+ }, [customStyles === null || customStyles === void 0 ? void 0 : customStyles.handle]);
429
+ var handleIconWrapperStyle = useMemo(function () {
430
+ return {
431
+ marginLeft: placement === 'right' ? 4 : 0,
432
+ marginRight: placement === 'left' ? 4 : 0,
433
+ transform: isExpand ? 'rotate(0deg)' : 'rotate(180deg)',
434
+ transition: "transform ".concat(COLLAPSE_ANIMATION_DELAY, " ease")
435
+ };
436
+ }, [placement, isExpand]);
437
+
438
+ // Toggle handle with smooth transitions
439
+ var handle = useMemo(function () {
440
+ return showHandle && expandable && /*#__PURE__*/_jsx("div", {
441
+ className: cx(styles.toggleRoot, placement === 'left' ? styles.toggleLeft : styles.toggleRight),
442
+ style: handleRootStyle,
443
+ children: /*#__PURE__*/_jsx(Center, {
444
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.handle,
445
+ onClick: toggleExpand,
446
+ style: handleCenterStyle,
447
+ children: /*#__PURE__*/_jsx("div", {
448
+ style: handleIconWrapperStyle,
449
+ children: /*#__PURE__*/_jsx(Icon, {
450
+ className: styles.handlerIcon,
451
+ icon: ArrowIcon,
452
+ size: 16
453
+ })
355
454
  })
356
455
  })
357
- })
358
- });
456
+ });
457
+ }, [showHandle, expandable, styles.toggleRoot, styles.toggleLeft, styles.toggleRight, styles.handlerIcon, placement, handleRootStyle, classNames === null || classNames === void 0 ? void 0 : classNames.handle, toggleExpand, handleCenterStyle, handleIconWrapperStyle, ArrowIcon, cx]);
359
458
 
360
459
  // Size configuration - 使用内部宽度状态
361
460
  var sizeConfig = useMemo(function () {
@@ -364,24 +463,15 @@ var DraggableSideNav = /*#__PURE__*/memo(function (_ref2) {
364
463
  minWidth: minWidth,
365
464
  size: {
366
465
  height: '100%',
367
- width: internalWidth
466
+ width: state.internalWidth
368
467
  }
369
468
  };
370
- }, [internalWidth, minWidth, maxWidth]);
469
+ }, [state.internalWidth, minWidth, maxWidth]);
371
470
 
372
471
  // Resize enable configuration - 始终允许拖拽
373
472
  var resizeEnable = useMemo(function () {
374
473
  if (!resizable) {
375
- return {
376
- bottom: false,
377
- bottomLeft: false,
378
- bottomRight: false,
379
- left: false,
380
- right: false,
381
- top: false,
382
- topLeft: false,
383
- topRight: false
384
- };
474
+ return RESIZE_DISABLED;
385
475
  }
386
476
  return {
387
477
  bottom: false,
@@ -394,55 +484,73 @@ var DraggableSideNav = /*#__PURE__*/memo(function (_ref2) {
394
484
  topRight: false
395
485
  };
396
486
  }, [resizable, placement]);
487
+
488
+ // Memoize handle classes to prevent recreation
489
+ var handleClasses = useMemo(function () {
490
+ return _defineProperty({}, placement === 'left' ? 'right' : 'left', cx(styles.resizeHandle, showHandleHighlight && styles.resizeHandleHighlight, placement === 'left' ? styles.resizeHandleLeft : styles.resizeHandleRight));
491
+ }, [placement, styles, showHandleHighlight, cx]);
492
+
493
+ // Memoize container style to prevent recreation
494
+ var containerStyle = useMemo(function () {
495
+ return _objectSpread(_objectSpread(_objectSpread({}, customStyles === null || customStyles === void 0 ? void 0 : customStyles.container), rest.style), {}, {
496
+ // 拖拽时不要动画,点击 handle 时有流畅的弹性动画
497
+ transition: state.isResizing ? 'none' : state.isAnimating ? "width ".concat(ANIMATION_DURATION, "ms cubic-bezier(0.22, 1, 0.36, 1)") : 'none'
498
+ });
499
+ }, [customStyles === null || customStyles === void 0 ? void 0 : customStyles.container, rest.style, state.isResizing, state.isAnimating]);
500
+
501
+ // Memoize class names
502
+ var containerClassName = useMemo(function () {
503
+ return cx(styles.container, classNames === null || classNames === void 0 ? void 0 : classNames.container, className);
504
+ }, [cx, styles.container, classNames === null || classNames === void 0 ? void 0 : classNames.container, className]);
505
+ var contentClassName = useMemo(function () {
506
+ return cx(styles.contentContainer, styles.menuOverride, classNames === null || classNames === void 0 ? void 0 : classNames.content);
507
+ }, [cx, styles.contentContainer, styles.menuOverride, classNames === null || classNames === void 0 ? void 0 : classNames.content]);
508
+ var headerClassName = useMemo(function () {
509
+ return cx(styles.header, classNames === null || classNames === void 0 ? void 0 : classNames.header);
510
+ }, [cx, styles.header, classNames === null || classNames === void 0 ? void 0 : classNames.header]);
511
+ var bodyClassName = useMemo(function () {
512
+ return cx(styles.body, classNames === null || classNames === void 0 ? void 0 : classNames.body);
513
+ }, [cx, styles.body, classNames === null || classNames === void 0 ? void 0 : classNames.body]);
514
+ var footerClassName = useMemo(function () {
515
+ return cx(styles.footer, classNames === null || classNames === void 0 ? void 0 : classNames.footer);
516
+ }, [cx, styles.footer, classNames === null || classNames === void 0 ? void 0 : classNames.footer]);
517
+
518
+ // Cleanup timeouts on unmount
519
+ useEffect(function () {
520
+ return function () {
521
+ if (animationTimeoutRef.current) {
522
+ clearTimeout(animationTimeoutRef.current);
523
+ }
524
+ if (collapseTimeoutRef.current) {
525
+ clearTimeout(collapseTimeoutRef.current);
526
+ }
527
+ };
528
+ }, []);
397
529
  return /*#__PURE__*/_jsx("aside", {
398
530
  ref: ref,
399
531
  children: /*#__PURE__*/_jsxs(Resizable, _objectSpread(_objectSpread({}, sizeConfig), {}, {
400
- className: cx(styles.container, classNames === null || classNames === void 0 ? void 0 : classNames.container, className),
532
+ className: containerClassName,
401
533
  enable: resizeEnable,
402
- handleClasses: _defineProperty({}, placement === 'left' ? 'right' : 'left', cx(styles.resizeHandle, showHandleHighlight && styles.resizeHandleHighlight, placement === 'left' ? styles.resizeHandleLeft : styles.resizeHandleRight)),
534
+ handleClasses: handleClasses,
403
535
  onResize: handleResize,
404
536
  onResizeStart: handleResizeStart,
405
537
  onResizeStop: handleResizeStop,
406
- style: _objectSpread(_objectSpread(_objectSpread({}, customStyles === null || customStyles === void 0 ? void 0 : customStyles.container), rest.style), {}, {
407
- // 拖拽时不要动画,点击 handle 时有流畅的弹性动画
408
- transition: isResizing ? 'none' : isAnimating ? 'width 0.4s cubic-bezier(0.22, 1, 0.36, 1)' : 'none'
409
- }),
538
+ style: containerStyle,
410
539
  children: [handle, /*#__PURE__*/_jsxs(Flexbox, {
411
- className: cx(styles.contentContainer, styles.menuOverride, classNames === null || classNames === void 0 ? void 0 : classNames.content),
540
+ className: contentClassName,
412
541
  style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.content,
413
542
  children: [currentHeader && /*#__PURE__*/_jsx("div", {
414
- className: cx(styles.header, classNames === null || classNames === void 0 ? void 0 : classNames.header),
543
+ className: headerClassName,
415
544
  style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.header,
416
- children: animation === false || animation === undefined ? currentHeader : /*#__PURE__*/_jsx(AnimationWrapper, {
417
- blur: (_animation$blur = animation.blur) !== null && _animation$blur !== void 0 ? _animation$blur : false,
418
- enabled: animation.header,
419
- expand: renderExpand,
420
- fade: (_animation$fade = animation.fade) !== null && _animation$fade !== void 0 ? _animation$fade : true,
421
- id: "header",
422
- children: currentHeader
423
- })
545
+ children: currentHeader
424
546
  }), /*#__PURE__*/_jsx("div", {
425
- className: cx(styles.body, classNames === null || classNames === void 0 ? void 0 : classNames.body),
547
+ className: bodyClassName,
426
548
  style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.body,
427
- children: animation === false || animation === undefined ? currentChildren : /*#__PURE__*/_jsx(AnimationWrapper, {
428
- blur: (_animation$blur2 = animation.blur) !== null && _animation$blur2 !== void 0 ? _animation$blur2 : false,
429
- enabled: animation.body,
430
- expand: renderExpand,
431
- fade: (_animation$fade2 = animation.fade) !== null && _animation$fade2 !== void 0 ? _animation$fade2 : true,
432
- id: "body",
433
- children: currentChildren
434
- })
549
+ children: currentBody
435
550
  }), currentFooter && /*#__PURE__*/_jsx("div", {
436
- className: cx(styles.footer, classNames === null || classNames === void 0 ? void 0 : classNames.footer),
551
+ className: footerClassName,
437
552
  style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.footer,
438
- children: animation === false || animation === undefined ? currentFooter : /*#__PURE__*/_jsx(AnimationWrapper, {
439
- blur: (_animation$blur3 = animation.blur) !== null && _animation$blur3 !== void 0 ? _animation$blur3 : false,
440
- enabled: animation.footer,
441
- expand: renderExpand,
442
- fade: (_animation$fade3 = animation.fade) !== null && _animation$fade3 !== void 0 ? _animation$fade3 : true,
443
- id: "footer",
444
- children: currentFooter
445
- })
553
+ children: currentFooter
446
554
  })]
447
555
  })]
448
556
  }))