@lobehub/ui 2.16.2 → 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.
@@ -115,7 +115,7 @@ var AccordionItem = /*#__PURE__*/memo(function (_ref) {
115
115
  height: 'auto',
116
116
  opacity: 1,
117
117
  transition: {
118
- duration: 0.3,
118
+ duration: 0.2,
119
119
  ease: [0.4, 0, 0.2, 1]
120
120
  }
121
121
  },
@@ -123,7 +123,7 @@ var AccordionItem = /*#__PURE__*/memo(function (_ref) {
123
123
  height: 0,
124
124
  opacity: 0,
125
125
  transition: {
126
- duration: 0.3,
126
+ duration: 0.2,
127
127
  ease: [0.4, 0, 0.2, 1]
128
128
  }
129
129
  }
@@ -1,23 +1,23 @@
1
1
  'use client';
2
2
 
3
3
  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
- 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; }
5
- 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; }
6
- 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; }
7
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
8
- 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); }
9
4
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
10
5
  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."); }
11
6
  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); }
12
7
  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; }
13
8
  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; } }
14
9
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
10
+ 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; }
11
+ 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; }
12
+ 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; }
13
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
14
+ 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); }
15
15
  import { useHover } from 'ahooks';
16
16
  import { ConfigProvider } from 'antd';
17
17
  import { cva } from 'class-variance-authority';
18
18
  import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from 'lucide-react';
19
19
  import { Resizable } from 're-resizable';
20
- import { memo, use, useEffect, useMemo, useRef, useState } from 'react';
20
+ import { memo, use, useCallback, useEffect, useMemo, useReducer, useRef, useTransition } from 'react';
21
21
  import { Center } from 'react-layout-kit';
22
22
  import useControlledState from 'use-merge-value';
23
23
  import Icon from "../Icon";
@@ -35,6 +35,37 @@ var DEFAULT_MODE = 'fixed';
35
35
  var DEFAULT_EXPANDABLE = true;
36
36
  var DEFAULT_EXPAND = true;
37
37
  var DEFAULT_SHOW_HANDLE_WIDE_AREA = true;
38
+
39
+ // State reducer for better state management
40
+
41
+ function draggablePanelReducer(state, action) {
42
+ switch (action.type) {
43
+ case 'START_RESIZE':
44
+ {
45
+ return _objectSpread(_objectSpread({}, state), {}, {
46
+ isResizing: true,
47
+ showExpand: false
48
+ });
49
+ }
50
+ case 'STOP_RESIZE':
51
+ {
52
+ return _objectSpread(_objectSpread({}, state), {}, {
53
+ isResizing: false,
54
+ showExpand: true
55
+ });
56
+ }
57
+ case 'SET_SHOW_EXPAND':
58
+ {
59
+ return _objectSpread(_objectSpread({}, state), {}, {
60
+ showExpand: action.payload
61
+ });
62
+ }
63
+ default:
64
+ {
65
+ return state;
66
+ }
67
+ }
68
+ }
38
69
  var DraggablePanel = /*#__PURE__*/memo(function (_ref) {
39
70
  var _ref$headerHeight = _ref.headerHeight,
40
71
  headerHeight = _ref$headerHeight === void 0 ? DEFAULT_HEADER_HEIGHT : _ref$headerHeight,
@@ -78,6 +109,13 @@ var DraggablePanel = /*#__PURE__*/memo(function (_ref) {
78
109
  var ref = useRef(null);
79
110
  var isHovering = useHover(ref);
80
111
  var isVertical = placement === 'top' || placement === 'bottom';
112
+ var _useTransition = useTransition(),
113
+ _useTransition2 = _slicedToArray(_useTransition, 2),
114
+ isPending = _useTransition2[0],
115
+ startTransition = _useTransition2[1];
116
+
117
+ // Use ref for hover timeout to avoid memory leaks
118
+ var hoverTimeoutRef = useRef(undefined);
81
119
 
82
120
  // inherit direction from Ant Design ConfigProvider
83
121
  var _use = use(ConfigProvider.ConfigContext),
@@ -104,23 +142,46 @@ var DraggablePanel = /*#__PURE__*/memo(function (_ref) {
104
142
  isExpand = _useControlledState2[0],
105
143
  setIsExpand = _useControlledState2[1];
106
144
 
107
- // Auto-expand on hover if not pinned
145
+ // Initialize state with useReducer for better performance
146
+ var initialState = {
147
+ isResizing: false,
148
+ showExpand: true
149
+ };
150
+ var _useReducer = useReducer(draggablePanelReducer, initialState),
151
+ _useReducer2 = _slicedToArray(_useReducer, 2),
152
+ state = _useReducer2[0],
153
+ dispatch = _useReducer2[1];
154
+
155
+ // Auto-expand on hover if not pinned with optimized transition
108
156
  useEffect(function () {
109
157
  if (pin) return;
158
+
159
+ // Clear previous timeout
160
+ if (hoverTimeoutRef.current) {
161
+ clearTimeout(hoverTimeoutRef.current);
162
+ }
110
163
  if (isHovering && !isExpand) {
111
- setIsExpand(true);
164
+ startTransition(function () {
165
+ setIsExpand(true);
166
+ });
112
167
  } else if (!isHovering && isExpand) {
113
- setIsExpand(false);
168
+ // Add a small delay before collapsing to prevent flickering
169
+ hoverTimeoutRef.current = setTimeout(function () {
170
+ startTransition(function () {
171
+ setIsExpand(false);
172
+ });
173
+ }, 150);
114
174
  }
115
175
  }, [pin, isHovering, isExpand, setIsExpand]);
116
- var _useState = useState(true),
117
- _useState2 = _slicedToArray(_useState, 2),
118
- showExpand = _useState2[0],
119
- setShowExpand = _useState2[1];
120
- var _useState3 = useState(false),
121
- _useState4 = _slicedToArray(_useState3, 2),
122
- isResizing = _useState4[0],
123
- setIsResizing = _useState4[1];
176
+
177
+ // Cleanup timeout on unmount
178
+ useEffect(function () {
179
+ return function () {
180
+ if (hoverTimeoutRef.current) {
181
+ clearTimeout(hoverTimeoutRef.current);
182
+ }
183
+ };
184
+ }, []);
124
185
  var canResizing = resize !== false && isExpand;
125
186
 
126
187
  // Style variants for the panel
@@ -265,70 +326,84 @@ var DraggablePanel = /*#__PURE__*/memo(function (_ref) {
265
326
  }
266
327
  }, [internalPlacement]);
267
328
 
329
+ // Toggle expand state with transition for better performance
330
+ var toggleExpand = useCallback(function () {
331
+ if (!expandable) return;
332
+ startTransition(function () {
333
+ setIsExpand(!isExpand);
334
+ });
335
+ }, [expandable, isExpand, setIsExpand]);
336
+
268
337
  // Toggle handle component
269
- var handle = /*#__PURE__*/_jsx(Center, {
270
- className: toggleVariants({
271
- placement: internalPlacement
272
- }),
273
- style: {
274
- opacity: isExpand ? pin ? undefined : 0 : showHandleWhenCollapsed ? 1 : 0
275
- },
276
- children: /*#__PURE__*/_jsx(Center, {
277
- className: classNames === null || classNames === void 0 ? void 0 : classNames.handle,
278
- onClick: function onClick() {
279
- return setIsExpand(!isExpand);
338
+ var handle = useMemo(function () {
339
+ return /*#__PURE__*/_jsx(Center, {
340
+ className: toggleVariants({
341
+ placement: internalPlacement
342
+ }),
343
+ style: {
344
+ opacity: isExpand ? pin ? undefined : 0 : showHandleWhenCollapsed ? 1 : 0
280
345
  },
281
- style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.handle,
282
- children: /*#__PURE__*/_jsx(Icon, {
283
- className: styles.handlerIcon,
284
- icon: Arrow,
285
- size: 16,
286
- style: {
287
- marginBottom: internalPlacement === 'top' ? 4 : 0,
288
- marginLeft: internalPlacement === 'right' ? 4 : 0,
289
- marginRight: internalPlacement === 'left' ? 4 : 0,
290
- marginTop: internalPlacement === 'bottom' ? 4 : 0,
291
- transform: "rotate(".concat(isExpand ? 180 : 0, "deg)")
292
- }
346
+ children: /*#__PURE__*/_jsx(Center, {
347
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.handle,
348
+ onClick: toggleExpand,
349
+ style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.handle,
350
+ children: /*#__PURE__*/_jsx(Icon, {
351
+ className: styles.handlerIcon,
352
+ icon: Arrow,
353
+ size: 16,
354
+ style: {
355
+ marginBottom: internalPlacement === 'top' ? 4 : 0,
356
+ marginLeft: internalPlacement === 'right' ? 4 : 0,
357
+ marginRight: internalPlacement === 'left' ? 4 : 0,
358
+ marginTop: internalPlacement === 'bottom' ? 4 : 0,
359
+ transform: "rotate(".concat(isExpand ? 180 : 0, "deg)"),
360
+ transition: 'transform 0.3s ease'
361
+ }
362
+ })
293
363
  })
294
- })
295
- });
364
+ });
365
+ }, [toggleVariants, internalPlacement, isExpand, pin, showHandleWhenCollapsed, classNames === null || classNames === void 0 ? void 0 : classNames.handle, toggleExpand, customStyles === null || customStyles === void 0 ? void 0 : customStyles.handle, styles.handlerIcon, Arrow]);
296
366
 
297
- // Handle resize events
298
- var handleResize = function handleResize(_, _direction, reference_, delta) {
367
+ // Handle resize events with memoization
368
+ var handleResize = useCallback(function (_, _direction, reference_, delta) {
299
369
  onSizeDragging === null || onSizeDragging === void 0 || onSizeDragging(delta, {
300
370
  height: reference_.style.height,
301
371
  width: reference_.style.width
302
372
  });
303
- };
304
- var handleResizeStart = function handleResizeStart() {
305
- setIsResizing(true);
306
- setShowExpand(false);
307
- };
308
- var handleResizeStop = function handleResizeStop(e, direction, reference_, delta) {
309
- setIsResizing(false);
310
- setShowExpand(true);
373
+ }, [onSizeDragging]);
374
+ var handleResizeStart = useCallback(function () {
375
+ dispatch({
376
+ type: 'START_RESIZE'
377
+ });
378
+ }, []);
379
+ var handleResizeStop = useCallback(function (e, direction, reference_, delta) {
380
+ dispatch({
381
+ type: 'STOP_RESIZE'
382
+ });
311
383
  onSizeChange === null || onSizeChange === void 0 || onSizeChange(delta, {
312
384
  height: reference_.style.height,
313
385
  width: reference_.style.width
314
386
  });
315
- };
387
+ }, [onSizeChange]);
316
388
 
317
389
  // Main panel content
318
- var inner = /*#__PURE__*/_jsx(Resizable, _objectSpread(_objectSpread({}, sizeProps), {}, {
319
- className: cx(styles.panel, classNames === null || classNames === void 0 ? void 0 : classNames.content),
320
- enable: canResizing ? resizing : undefined,
321
- handleClasses: canResizing ? _defineProperty({}, reversePlacement(internalPlacement), cx(handleVariants({
322
- placement: reversePlacement(internalPlacement)
323
- }), showHandleHighlight && styles.handleHighlight)) : {},
324
- onResize: handleResize,
325
- onResizeStart: handleResizeStart,
326
- onResizeStop: handleResizeStop,
327
- style: _objectSpread({
328
- transition: isResizing ? 'unset' : undefined
329
- }, style),
330
- children: children
331
- }));
390
+ var inner = useMemo(function () {
391
+ return /*#__PURE__*/_jsx(Resizable, _objectSpread(_objectSpread({}, sizeProps), {}, {
392
+ className: cx(styles.panel, classNames === null || classNames === void 0 ? void 0 : classNames.content),
393
+ enable: canResizing ? resizing : undefined,
394
+ handleClasses: canResizing ? _defineProperty({}, reversePlacement(internalPlacement), cx(handleVariants({
395
+ placement: reversePlacement(internalPlacement)
396
+ }), showHandleHighlight && styles.handleHighlight)) : {},
397
+ onResize: handleResize,
398
+ onResizeStart: handleResizeStart,
399
+ onResizeStop: handleResizeStop,
400
+ style: _objectSpread({
401
+ opacity: isPending ? 0.95 : 1,
402
+ transition: state.isResizing ? 'unset' : undefined
403
+ }, style),
404
+ children: children
405
+ }));
406
+ }, [sizeProps, styles.panel, classNames === null || classNames === void 0 ? void 0 : classNames.content, canResizing, resizing, internalPlacement, handleVariants, showHandleHighlight, styles.handleHighlight, handleResize, handleResizeStart, handleResizeStop, state.isResizing, isPending, style, children, cx]);
332
407
 
333
408
  // For fullscreen mode, return a simpler layout
334
409
  if (fullscreen) {
@@ -344,8 +419,13 @@ var DraggablePanel = /*#__PURE__*/memo(function (_ref) {
344
419
  }), className),
345
420
  dir: dir,
346
421
  ref: ref,
347
- children: [expandable && showExpand && handle, destroyOnClose ? isExpand && inner : inner]
422
+ children: [expandable && state.showExpand && handle, destroyOnClose ? isExpand && inner : inner]
348
423
  });
424
+ },
425
+ // Custom comparison function to avoid unnecessary re-renders
426
+ function (prevProps, nextProps) {
427
+ // Only re-render if critical props change
428
+ return prevProps.placement === nextProps.placement && prevProps.mode === nextProps.mode && prevProps.expand === nextProps.expand && prevProps.pin === nextProps.pin && prevProps.fullscreen === nextProps.fullscreen && prevProps.size === nextProps.size && prevProps.defaultSize === nextProps.defaultSize && prevProps.minWidth === nextProps.minWidth && prevProps.minHeight === nextProps.minHeight && prevProps.maxWidth === nextProps.maxWidth && prevProps.maxHeight === nextProps.maxHeight && prevProps.expandable === nextProps.expandable && prevProps.resize === nextProps.resize && prevProps.showHandleWhenCollapsed === nextProps.showHandleWhenCollapsed && prevProps.destroyOnClose === nextProps.destroyOnClose && prevProps.showBorder === nextProps.showBorder && prevProps.showHandleHighlight === nextProps.showHandleHighlight && prevProps.showHandleWideArea === nextProps.showHandleWideArea && prevProps.backgroundColor === nextProps.backgroundColor && prevProps.className === nextProps.className && prevProps.dir === nextProps.dir && prevProps.headerHeight === nextProps.headerHeight && prevProps.onSizeChange === nextProps.onSizeChange && prevProps.onSizeDragging === nextProps.onSizeDragging && prevProps.onExpandChange === nextProps.onExpandChange && prevProps.children === nextProps.children;
349
429
  });
350
430
  DraggablePanel.displayName = 'DraggablePanel';
351
431
  export default DraggablePanel;
@@ -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
  }))
@@ -26,8 +26,8 @@ export var useStyles = createStyles(function (_ref, _ref2) {
26
26
  resizeHandleHighlight: css(_templateObject9 || (_templateObject9 = _taggedTemplateLiteral(["\n &:hover {\n &::after {\n width: 3px;\n background: ", ";\n box-shadow: 0 0 8px ", "40;\n }\n }\n\n &:active {\n &::after {\n background: ", ";\n }\n }\n "])), token.colorPrimary, token.colorPrimary, token.colorPrimaryActive),
27
27
  resizeHandleLeft: css(_templateObject10 || (_templateObject10 = _taggedTemplateLiteral(["\n inset-inline-end: -4px;\n "]))),
28
28
  resizeHandleRight: css(_templateObject11 || (_templateObject11 = _taggedTemplateLiteral(["\n inset-inline-start: -4px;\n "]))),
29
- toggleLeft: css(_templateObject12 || (_templateObject12 = _taggedTemplateLiteral(["\n inset-inline-end: -", "px;\n width: ", "px;\n height: 100%;\n\n > div {\n inset-block-start: 50%;\n\n width: ", "px;\n height: ", "px;\n margin-block-start: -", "px;\n border-inline-start-width: 0;\n border-radius: 0 ", "px ", "px 0;\n }\n "])), LAYOUT.offset, LAYOUT.toggleShort, LAYOUT.toggleShort, LAYOUT.toggleLength, LAYOUT.toggleLength / 2, token.borderRadiusLG, token.borderRadiusLG),
30
- toggleRight: css(_templateObject13 || (_templateObject13 = _taggedTemplateLiteral(["\n inset-inline-start: -", "px;\n width: ", "px;\n height: 100%;\n\n > div {\n inset-block-start: 50%;\n\n width: ", "px;\n height: ", "px;\n margin-block-start: -", "px;\n border-inline-end-width: 0;\n border-radius: ", "px 0 0 ", "px; /* \u53F3\u4FA7\u9762\u677F\uFF0Chandle \u5728\u5DE6\u8FB9\uFF0C\u5DE6\u4FA7\u5706\u89D2 */\n }\n "])), LAYOUT.offset, LAYOUT.toggleShort, LAYOUT.toggleShort, LAYOUT.toggleLength, LAYOUT.toggleLength / 2, token.borderRadiusLG, token.borderRadiusLG),
29
+ toggleLeft: css(_templateObject12 || (_templateObject12 = _taggedTemplateLiteral(["\n inset-inline-end: -", "px;\n width: ", "px;\n height: 100%;\n\n > div {\n inset-block-start: 50%;\n\n width: ", "px;\n height: ", "px;\n margin-block-start: -", "px;\n margin-inline-start: -1px;\n border-inline-start-width: 0;\n border-radius: 0 ", "px ", "px 0;\n }\n "])), LAYOUT.offset, LAYOUT.toggleShort, LAYOUT.toggleShort, LAYOUT.toggleLength, LAYOUT.toggleLength / 2, token.borderRadiusLG, token.borderRadiusLG),
30
+ toggleRight: css(_templateObject13 || (_templateObject13 = _taggedTemplateLiteral(["\n inset-inline-start: -", "px;\n width: ", "px;\n height: 100%;\n\n > div {\n inset-block-start: 50%;\n\n width: ", "px;\n height: ", "px;\n margin-block-start: -", "px;\n margin-inline-end: -1px;\n border-inline-end-width: 0;\n border-radius: ", "px 0 0 ", "px; /* \u53F3\u4FA7\u9762\u677F\uFF0Chandle \u5728\u5DE6\u8FB9\uFF0C\u5DE6\u4FA7\u5706\u89D2 */\n }\n "])), LAYOUT.offset, LAYOUT.toggleShort, LAYOUT.toggleShort, LAYOUT.toggleLength, LAYOUT.toggleLength / 2, token.borderRadiusLG, token.borderRadiusLG),
31
31
  toggleRoot: css(_templateObject14 || (_templateObject14 = _taggedTemplateLiteral(["\n pointer-events: none;\n position: absolute;\n z-index: 50;\n\n /* Smooth transitions for all states */\n transition: opacity 0.25s cubic-bezier(0.22, 1, 0.36, 1);\n\n &:has(> div) {\n pointer-events: all;\n }\n\n > div {\n pointer-events: all;\n cursor: pointer;\n\n position: absolute;\n\n border: 1px solid ", ";\n\n color: ", ";\n\n background: ", ";\n backdrop-filter: blur(8px);\n\n /* Enhanced transitions with backdrop blur */\n transition:\n color 0.2s ", ",\n transform 0.2s ", ",\n box-shadow 0.2s ", ";\n\n &:hover {\n color: ", ";\n }\n\n &:active {\n transform: scale(0.95);\n color: ", ";\n }\n }\n "])), token.colorBorder, token.colorTextTertiary, backgroundColor || token.colorBgLayout, token.motionEaseOut, token.motionEaseOut, token.motionEaseOut, token.colorTextSecondary, token.colorText)
32
32
  };
33
33
  });
@@ -2,44 +2,12 @@ import type { NumberSize } from 're-resizable';
2
2
  import type { CSSProperties, ReactNode } from 'react';
3
3
  import type { DivProps } from "../types";
4
4
  export interface DraggableSideNavProps extends Omit<DivProps, 'children' | 'onSelect'> {
5
- /**
6
- * Animation configuration for content transitions
7
- * Set to false to disable all animations
8
- * @default undefined
9
- */
10
- animation?: false | {
11
- /**
12
- * Enable blur effect during transitions
13
- * @default false
14
- */
15
- blur?: boolean;
16
- /**
17
- * Enable animation for body section
18
- * @default false
19
- */
20
- body?: boolean;
21
- /**
22
- * Enable fade effect during transitions
23
- * @default true
24
- */
25
- fade?: boolean;
26
- /**
27
- * Enable animation for footer section
28
- * @default false
29
- */
30
- footer?: boolean;
31
- /**
32
- * Enable animation for header section
33
- * @default false
34
- */
35
- header?: boolean;
36
- };
37
5
  backgroundColor?: string;
38
6
  /**
39
7
  * Body content (main content area)
40
- * Can be a static element or a function that receives expand state
8
+ * Function that receives expand state
41
9
  */
42
- children: ReactNode | ((expand: boolean) => ReactNode);
10
+ body: (expand: boolean) => ReactNode;
43
11
  /**
44
12
  * Classnames for internal components
45
13
  */
@@ -84,7 +52,8 @@ export interface DraggableSideNavProps extends Omit<DivProps, 'children' | 'onSe
84
52
  */
85
53
  maxWidth?: number;
86
54
  /**
87
- * Minimum width (also the collapsed width)
55
+ * Minimum width when expanded (does not affect collapsed width which is always 64px)
56
+ * Only applies when the panel is in expanded state
88
57
  * @default 64
89
58
  */
90
59
  minWidth?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/ui",
3
- "version": "2.16.2",
3
+ "version": "2.16.3",
4
4
  "description": "Lobe UI is an open-source UI component library for building AIGC web apps",
5
5
  "keywords": [
6
6
  "lobehub",