@lobehub/ui 5.6.5 → 5.7.0

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.
@@ -3,7 +3,7 @@ import Center from "../Flex/Center.mjs";
3
3
  import Icon from "../Icon/Icon.mjs";
4
4
  import { handleVariants, panelVariants, styles, toggleVariants } from "./style.mjs";
5
5
  import { reversePlacement } from "./utils.mjs";
6
- import { memo, use, useCallback, useEffect, useMemo, useReducer, useRef, useTransition } from "react";
6
+ import { memo, startTransition, use, useCallback, useEffect, useMemo, useRef, useState } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { ConfigProvider } from "antd";
9
9
  import { cx } from "antd-style";
@@ -13,66 +13,65 @@ import { useHover } from "ahooks";
13
13
  import isEqual from "fast-deep-equal";
14
14
  import { Resizable } from "re-resizable";
15
15
  //#region src/DraggablePanel/DraggablePanel.tsx
16
- const DEFAULT_HEIGHT = 180;
17
- const DEFAULT_WIDTH = 280;
18
- const DEFAULT_HEADER_HEIGHT = 0;
19
- const DEFAULT_PIN = true;
20
- const DEFAULT_MODE = "fixed";
21
- const DEFAULT_EXPANDABLE = true;
22
- const DEFAULT_EXPAND = true;
23
- const DEFAULT_SHOW_HANDLE_WIDE_AREA = true;
24
- function draggablePanelReducer(state, action) {
25
- switch (action.type) {
26
- case "START_RESIZE": return {
27
- ...state,
28
- isResizing: true,
29
- showExpand: false
30
- };
31
- case "STOP_RESIZE": return {
32
- ...state,
33
- isResizing: false,
34
- showExpand: true
35
- };
36
- case "SET_SHOW_EXPAND": return {
37
- ...state,
38
- showExpand: action.payload
39
- };
40
- default: return state;
41
- }
42
- }
43
- const DraggablePanel = memo(({ headerHeight = DEFAULT_HEADER_HEIGHT, fullscreen, maxHeight, pin = DEFAULT_PIN, mode = DEFAULT_MODE, children, placement = "right", resize, style, showBorder = true, showHandleHighlight = false, showHandleWideArea = DEFAULT_SHOW_HANDLE_WIDE_AREA, backgroundColor, size, defaultSize: customizeDefaultSize, minWidth, minHeight, maxWidth, onSizeChange, onSizeDragging, expandable = DEFAULT_EXPANDABLE, expand, defaultExpand = DEFAULT_EXPAND, onExpandChange, className, showHandleWhenCollapsed, destroyOnClose, styles: customStyles, classNames, dir }) => {
16
+ const ARROW_MAP = {
17
+ bottom: ChevronUp,
18
+ left: ChevronRight,
19
+ right: ChevronLeft,
20
+ top: ChevronDown
21
+ };
22
+ const MARGIN_MAP = {
23
+ bottom: { marginTop: 4 },
24
+ left: { marginRight: 4 },
25
+ right: { marginLeft: 4 },
26
+ top: { marginBottom: 4 }
27
+ };
28
+ const DISABLED_RESIZING = {
29
+ bottom: false,
30
+ bottomLeft: false,
31
+ bottomRight: false,
32
+ left: false,
33
+ right: false,
34
+ top: false,
35
+ topLeft: false,
36
+ topRight: false
37
+ };
38
+ const toCssSize = (value, fallback) => {
39
+ if (typeof value === "number") return `${Math.max(value, 0)}px`;
40
+ if (typeof value === "string" && value.length > 0) return value;
41
+ return fallback;
42
+ };
43
+ const DraggablePanel = memo(({ headerHeight = 0, fullscreen, maxHeight, pin = true, mode = "fixed", children, placement = "right", resize, style, showBorder = true, showHandleHighlight = false, showHandleWideArea = true, backgroundColor, size, stableLayout = false, defaultSize: customizeDefaultSize, minWidth, minHeight, maxWidth, onSizeChange, onSizeDragging, expandable = true, expand, defaultExpand = true, onExpandChange, className, showHandleWhenCollapsed, destroyOnClose, styles: customStyles, classNames, dir }) => {
44
44
  const ref = useRef(null);
45
45
  const isHovering = useHover(ref);
46
46
  const isVertical = placement === "top" || placement === "bottom";
47
- const [isPending, startTransition] = useTransition();
48
47
  const hoverTimeoutRef = useRef(void 0);
48
+ const resetTransitionTimeoutRef = useRef(void 0);
49
+ const resizableRef = useRef(null);
50
+ const initialExpandedSizeRef = useRef(void 0);
49
51
  const { direction: antdDirection } = use(ConfigProvider.ConfigContext);
50
52
  const direction = dir ?? antdDirection;
51
53
  const internalPlacement = useMemo(() => {
52
- return direction === "rtl" && ["left", "right"].includes(placement) ? placement === "left" ? "right" : "left" : placement;
54
+ if (direction !== "rtl") return placement;
55
+ if (placement === "left") return "right";
56
+ if (placement === "right") return "left";
57
+ return placement;
53
58
  }, [direction, placement]);
54
- const cssVariables = useMemo(() => ({
59
+ const cssVariables = {
55
60
  "--draggable-panel-bg": backgroundColor || "",
56
61
  "--draggable-panel-header-height": `${headerHeight}px`
57
- }), [backgroundColor, headerHeight]);
62
+ };
58
63
  const [isExpand, setIsExpand] = useMergeState(defaultExpand, {
59
64
  onChange: onExpandChange,
60
65
  value: expand
61
66
  });
62
- const [state, dispatch] = useReducer(draggablePanelReducer, {
63
- isResizing: false,
64
- showExpand: true
65
- });
67
+ const [shouldTransition, setShouldTransition] = useState(true);
68
+ const [showExpand, setShowExpand] = useState(true);
66
69
  useEffect(() => {
67
70
  if (pin) return;
68
71
  if (hoverTimeoutRef.current) clearTimeout(hoverTimeoutRef.current);
69
- if (isHovering && !isExpand) startTransition(() => {
70
- setIsExpand(true);
71
- });
72
+ if (isHovering && !isExpand) startTransition(() => setIsExpand(true));
72
73
  else if (!isHovering && isExpand) hoverTimeoutRef.current = setTimeout(() => {
73
- startTransition(() => {
74
- setIsExpand(false);
75
- });
74
+ startTransition(() => setIsExpand(false));
76
75
  }, 150);
77
76
  }, [
78
77
  pin,
@@ -83,8 +82,13 @@ const DraggablePanel = memo(({ headerHeight = DEFAULT_HEADER_HEIGHT, fullscreen,
83
82
  useEffect(() => {
84
83
  return () => {
85
84
  if (hoverTimeoutRef.current) clearTimeout(hoverTimeoutRef.current);
85
+ if (resetTransitionTimeoutRef.current) clearTimeout(resetTransitionTimeoutRef.current);
86
86
  };
87
87
  }, []);
88
+ useEffect(() => {
89
+ initialExpandedSizeRef.current = void 0;
90
+ }, [internalPlacement]);
91
+ const reversed = reversePlacement(internalPlacement);
88
92
  const canResizing = resize !== false && isExpand;
89
93
  const resizing = useMemo(() => ({
90
94
  bottom: false,
@@ -95,23 +99,27 @@ const DraggablePanel = memo(({ headerHeight = DEFAULT_HEADER_HEIGHT, fullscreen,
95
99
  top: false,
96
100
  topLeft: false,
97
101
  topRight: false,
98
- [reversePlacement(internalPlacement)]: true,
102
+ [reversed]: true,
99
103
  ...resize
100
- }), [internalPlacement, resize]);
104
+ }), [reversed, resize]);
101
105
  const defaultSize = useMemo(() => {
102
106
  if (isVertical) return {
103
- height: DEFAULT_HEIGHT,
107
+ height: 180,
104
108
  width: "100%",
105
109
  ...customizeDefaultSize
106
110
  };
107
111
  return {
108
112
  height: "100%",
109
- width: DEFAULT_WIDTH,
113
+ width: 280,
110
114
  ...customizeDefaultSize
111
115
  };
112
116
  }, [isVertical, customizeDefaultSize]);
117
+ const normalizedMaxHeight = typeof maxHeight === "number" ? Math.max(maxHeight, 0) : void 0;
118
+ const normalizedMaxWidth = typeof maxWidth === "number" ? Math.max(maxWidth, 0) : void 0;
119
+ const normalizedMinHeight = typeof minHeight === "number" ? Math.max(minHeight, 0) : void 0;
120
+ const normalizedMinWidth = typeof minWidth === "number" ? Math.max(minWidth, 0) : void 0;
113
121
  const sizeProps = useMemo(() => {
114
- if (!isExpand) return isVertical ? {
122
+ if (!stableLayout && !isExpand) return isVertical ? {
115
123
  minHeight: 0,
116
124
  size: { height: 0 }
117
125
  } : {
@@ -120,143 +128,294 @@ const DraggablePanel = memo(({ headerHeight = DEFAULT_HEADER_HEIGHT, fullscreen,
120
128
  };
121
129
  return {
122
130
  defaultSize,
123
- maxHeight: typeof maxHeight === "number" ? Math.max(maxHeight, 0) : maxHeight,
124
- maxWidth: typeof maxWidth === "number" ? Math.max(maxWidth, 0) : maxWidth,
125
- minHeight: typeof minHeight === "number" ? Math.max(minHeight, 0) : minHeight,
126
- minWidth: typeof minWidth === "number" ? Math.max(minWidth, 0) : minWidth,
131
+ maxHeight: normalizedMaxHeight,
132
+ maxWidth: normalizedMaxWidth,
133
+ minHeight: normalizedMinHeight,
134
+ minWidth: normalizedMinWidth,
127
135
  size
128
136
  };
129
137
  }, [
138
+ stableLayout,
130
139
  isExpand,
131
140
  isVertical,
132
141
  defaultSize,
133
- maxHeight,
134
- maxWidth,
135
- minHeight,
136
- minWidth,
142
+ normalizedMaxHeight,
143
+ normalizedMaxWidth,
144
+ normalizedMinHeight,
145
+ normalizedMinWidth,
137
146
  size
138
147
  ]);
139
- const Arrow = useMemo(() => {
140
- switch (internalPlacement) {
141
- case "top": return ChevronDown;
142
- case "bottom": return ChevronUp;
143
- case "right": return ChevronLeft;
144
- case "left": return ChevronRight;
145
- default: return ChevronLeft;
146
- }
147
- }, [internalPlacement]);
148
- const toggleExpand = useCallback(() => {
149
- if (!expandable) return;
150
- startTransition(() => {
151
- setIsExpand(!isExpand);
148
+ const fallbackExpandedSize = isVertical ? "180px" : "280px";
149
+ const controlledExpandedSize = useMemo(() => {
150
+ const controlledSize = isVertical ? size?.height : size?.width;
151
+ if (controlledSize === void 0) return void 0;
152
+ return toCssSize(controlledSize, fallbackExpandedSize);
153
+ }, [
154
+ isVertical,
155
+ size?.height,
156
+ size?.width,
157
+ fallbackExpandedSize
158
+ ]);
159
+ const defaultExpandedSize = useMemo(() => {
160
+ return toCssSize(isVertical ? defaultSize.height : defaultSize.width, fallbackExpandedSize);
161
+ }, [
162
+ isVertical,
163
+ defaultSize.height,
164
+ defaultSize.width,
165
+ fallbackExpandedSize
166
+ ]);
167
+ const [resizedExpandedSize, setResizedExpandedSize] = useState({});
168
+ const expandedOuterSize = controlledExpandedSize ?? (isVertical ? resizedExpandedSize.vertical : resizedExpandedSize.horizontal) ?? defaultExpandedSize;
169
+ const setExpandedMainSize = useCallback((nextSize) => {
170
+ if (!stableLayout) return;
171
+ const currentSize = isVertical ? nextSize.height : nextSize.width;
172
+ if (!currentSize) return;
173
+ const normalizedSize = toCssSize(currentSize, fallbackExpandedSize);
174
+ setResizedExpandedSize((state) => isVertical ? {
175
+ ...state,
176
+ vertical: normalizedSize
177
+ } : {
178
+ ...state,
179
+ horizontal: normalizedSize
152
180
  });
181
+ }, [
182
+ fallbackExpandedSize,
183
+ isVertical,
184
+ stableLayout
185
+ ]);
186
+ const captureInitialExpandedSize = useCallback(() => {
187
+ if (initialExpandedSizeRef.current) return initialExpandedSizeRef.current;
188
+ const rect = resizableRef.current?.resizable?.getBoundingClientRect();
189
+ if (!rect) return void 0;
190
+ const nextInitialSize = isVertical ? {
191
+ height: rect.height,
192
+ width: "100%"
193
+ } : {
194
+ height: "100%",
195
+ width: rect.width
196
+ };
197
+ initialExpandedSizeRef.current = nextInitialSize;
198
+ return nextInitialSize;
199
+ }, [isVertical]);
200
+ useEffect(() => {
201
+ if (!isExpand) return;
202
+ captureInitialExpandedSize();
203
+ }, [captureInitialExpandedSize, isExpand]);
204
+ const toggleExpand = useCallback(() => {
205
+ if (expandable) setIsExpand(!isExpand);
153
206
  }, [
154
207
  expandable,
155
208
  isExpand,
156
209
  setIsExpand
157
210
  ]);
158
- const handle = useMemo(() => /* @__PURE__ */ jsx(Center, {
159
- className: toggleVariants({
160
- placement: internalPlacement,
161
- showHandleWideArea
162
- }),
163
- style: { opacity: isExpand ? pin ? void 0 : 0 : showHandleWhenCollapsed ? 1 : 0 },
164
- children: /* @__PURE__ */ jsx(Center, {
165
- className: classNames?.handle,
166
- style: customStyles?.handle,
167
- onClick: toggleExpand,
168
- children: /* @__PURE__ */ jsx(Icon, {
169
- className: styles.handlerIcon,
170
- icon: Arrow,
171
- size: 16,
172
- style: {
173
- marginBottom: internalPlacement === "top" ? 4 : 0,
174
- marginLeft: internalPlacement === "right" ? 4 : 0,
175
- marginRight: internalPlacement === "left" ? 4 : 0,
176
- marginTop: internalPlacement === "bottom" ? 4 : 0,
177
- transform: `rotate(${isExpand ? 180 : 0}deg)`,
178
- transition: "transform 0.3s ease"
179
- }
180
- })
181
- })
182
- }), [
183
- toggleVariants,
184
- internalPlacement,
185
- isExpand,
186
- pin,
187
- showHandleWhenCollapsed,
188
- classNames?.handle,
189
- toggleExpand,
190
- customStyles?.handle,
191
- styles.handlerIcon,
192
- Arrow
193
- ]);
194
- const handleResize = useCallback((_, _direction, reference_, delta) => {
195
- onSizeDragging?.(delta, {
196
- height: reference_.style.height,
197
- width: reference_.style.width
211
+ const clampResizeSize = useCallback((el) => {
212
+ const rect = el.getBoundingClientRect();
213
+ const currentMainSize = isVertical ? rect.height : rect.width;
214
+ const minMainSize = isVertical ? normalizedMinHeight : normalizedMinWidth;
215
+ const maxMainSize = isVertical ? normalizedMaxHeight : normalizedMaxWidth;
216
+ let clampedMainSize = currentMainSize;
217
+ if (typeof minMainSize === "number") clampedMainSize = Math.max(clampedMainSize, minMainSize);
218
+ if (typeof maxMainSize === "number") clampedMainSize = Math.min(clampedMainSize, maxMainSize);
219
+ if (!Number.isFinite(clampedMainSize) || Math.abs(clampedMainSize - currentMainSize) < .5) return {
220
+ height: el.style.height,
221
+ width: el.style.width
222
+ };
223
+ const width = isVertical ? el.style.width || "100%" : `${clampedMainSize}px`;
224
+ const height = isVertical ? `${clampedMainSize}px` : el.style.height || "100%";
225
+ resizableRef.current?.updateSize({
226
+ height,
227
+ width
198
228
  });
199
- }, [onSizeDragging]);
200
- const handleResizeStart = useCallback(() => {
201
- dispatch({ type: "START_RESIZE" });
229
+ return {
230
+ height,
231
+ width
232
+ };
233
+ }, [
234
+ isVertical,
235
+ normalizedMaxHeight,
236
+ normalizedMaxWidth,
237
+ normalizedMinHeight,
238
+ normalizedMinWidth
239
+ ]);
240
+ const handleResize = useCallback((_event, _direction, el, delta) => {
241
+ const nextSize = clampResizeSize(el);
242
+ setExpandedMainSize(nextSize);
243
+ onSizeDragging?.(delta, nextSize);
244
+ }, [
245
+ clampResizeSize,
246
+ onSizeDragging,
247
+ setExpandedMainSize
248
+ ]);
249
+ const triggerResetWithoutTransition = useCallback(() => {
250
+ if (resetTransitionTimeoutRef.current) clearTimeout(resetTransitionTimeoutRef.current);
251
+ setShouldTransition(false);
252
+ resetTransitionTimeoutRef.current = setTimeout(() => {
253
+ setShouldTransition(true);
254
+ }, 0);
202
255
  }, []);
203
- const handleResizeStop = useCallback((e, direction, reference_, delta) => {
204
- dispatch({ type: "STOP_RESIZE" });
205
- onSizeChange?.(delta, {
206
- height: reference_.style.height,
207
- width: reference_.style.width
208
- });
209
- }, [onSizeChange]);
210
- const inner = useMemo(() => /* @__PURE__ */ jsx(Resizable, {
256
+ const handleResetSize = useCallback(() => {
257
+ if (!canResizing) return;
258
+ const resetSize = captureInitialExpandedSize();
259
+ if (!resetSize) return;
260
+ triggerResetWithoutTransition();
261
+ const rect = resizableRef.current?.resizable?.getBoundingClientRect();
262
+ const prevMainSize = rect ? isVertical ? rect.height : rect.width : 0;
263
+ const resetMainSize = isVertical ? resetSize.height : resetSize.width;
264
+ const nextMainSize = typeof resetMainSize === "number" ? resetMainSize : prevMainSize;
265
+ resizableRef.current?.updateSize(resetSize);
266
+ setExpandedMainSize(resetSize);
267
+ onSizeChange?.(isVertical ? {
268
+ height: nextMainSize - prevMainSize,
269
+ width: 0
270
+ } : {
271
+ height: 0,
272
+ width: nextMainSize - prevMainSize
273
+ }, resetSize);
274
+ }, [
275
+ canResizing,
276
+ captureInitialExpandedSize,
277
+ isVertical,
278
+ onSizeChange,
279
+ setExpandedMainSize,
280
+ triggerResetWithoutTransition
281
+ ]);
282
+ const handleResizeStart = useCallback((event) => {
283
+ if (event.detail === 2) {
284
+ handleResetSize();
285
+ return false;
286
+ }
287
+ if (resetTransitionTimeoutRef.current) {
288
+ clearTimeout(resetTransitionTimeoutRef.current);
289
+ resetTransitionTimeoutRef.current = void 0;
290
+ }
291
+ setShouldTransition(false);
292
+ setShowExpand(false);
293
+ }, [handleResetSize]);
294
+ const handleResizeStop = useCallback((_event, _direction, el, delta) => {
295
+ const nextSize = clampResizeSize(el);
296
+ setExpandedMainSize(nextSize);
297
+ setShouldTransition(true);
298
+ setShowExpand(true);
299
+ onSizeChange?.(delta, nextSize);
300
+ }, [
301
+ clampResizeSize,
302
+ onSizeChange,
303
+ setExpandedMainSize
304
+ ]);
305
+ const resizeHandleClassName = useMemo(() => cx(handleVariants({ placement: reversed }), showHandleHighlight && styles.handleHighlight), [reversed, showHandleHighlight]);
306
+ if (fullscreen) return /* @__PURE__ */ jsx("div", {
307
+ className: cx(styles.fullscreen, className),
308
+ style: cssVariables,
309
+ children
310
+ });
311
+ const Arrow = ARROW_MAP[internalPlacement] ?? ChevronLeft;
312
+ const stableOuterFlex = stableLayout ? {
313
+ display: "flex",
314
+ flexDirection: "column",
315
+ minHeight: 0
316
+ } : {};
317
+ const sidebarOuterStyle = isVertical ? {
318
+ height: isExpand ? expandedOuterSize : 0,
319
+ overflow: "hidden",
320
+ transition: shouldTransition ? "height 0.2s var(--ant-motion-ease-out, ease)" : "none",
321
+ width: "100%",
322
+ ...stableOuterFlex
323
+ } : {
324
+ overflow: "hidden",
325
+ transition: shouldTransition ? "width 0.2s var(--ant-motion-ease-out, ease)" : "none",
326
+ width: isExpand ? expandedOuterSize : 0,
327
+ ...stableLayout ? {
328
+ ...stableOuterFlex,
329
+ flex: 1,
330
+ minWidth: 0,
331
+ height: "100%"
332
+ } : {}
333
+ };
334
+ const sidebarInnerStyle = stableLayout ? {
335
+ display: "flex",
336
+ flex: 1,
337
+ flexDirection: "column",
338
+ height: "100%",
339
+ minHeight: 0,
340
+ minWidth: 0,
341
+ width: "100%"
342
+ } : isVertical ? {
343
+ height: "100%",
344
+ width: "100%"
345
+ } : { width: "100%" };
346
+ const stableAsideStyle = stableLayout ? {
347
+ display: "flex",
348
+ flexDirection: "column",
349
+ minHeight: 0,
350
+ ...mode === "fixed" ? { height: "100%" } : {}
351
+ } : {};
352
+ const stableResizableStyle = {
353
+ display: "flex",
354
+ flex: 1,
355
+ flexDirection: "column",
356
+ height: "100%",
357
+ minHeight: 0,
358
+ minWidth: 0,
359
+ width: "100%"
360
+ };
361
+ const panelNode = (!destroyOnClose || isExpand) && /* @__PURE__ */ jsx(Resizable, {
362
+ ref: resizableRef,
211
363
  ...sizeProps,
212
364
  className: cx(styles.panel, classNames?.content),
213
- enable: canResizing ? resizing : void 0,
214
- handleClasses: canResizing ? { [reversePlacement(internalPlacement)]: cx(handleVariants({ placement: reversePlacement(internalPlacement) }), showHandleHighlight && styles.handleHighlight) } : {},
365
+ enable: canResizing ? resizing : DISABLED_RESIZING,
366
+ handleClasses: canResizing ? { [reversed]: resizeHandleClassName } : {},
215
367
  style: {
216
368
  ...cssVariables,
217
- opacity: isPending ? .95 : 1,
218
- transition: state.isResizing ? "unset" : void 0,
369
+ transition: shouldTransition ? void 0 : "none",
370
+ ...stableLayout ? stableResizableStyle : {},
219
371
  ...style
220
372
  },
221
373
  onResize: handleResize,
222
374
  onResizeStart: handleResizeStart,
223
375
  onResizeStop: handleResizeStop,
224
- children
225
- }), [
226
- sizeProps,
227
- styles.panel,
228
- classNames?.content,
229
- canResizing,
230
- resizing,
231
- internalPlacement,
232
- handleVariants,
233
- showHandleHighlight,
234
- styles.handleHighlight,
235
- handleResize,
236
- handleResizeStart,
237
- handleResizeStop,
238
- state.isResizing,
239
- isPending,
240
- style,
241
- children,
242
- cx
243
- ]);
244
- if (fullscreen) return /* @__PURE__ */ jsx("div", {
245
- className: cx(styles.fullscreen, className),
246
- style: cssVariables,
247
- children
376
+ children: stableLayout ? /* @__PURE__ */ jsx("div", {
377
+ style: sidebarInnerStyle,
378
+ children
379
+ }) : children
248
380
  });
249
381
  return /* @__PURE__ */ jsxs("aside", {
250
382
  dir,
251
383
  ref,
252
- style: cssVariables,
384
+ style: {
385
+ ...cssVariables,
386
+ ...stableAsideStyle
387
+ },
253
388
  className: cx(panelVariants({
254
389
  isExpand,
255
390
  mode,
256
391
  placement: internalPlacement,
257
392
  showBorder
258
393
  }), className),
259
- children: [expandable && state.showExpand && handle, destroyOnClose ? isExpand && inner : inner]
394
+ children: [expandable && showExpand && /* @__PURE__ */ jsx(Center, {
395
+ className: toggleVariants({
396
+ placement: internalPlacement,
397
+ showHandleWideArea
398
+ }),
399
+ style: { opacity: isExpand ? pin ? void 0 : 0 : showHandleWhenCollapsed ? 1 : 0 },
400
+ children: /* @__PURE__ */ jsx(Center, {
401
+ className: classNames?.handle,
402
+ style: customStyles?.handle,
403
+ onClick: toggleExpand,
404
+ children: /* @__PURE__ */ jsx(Icon, {
405
+ className: styles.handlerIcon,
406
+ icon: Arrow,
407
+ size: 16,
408
+ style: {
409
+ ...MARGIN_MAP[internalPlacement],
410
+ transform: `rotate(${isExpand ? 180 : 0}deg)`,
411
+ transition: "transform 0.3s ease"
412
+ }
413
+ })
414
+ })
415
+ }), stableLayout ? /* @__PURE__ */ jsx("div", {
416
+ style: sidebarOuterStyle,
417
+ children: panelNode
418
+ }) : panelNode]
260
419
  });
261
420
  }, isEqual);
262
421
  DraggablePanel.displayName = "DraggablePanel";
@@ -1 +1 @@
1
- {"version":3,"file":"DraggablePanel.mjs","names":["useControlledState"],"sources":["../../src/DraggablePanel/DraggablePanel.tsx"],"sourcesContent":["'use client';\n\nimport { useHover } from 'ahooks';\nimport { ConfigProvider } from 'antd';\nimport { cx } from 'antd-style';\nimport isEqual from 'fast-deep-equal';\nimport { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from 'lucide-react';\nimport type { Enable, NumberSize, Size } from 're-resizable';\nimport { Resizable } from 're-resizable';\nimport {\n memo,\n use,\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useTransition,\n} from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport { Center } from '@/Flex';\nimport Icon from '@/Icon';\n\nimport { handleVariants, panelVariants, styles, toggleVariants } from './style';\nimport type { DraggablePanelProps } from './type';\nimport { reversePlacement } from './utils';\n\n// Constants\nconst DEFAULT_HEIGHT = 180;\nconst DEFAULT_WIDTH = 280;\nconst DEFAULT_HEADER_HEIGHT = 0;\nconst DEFAULT_PIN = true;\nconst DEFAULT_MODE = 'fixed';\nconst DEFAULT_EXPANDABLE = true;\nconst DEFAULT_EXPAND = true;\nconst DEFAULT_SHOW_HANDLE_WIDE_AREA = true;\n\n// State reducer for better state management\ninterface DraggablePanelState {\n isResizing: boolean;\n showExpand: boolean;\n}\n\ntype DraggablePanelAction =\n | { type: 'START_RESIZE' }\n | { type: 'STOP_RESIZE' }\n | { payload: boolean; type: 'SET_SHOW_EXPAND' };\n\nfunction draggablePanelReducer(\n state: DraggablePanelState,\n action: DraggablePanelAction,\n): DraggablePanelState {\n switch (action.type) {\n case 'START_RESIZE': {\n return { ...state, isResizing: true, showExpand: false };\n }\n case 'STOP_RESIZE': {\n return { ...state, isResizing: false, showExpand: true };\n }\n case 'SET_SHOW_EXPAND': {\n return { ...state, showExpand: action.payload };\n }\n default: {\n return state;\n }\n }\n}\n\nconst DraggablePanel = memo<DraggablePanelProps>(\n ({\n headerHeight = DEFAULT_HEADER_HEIGHT,\n fullscreen,\n maxHeight,\n pin = DEFAULT_PIN,\n mode = DEFAULT_MODE,\n children,\n placement = 'right',\n resize,\n style,\n showBorder = true,\n showHandleHighlight = false,\n showHandleWideArea = DEFAULT_SHOW_HANDLE_WIDE_AREA,\n backgroundColor,\n size,\n defaultSize: customizeDefaultSize,\n minWidth,\n minHeight,\n maxWidth,\n onSizeChange,\n onSizeDragging,\n expandable = DEFAULT_EXPANDABLE,\n expand,\n defaultExpand = DEFAULT_EXPAND,\n onExpandChange,\n className,\n showHandleWhenCollapsed,\n destroyOnClose,\n styles: customStyles,\n classNames,\n dir,\n }) => {\n const ref = useRef<HTMLDivElement>(null);\n const isHovering = useHover(ref);\n const isVertical = placement === 'top' || placement === 'bottom';\n const [isPending, startTransition] = useTransition();\n\n // Use ref for hover timeout to avoid memory leaks\n const hoverTimeoutRef = useRef<any>(undefined);\n\n // inherit direction from Ant Design ConfigProvider\n const { direction: antdDirection } = use(ConfigProvider.ConfigContext);\n const direction = dir ?? antdDirection;\n\n // Handle RTL direction\n const internalPlacement = useMemo(() => {\n return direction === 'rtl' && ['left', 'right'].includes(placement)\n ? placement === 'left'\n ? 'right'\n : 'left'\n : placement;\n }, [direction, placement]);\n\n const cssVariables = useMemo<Record<string, string>>(\n () => ({\n '--draggable-panel-bg': backgroundColor || '',\n '--draggable-panel-header-height': `${headerHeight}px`,\n }),\n [backgroundColor, headerHeight],\n );\n\n const [isExpand, setIsExpand] = useControlledState(defaultExpand, {\n onChange: onExpandChange,\n value: expand,\n });\n\n // Initialize state with useReducer for better performance\n const initialState: DraggablePanelState = {\n isResizing: false,\n showExpand: true,\n };\n\n const [state, dispatch] = useReducer(draggablePanelReducer, initialState);\n\n // Auto-expand on hover if not pinned with optimized transition\n useEffect(() => {\n if (pin) return;\n\n // Clear previous timeout\n if (hoverTimeoutRef.current) {\n clearTimeout(hoverTimeoutRef.current);\n }\n\n if (isHovering && !isExpand) {\n startTransition(() => {\n setIsExpand(true);\n });\n } else if (!isHovering && isExpand) {\n // Add a small delay before collapsing to prevent flickering\n hoverTimeoutRef.current = setTimeout(() => {\n startTransition(() => {\n setIsExpand(false);\n });\n }, 150);\n }\n }, [pin, isHovering, isExpand, setIsExpand]);\n\n // Cleanup timeout on unmount\n useEffect(() => {\n return () => {\n if (hoverTimeoutRef.current) {\n clearTimeout(hoverTimeoutRef.current);\n }\n };\n }, []);\n\n const canResizing = resize !== false && isExpand;\n\n // Configure resizing handles\n const resizing = useMemo(\n () => ({\n bottom: false,\n bottomLeft: false,\n bottomRight: false,\n left: false,\n right: false,\n top: false,\n topLeft: false,\n topRight: false,\n [reversePlacement(internalPlacement)]: true,\n ...(resize as Enable),\n }),\n [internalPlacement, resize],\n );\n\n // Calculate default size based on orientation\n const defaultSize: Size = useMemo(() => {\n if (isVertical)\n return {\n height: DEFAULT_HEIGHT,\n width: '100%',\n ...customizeDefaultSize,\n };\n\n return {\n height: '100%',\n width: DEFAULT_WIDTH,\n ...customizeDefaultSize,\n };\n }, [isVertical, customizeDefaultSize]);\n\n // Determine appropriate size props based on expand state\n const sizeProps = useMemo(() => {\n if (!isExpand) {\n return isVertical\n ? { minHeight: 0, size: { height: 0 } }\n : { minWidth: 0, size: { width: 0 } };\n }\n\n return {\n defaultSize,\n maxHeight: typeof maxHeight === 'number' ? Math.max(maxHeight, 0) : maxHeight,\n maxWidth: typeof maxWidth === 'number' ? Math.max(maxWidth, 0) : maxWidth,\n minHeight: typeof minHeight === 'number' ? Math.max(minHeight, 0) : minHeight,\n minWidth: typeof minWidth === 'number' ? Math.max(minWidth, 0) : minWidth,\n size: size as Size,\n };\n }, [isExpand, isVertical, defaultSize, maxHeight, maxWidth, minHeight, minWidth, size]);\n\n // Determine the appropriate arrow icon based on placement\n const Arrow = useMemo(() => {\n switch (internalPlacement) {\n case 'top': {\n return ChevronDown;\n }\n case 'bottom': {\n return ChevronUp;\n }\n case 'right': {\n return ChevronLeft;\n }\n case 'left': {\n return ChevronRight;\n }\n default: {\n return ChevronLeft;\n }\n }\n }, [internalPlacement]);\n\n // Toggle expand state with transition for better performance\n const toggleExpand = useCallback(() => {\n if (!expandable) return;\n\n startTransition(() => {\n setIsExpand(!isExpand);\n });\n }, [expandable, isExpand, setIsExpand]);\n\n // Toggle handle component\n const handle = useMemo(\n () => (\n <Center\n className={toggleVariants({ placement: internalPlacement, showHandleWideArea })}\n style={{ opacity: isExpand ? (pin ? undefined : 0) : showHandleWhenCollapsed ? 1 : 0 }}\n >\n <Center\n className={classNames?.handle}\n style={customStyles?.handle}\n onClick={toggleExpand}\n >\n <Icon\n className={styles.handlerIcon}\n icon={Arrow}\n size={16}\n style={{\n marginBottom: internalPlacement === 'top' ? 4 : 0,\n marginLeft: internalPlacement === 'right' ? 4 : 0,\n marginRight: internalPlacement === 'left' ? 4 : 0,\n marginTop: internalPlacement === 'bottom' ? 4 : 0,\n transform: `rotate(${isExpand ? 180 : 0}deg)`,\n transition: 'transform 0.3s ease',\n }}\n />\n </Center>\n </Center>\n ),\n [\n toggleVariants,\n internalPlacement,\n isExpand,\n pin,\n showHandleWhenCollapsed,\n classNames?.handle,\n toggleExpand,\n customStyles?.handle,\n styles.handlerIcon,\n Arrow,\n ],\n );\n\n // Handle resize events with memoization\n const handleResize = useCallback(\n (_: unknown, _direction: unknown, reference_: HTMLElement, delta: NumberSize) => {\n onSizeDragging?.(delta, {\n height: reference_.style.height,\n width: reference_.style.width,\n });\n },\n [onSizeDragging],\n );\n\n const handleResizeStart = useCallback(() => {\n dispatch({ type: 'START_RESIZE' });\n }, []);\n\n const handleResizeStop = useCallback(\n (e: unknown, direction: unknown, reference_: HTMLElement, delta: NumberSize) => {\n dispatch({ type: 'STOP_RESIZE' });\n onSizeChange?.(delta, {\n height: reference_.style.height,\n width: reference_.style.width,\n });\n },\n [onSizeChange],\n );\n\n // Main panel content\n const inner = useMemo(\n () => (\n <Resizable\n {...sizeProps}\n className={cx(styles.panel, classNames?.content)}\n enable={canResizing ? (resizing as Enable) : undefined}\n handleClasses={\n canResizing\n ? {\n [reversePlacement(internalPlacement)]: cx(\n handleVariants({\n placement: reversePlacement(internalPlacement),\n }),\n showHandleHighlight && styles.handleHighlight,\n ),\n }\n : {}\n }\n style={{\n ...cssVariables,\n opacity: isPending ? 0.95 : 1,\n transition: state.isResizing ? 'unset' : undefined,\n ...style,\n }}\n onResize={handleResize}\n onResizeStart={handleResizeStart}\n onResizeStop={handleResizeStop}\n >\n {children}\n </Resizable>\n ),\n [\n sizeProps,\n styles.panel,\n classNames?.content,\n canResizing,\n resizing,\n internalPlacement,\n handleVariants,\n showHandleHighlight,\n styles.handleHighlight,\n handleResize,\n handleResizeStart,\n handleResizeStop,\n state.isResizing,\n isPending,\n style,\n children,\n cx,\n ],\n );\n\n // For fullscreen mode, return a simpler layout\n if (fullscreen) {\n return (\n <div className={cx(styles.fullscreen, className)} style={cssVariables}>\n {children}\n </div>\n );\n }\n\n return (\n <aside\n dir={dir}\n ref={ref}\n style={cssVariables}\n className={cx(\n panelVariants({\n isExpand,\n mode,\n placement: internalPlacement,\n showBorder,\n }),\n className,\n )}\n >\n {expandable && state.showExpand && handle}\n {destroyOnClose ? isExpand && inner : inner}\n </aside>\n );\n },\n isEqual,\n);\n\nDraggablePanel.displayName = 'DraggablePanel';\n\nexport default DraggablePanel;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DraggablePanel.mjs","names":["useControlledState"],"sources":["../../src/DraggablePanel/DraggablePanel.tsx"],"sourcesContent":["'use client';\n\nimport { useHover } from 'ahooks';\nimport { ConfigProvider } from 'antd';\nimport { cx } from 'antd-style';\nimport isEqual from 'fast-deep-equal';\nimport { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from 'lucide-react';\nimport type { Enable, NumberSize, Size } from 're-resizable';\nimport { Resizable } from 're-resizable';\nimport {\n type CSSProperties,\n memo,\n startTransition,\n use,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport useControlledState from 'use-merge-value';\n\nimport { Center } from '@/Flex';\nimport Icon from '@/Icon';\n\nimport { handleVariants, panelVariants, styles, toggleVariants } from './style';\nimport type { DraggablePanelProps } from './type';\nimport { reversePlacement } from './utils';\n\nconst ARROW_MAP = {\n bottom: ChevronUp,\n left: ChevronRight,\n right: ChevronLeft,\n top: ChevronDown,\n} as const;\n\nconst MARGIN_MAP = {\n bottom: { marginTop: 4 },\n left: { marginRight: 4 },\n right: { marginLeft: 4 },\n top: { marginBottom: 4 },\n} as const;\n\nconst DISABLED_RESIZING: Enable = {\n bottom: false,\n bottomLeft: false,\n bottomRight: false,\n left: false,\n right: false,\n top: false,\n topLeft: false,\n topRight: false,\n};\n\nconst toCssSize = (value: string | number | undefined, fallback: string) => {\n if (typeof value === 'number') return `${Math.max(value, 0)}px`;\n if (typeof value === 'string' && value.length > 0) return value;\n return fallback;\n};\n\nconst DraggablePanel = memo<DraggablePanelProps>(\n ({\n headerHeight = 0,\n fullscreen,\n maxHeight,\n pin = true,\n mode = 'fixed',\n children,\n placement = 'right',\n resize,\n style,\n showBorder = true,\n showHandleHighlight = false,\n showHandleWideArea = true,\n backgroundColor,\n size,\n stableLayout = false,\n defaultSize: customizeDefaultSize,\n minWidth,\n minHeight,\n maxWidth,\n onSizeChange,\n onSizeDragging,\n expandable = true,\n expand,\n defaultExpand = true,\n onExpandChange,\n className,\n showHandleWhenCollapsed,\n destroyOnClose,\n styles: customStyles,\n classNames,\n dir,\n }) => {\n const ref = useRef<HTMLDivElement>(null);\n const isHovering = useHover(ref);\n const isVertical = placement === 'top' || placement === 'bottom';\n const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n const resetTransitionTimeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n const resizableRef = useRef<Resizable>(null);\n const initialExpandedSizeRef = useRef<Size | undefined>(undefined);\n\n const { direction: antdDirection } = use(ConfigProvider.ConfigContext);\n const direction = dir ?? antdDirection;\n\n const internalPlacement = useMemo(() => {\n if (direction !== 'rtl') return placement;\n if (placement === 'left') return 'right';\n if (placement === 'right') return 'left';\n return placement;\n }, [direction, placement]);\n\n const cssVariables = {\n '--draggable-panel-bg': backgroundColor || '',\n '--draggable-panel-header-height': `${headerHeight}px`,\n } as Record<string, string>;\n\n const [isExpand, setIsExpand] = useControlledState(defaultExpand, {\n onChange: onExpandChange,\n value: expand,\n });\n\n const [shouldTransition, setShouldTransition] = useState(true);\n const [showExpand, setShowExpand] = useState(true);\n\n useEffect(() => {\n if (pin) return;\n\n if (hoverTimeoutRef.current) {\n clearTimeout(hoverTimeoutRef.current);\n }\n\n if (isHovering && !isExpand) {\n startTransition(() => setIsExpand(true));\n } else if (!isHovering && isExpand) {\n hoverTimeoutRef.current = setTimeout(() => {\n startTransition(() => setIsExpand(false));\n }, 150);\n }\n }, [pin, isHovering, isExpand, setIsExpand]);\n\n useEffect(() => {\n return () => {\n if (hoverTimeoutRef.current) {\n clearTimeout(hoverTimeoutRef.current);\n }\n if (resetTransitionTimeoutRef.current) {\n clearTimeout(resetTransitionTimeoutRef.current);\n }\n };\n }, []);\n\n useEffect(() => {\n initialExpandedSizeRef.current = undefined;\n }, [internalPlacement]);\n\n const reversed = reversePlacement(internalPlacement);\n const canResizing = resize !== false && isExpand;\n\n const resizing = useMemo(\n () => ({\n bottom: false,\n bottomLeft: false,\n bottomRight: false,\n left: false,\n right: false,\n top: false,\n topLeft: false,\n topRight: false,\n [reversed]: true,\n ...(resize as Enable),\n }),\n [reversed, resize],\n );\n\n const defaultSize: Size = useMemo(() => {\n if (isVertical) return { height: 180, width: '100%', ...customizeDefaultSize };\n return { height: '100%', width: 280, ...customizeDefaultSize };\n }, [isVertical, customizeDefaultSize]);\n const normalizedMaxHeight = typeof maxHeight === 'number' ? Math.max(maxHeight, 0) : undefined;\n const normalizedMaxWidth = typeof maxWidth === 'number' ? Math.max(maxWidth, 0) : undefined;\n const normalizedMinHeight = typeof minHeight === 'number' ? Math.max(minHeight, 0) : undefined;\n const normalizedMinWidth = typeof minWidth === 'number' ? Math.max(minWidth, 0) : undefined;\n\n const sizeProps = useMemo(() => {\n if (!stableLayout && !isExpand) {\n return isVertical\n ? { minHeight: 0, size: { height: 0 } }\n : { minWidth: 0, size: { width: 0 } };\n }\n\n return {\n defaultSize,\n maxHeight: normalizedMaxHeight,\n maxWidth: normalizedMaxWidth,\n minHeight: normalizedMinHeight,\n minWidth: normalizedMinWidth,\n size: size as Size,\n };\n }, [\n stableLayout,\n isExpand,\n isVertical,\n defaultSize,\n normalizedMaxHeight,\n normalizedMaxWidth,\n normalizedMinHeight,\n normalizedMinWidth,\n size,\n ]);\n\n const fallbackExpandedSize = isVertical ? '180px' : '280px';\n const controlledExpandedSize = useMemo(() => {\n const controlledSize = isVertical ? size?.height : size?.width;\n if (controlledSize === undefined) return undefined;\n return toCssSize(controlledSize, fallbackExpandedSize);\n }, [isVertical, size?.height, size?.width, fallbackExpandedSize]);\n const defaultExpandedSize = useMemo(() => {\n const initialSize = isVertical ? defaultSize.height : defaultSize.width;\n return toCssSize(initialSize, fallbackExpandedSize);\n }, [isVertical, defaultSize.height, defaultSize.width, fallbackExpandedSize]);\n const [resizedExpandedSize, setResizedExpandedSize] = useState<{\n horizontal?: string;\n vertical?: string;\n }>({});\n const expandedOuterSize =\n controlledExpandedSize ??\n (isVertical ? resizedExpandedSize.vertical : resizedExpandedSize.horizontal) ??\n defaultExpandedSize;\n\n const setExpandedMainSize = useCallback(\n (nextSize: Size) => {\n if (!stableLayout) return;\n\n const currentSize = isVertical ? nextSize.height : nextSize.width;\n if (!currentSize) return;\n\n const normalizedSize = toCssSize(currentSize, fallbackExpandedSize);\n setResizedExpandedSize((state) =>\n isVertical\n ? { ...state, vertical: normalizedSize }\n : { ...state, horizontal: normalizedSize },\n );\n },\n [fallbackExpandedSize, isVertical, stableLayout],\n );\n\n const captureInitialExpandedSize = useCallback(() => {\n if (initialExpandedSizeRef.current) return initialExpandedSizeRef.current;\n\n const rect = resizableRef.current?.resizable?.getBoundingClientRect();\n if (!rect) return undefined;\n\n const nextInitialSize = isVertical\n ? ({ height: rect.height, width: '100%' } as Size)\n : ({ height: '100%', width: rect.width } as Size);\n\n initialExpandedSizeRef.current = nextInitialSize;\n return nextInitialSize;\n }, [isVertical]);\n\n useEffect(() => {\n if (!isExpand) return;\n captureInitialExpandedSize();\n }, [captureInitialExpandedSize, isExpand]);\n\n const toggleExpand = useCallback(() => {\n if (expandable) setIsExpand(!isExpand);\n }, [expandable, isExpand, setIsExpand]);\n\n const clampResizeSize = useCallback(\n (el: HTMLElement) => {\n const rect = el.getBoundingClientRect();\n const currentMainSize = isVertical ? rect.height : rect.width;\n const minMainSize = isVertical ? normalizedMinHeight : normalizedMinWidth;\n const maxMainSize = isVertical ? normalizedMaxHeight : normalizedMaxWidth;\n\n let clampedMainSize = currentMainSize;\n if (typeof minMainSize === 'number')\n clampedMainSize = Math.max(clampedMainSize, minMainSize);\n if (typeof maxMainSize === 'number')\n clampedMainSize = Math.min(clampedMainSize, maxMainSize);\n\n if (\n !Number.isFinite(clampedMainSize) ||\n Math.abs(clampedMainSize - currentMainSize) < 0.5\n ) {\n return { height: el.style.height, width: el.style.width };\n }\n\n const width = isVertical ? el.style.width || '100%' : `${clampedMainSize}px`;\n const height = isVertical ? `${clampedMainSize}px` : el.style.height || '100%';\n resizableRef.current?.updateSize({ height, width });\n\n return { height, width };\n },\n [\n isVertical,\n normalizedMaxHeight,\n normalizedMaxWidth,\n normalizedMinHeight,\n normalizedMinWidth,\n ],\n );\n\n const handleResize = useCallback(\n (_event: unknown, _direction: unknown, el: HTMLElement, delta: NumberSize) => {\n const nextSize = clampResizeSize(el);\n setExpandedMainSize(nextSize);\n onSizeDragging?.(delta, nextSize);\n },\n [clampResizeSize, onSizeDragging, setExpandedMainSize],\n );\n\n const triggerResetWithoutTransition = useCallback(() => {\n if (resetTransitionTimeoutRef.current) {\n clearTimeout(resetTransitionTimeoutRef.current);\n }\n\n setShouldTransition(false);\n resetTransitionTimeoutRef.current = setTimeout(() => {\n setShouldTransition(true);\n }, 0);\n }, []);\n\n const handleResetSize = useCallback(() => {\n if (!canResizing) return;\n\n const resetSize = captureInitialExpandedSize();\n if (!resetSize) return;\n\n triggerResetWithoutTransition();\n\n const rect = resizableRef.current?.resizable?.getBoundingClientRect();\n const prevMainSize = rect ? (isVertical ? rect.height : rect.width) : 0;\n const resetMainSize = isVertical ? resetSize.height : resetSize.width;\n const nextMainSize = typeof resetMainSize === 'number' ? resetMainSize : prevMainSize;\n\n resizableRef.current?.updateSize(resetSize);\n setExpandedMainSize(resetSize);\n\n onSizeChange?.(\n isVertical\n ? { height: nextMainSize - prevMainSize, width: 0 }\n : { height: 0, width: nextMainSize - prevMainSize },\n resetSize,\n );\n }, [\n canResizing,\n captureInitialExpandedSize,\n isVertical,\n onSizeChange,\n setExpandedMainSize,\n triggerResetWithoutTransition,\n ]);\n\n const handleResizeStart = useCallback(\n (event: { detail?: number }) => {\n if (event.detail === 2) {\n handleResetSize();\n return false;\n }\n\n if (resetTransitionTimeoutRef.current) {\n clearTimeout(resetTransitionTimeoutRef.current);\n resetTransitionTimeoutRef.current = undefined;\n }\n\n setShouldTransition(false);\n setShowExpand(false);\n },\n [handleResetSize],\n );\n\n const handleResizeStop = useCallback(\n (_event: unknown, _direction: unknown, el: HTMLElement, delta: NumberSize) => {\n const nextSize = clampResizeSize(el);\n setExpandedMainSize(nextSize);\n setShouldTransition(true);\n setShowExpand(true);\n onSizeChange?.(delta, nextSize);\n },\n [clampResizeSize, onSizeChange, setExpandedMainSize],\n );\n\n const resizeHandleClassName = useMemo(\n () =>\n cx(handleVariants({ placement: reversed }), showHandleHighlight && styles.handleHighlight),\n [reversed, showHandleHighlight],\n );\n\n if (fullscreen) {\n return (\n <div className={cx(styles.fullscreen, className)} style={cssVariables}>\n {children}\n </div>\n );\n }\n\n const Arrow = ARROW_MAP[internalPlacement] ?? ChevronLeft;\n const stableOuterFlex = stableLayout\n ? ({\n display: 'flex',\n flexDirection: 'column',\n minHeight: 0,\n } as const)\n : {};\n\n const sidebarOuterStyle = isVertical\n ? {\n height: isExpand ? expandedOuterSize : 0,\n overflow: 'hidden',\n transition: shouldTransition ? 'height 0.2s var(--ant-motion-ease-out, ease)' : 'none',\n width: '100%',\n ...stableOuterFlex,\n }\n : {\n overflow: 'hidden',\n transition: shouldTransition ? 'width 0.2s var(--ant-motion-ease-out, ease)' : 'none',\n width: isExpand ? expandedOuterSize : 0,\n ...(stableLayout\n ? {\n ...stableOuterFlex,\n flex: 1,\n minWidth: 0,\n height: '100%',\n }\n : {}),\n };\n\n const stableInnerStyle: CSSProperties = {\n display: 'flex',\n flex: 1,\n flexDirection: 'column',\n height: '100%',\n minHeight: 0,\n minWidth: 0,\n width: '100%',\n };\n const sidebarInnerStyle: CSSProperties = stableLayout\n ? stableInnerStyle\n : isVertical\n ? { height: '100%', width: '100%' }\n : { width: '100%' };\n\n const stableAsideStyle: CSSProperties = stableLayout\n ? {\n display: 'flex',\n flexDirection: 'column',\n minHeight: 0,\n ...(mode === 'fixed' ? { height: '100%' } : {}),\n }\n : {};\n\n const stableResizableStyle: CSSProperties = {\n display: 'flex',\n flex: 1,\n flexDirection: 'column',\n height: '100%',\n minHeight: 0,\n minWidth: 0,\n width: '100%',\n };\n\n const panelNode = (!destroyOnClose || isExpand) && (\n <Resizable\n ref={resizableRef}\n {...sizeProps}\n className={cx(styles.panel, classNames?.content)}\n enable={canResizing ? (resizing as Enable) : DISABLED_RESIZING}\n handleClasses={\n canResizing\n ? {\n [reversed]: resizeHandleClassName,\n }\n : {}\n }\n style={{\n ...cssVariables,\n transition: shouldTransition ? undefined : 'none',\n ...(stableLayout ? stableResizableStyle : {}),\n ...style,\n }}\n onResize={handleResize}\n onResizeStart={handleResizeStart}\n onResizeStop={handleResizeStop}\n >\n {stableLayout ? <div style={sidebarInnerStyle}>{children}</div> : children}\n </Resizable>\n );\n\n return (\n <aside\n dir={dir}\n ref={ref}\n style={{ ...cssVariables, ...stableAsideStyle }}\n className={cx(\n panelVariants({ isExpand, mode, placement: internalPlacement, showBorder }),\n className,\n )}\n >\n {expandable && showExpand && (\n <Center\n className={toggleVariants({ placement: internalPlacement, showHandleWideArea })}\n style={{\n opacity: isExpand ? (pin ? undefined : 0) : showHandleWhenCollapsed ? 1 : 0,\n }}\n >\n <Center\n className={classNames?.handle}\n style={customStyles?.handle}\n onClick={toggleExpand}\n >\n <Icon\n className={styles.handlerIcon}\n icon={Arrow}\n size={16}\n style={{\n ...MARGIN_MAP[internalPlacement],\n transform: `rotate(${isExpand ? 180 : 0}deg)`,\n transition: 'transform 0.3s ease',\n }}\n />\n </Center>\n </Center>\n )}\n {stableLayout ? <div style={sidebarOuterStyle}>{panelNode}</div> : panelNode}\n </aside>\n );\n },\n isEqual,\n);\n\nDraggablePanel.displayName = 'DraggablePanel';\n\nexport default DraggablePanel;\n"],"mappings":"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"}
@@ -38,6 +38,11 @@ interface DraggablePanelProps extends DivProps {
38
38
  showHandleWhenCollapsed?: boolean;
39
39
  showHandleWideArea?: boolean;
40
40
  size?: Partial<Size>;
41
+ /**
42
+ * Use two-layer container layout to keep content layout stable when collapsed/expanded.
43
+ * @default false
44
+ */
45
+ stableLayout?: boolean;
41
46
  styles?: {
42
47
  content?: CSSProperties;
43
48
  handle?: CSSProperties;
@@ -65,7 +65,6 @@ const Modal$1 = memo(({ panelRef, allowFullscreen, children, title = " ", classN
65
65
  theme: { token: { colorBgElevated: cssVar.colorBgContainer } },
66
66
  children: /* @__PURE__ */ jsx(Modal, {
67
67
  closable: true,
68
- maskClosable: true,
69
68
  cancelText,
70
69
  className: cx(styles.content, className),
71
70
  closeIcon: /* @__PURE__ */ jsx(Icon, {
@@ -75,6 +74,7 @@ const Modal$1 = memo(({ panelRef, allowFullscreen, children, title = " ", classN
75
74
  confirmLoading,
76
75
  destroyOnHidden,
77
76
  footer: hideFooter ? null : footer,
77
+ mask: { closable: true },
78
78
  okButtonProps,
79
79
  okText,
80
80
  open,
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.mjs","names":["Modal","AntModal"],"sources":["../../src/Modal/Modal.tsx"],"sourcesContent":["'use client';\n\nimport { Button, ConfigProvider, Drawer, Modal as AntModal } from 'antd';\nimport { cssVar, cx, useResponsive } from 'antd-style';\nimport { Maximize2, Minimize2, X } from 'lucide-react';\nimport { memo, type ReactNode, useState } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport Icon from '@/Icon';\n\nimport { styles } from './style';\nimport { type ModalProps } from './type';\n\nconst Modal = memo<ModalProps>(\n ({\n panelRef,\n allowFullscreen,\n children,\n title = ' ',\n className,\n classNames,\n width = 700,\n onCancel,\n open,\n destroyOnHidden,\n paddings,\n height = '75dvh',\n enableResponsive = true,\n footer,\n styles: customStyles,\n okText,\n onOk,\n cancelText,\n okButtonProps,\n cancelButtonProps,\n confirmLoading,\n ...rest\n }) => {\n const [fullscreen, setFullscreen] = useState(false);\n const { mobile } = useResponsive();\n const hideFooter = footer === false || footer === null;\n if (enableResponsive && mobile)\n return (\n <ConfigProvider\n theme={{\n token: {\n colorBgElevated: cssVar.colorBgContainer,\n },\n }}\n >\n <Drawer\n className={cx(styles.drawerContent, className)}\n closeIcon={<ActionIcon icon={X} />}\n destroyOnHidden={destroyOnHidden}\n height={fullscreen ? 'calc(100% - env(safe-area-inset-top))' : height}\n open={open}\n panelRef={panelRef}\n placement={'bottom'}\n title={title}\n classNames={\n typeof classNames === 'function'\n ? classNames\n : {\n ...classNames,\n wrapper: cx(styles.wrap, classNames?.wrapper),\n }\n }\n extra={\n allowFullscreen && (\n <ActionIcon\n icon={fullscreen ? Minimize2 : Maximize2}\n onClick={() => setFullscreen(!fullscreen)}\n />\n )\n }\n footer={\n hideFooter\n ? null\n : (footer as ReactNode) || (\n <>\n <Button\n color={'default'}\n variant={'filled'}\n onClick={onCancel as any}\n {...cancelButtonProps}\n >\n {cancelText || 'Cancel'}\n </Button>\n <Button\n loading={confirmLoading}\n type=\"primary\"\n onClick={onOk as any}\n {...okButtonProps}\n style={{\n marginInlineStart: 8,\n ...okButtonProps?.style,\n }}\n >\n {okText || 'OK'}\n </Button>\n </>\n )\n }\n styles={\n typeof customStyles === 'function'\n ? customStyles\n : {\n ...customStyles,\n body: {\n paddingBlock: `16px ${footer ? 0 : '16px'}`,\n paddingInline: paddings?.desktop ?? 16,\n ...customStyles?.body,\n },\n }\n }\n onClose={onCancel as any}\n {...rest}\n >\n {children}\n </Drawer>\n </ConfigProvider>\n );\n\n return (\n <ConfigProvider\n theme={{\n token: {\n colorBgElevated: cssVar.colorBgContainer,\n },\n }}\n >\n <AntModal\n closable\n maskClosable\n cancelText={cancelText}\n className={cx(styles.content, className)}\n closeIcon={<Icon icon={X} size={20} />}\n confirmLoading={confirmLoading}\n destroyOnHidden={destroyOnHidden}\n footer={hideFooter ? null : footer}\n okButtonProps={okButtonProps}\n okText={okText}\n open={open}\n panelRef={panelRef}\n title={title}\n width={width}\n cancelButtonProps={{\n color: 'default',\n variant: 'filled',\n ...cancelButtonProps,\n }}\n classNames={\n typeof classNames === 'function'\n ? classNames\n : {\n ...classNames,\n wrapper: cx(styles.wrap, classNames?.wrapper),\n }\n }\n styles={\n typeof customStyles === 'function'\n ? customStyles\n : {\n ...customStyles,\n body: {\n maxHeight: height,\n overflow: 'hidden auto',\n paddingBlock: `0 ${footer === null ? '16px' : 0}`,\n paddingInline: paddings?.desktop ?? 16,\n ...customStyles?.body,\n },\n }\n }\n onCancel={onCancel}\n onOk={onOk}\n {...rest}\n >\n {children}\n </AntModal>\n </ConfigProvider>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"mappings":";;;;;;;;;;AAaA,MAAMA,UAAQ,MACX,EACC,UACA,iBACA,UACA,QAAQ,KACR,WACA,YACA,QAAQ,KACR,UACA,MACA,iBACA,UACA,SAAS,SACT,mBAAmB,MACnB,QACA,QAAQ,cACR,QACA,MACA,YACA,eACA,mBACA,gBACA,GAAG,WACC;CACJ,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,EAAE,WAAW,eAAe;CAClC,MAAM,aAAa,WAAW,SAAS,WAAW;AAClD,KAAI,oBAAoB,OACtB,QACE,oBAAC,gBAAD;EACE,OAAO,EACL,OAAO,EACL,iBAAiB,OAAO,kBACzB,EACF;YAED,oBAAC,QAAD;GACE,WAAW,GAAG,OAAO,eAAe,UAAU;GAC9C,WAAW,oBAAC,YAAD,EAAY,MAAM,GAAK,CAAA;GACjB;GACjB,QAAQ,aAAa,0CAA0C;GACzD;GACI;GACV,WAAW;GACJ;GACP,YACE,OAAO,eAAe,aAClB,aACA;IACE,GAAG;IACH,SAAS,GAAG,OAAO,MAAM,YAAY,QAAQ;IAC9C;GAEP,OACE,mBACE,oBAAC,YAAD;IACE,MAAM,aAAa,YAAY;IAC/B,eAAe,cAAc,CAAC,WAAW;IACzC,CAAA;GAGN,QACE,aACI,OACC,UACC,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,QAAD;IACE,OAAO;IACP,SAAS;IACT,SAAS;IACT,GAAI;cAEH,cAAc;IACR,CAAA,EACT,oBAAC,QAAD;IACE,SAAS;IACT,MAAK;IACL,SAAS;IACT,GAAI;IACJ,OAAO;KACL,mBAAmB;KACnB,GAAG,eAAe;KACnB;cAEA,UAAU;IACJ,CAAA,CACR,EAAA,CAAA;GAGX,QACE,OAAO,iBAAiB,aACpB,eACA;IACE,GAAG;IACH,MAAM;KACJ,cAAc,QAAQ,SAAS,IAAI;KACnC,eAAe,UAAU,WAAW;KACpC,GAAG,cAAc;KAClB;IACF;GAEP,SAAS;GACT,GAAI;GAEH;GACM,CAAA;EACM,CAAA;AAGrB,QACE,oBAAC,gBAAD;EACE,OAAO,EACL,OAAO,EACL,iBAAiB,OAAO,kBACzB,EACF;YAED,oBAACC,OAAD;GACE,UAAA;GACA,cAAA;GACY;GACZ,WAAW,GAAG,OAAO,SAAS,UAAU;GACxC,WAAW,oBAAC,MAAD;IAAM,MAAM;IAAG,MAAM;IAAM,CAAA;GACtB;GACC;GACjB,QAAQ,aAAa,OAAO;GACb;GACP;GACF;GACI;GACH;GACA;GACP,mBAAmB;IACjB,OAAO;IACP,SAAS;IACT,GAAG;IACJ;GACD,YACE,OAAO,eAAe,aAClB,aACA;IACE,GAAG;IACH,SAAS,GAAG,OAAO,MAAM,YAAY,QAAQ;IAC9C;GAEP,QACE,OAAO,iBAAiB,aACpB,eACA;IACE,GAAG;IACH,MAAM;KACJ,WAAW;KACX,UAAU;KACV,cAAc,KAAK,WAAW,OAAO,SAAS;KAC9C,eAAe,UAAU,WAAW;KACpC,GAAG,cAAc;KAClB;IACF;GAEG;GACJ;GACN,GAAI;GAEH;GACQ,CAAA;EACI,CAAA;EAGtB;AAED,QAAM,cAAc"}
1
+ {"version":3,"file":"Modal.mjs","names":["Modal","AntModal"],"sources":["../../src/Modal/Modal.tsx"],"sourcesContent":["'use client';\n\nimport { Button, ConfigProvider, Drawer, Modal as AntModal } from 'antd';\nimport { cssVar, cx, useResponsive } from 'antd-style';\nimport { Maximize2, Minimize2, X } from 'lucide-react';\nimport { memo, type ReactNode, useState } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport Icon from '@/Icon';\n\nimport { styles } from './style';\nimport { type ModalProps } from './type';\n\nconst Modal = memo<ModalProps>(\n ({\n panelRef,\n allowFullscreen,\n children,\n title = ' ',\n className,\n classNames,\n width = 700,\n onCancel,\n open,\n destroyOnHidden,\n paddings,\n height = '75dvh',\n enableResponsive = true,\n footer,\n styles: customStyles,\n okText,\n onOk,\n cancelText,\n okButtonProps,\n cancelButtonProps,\n confirmLoading,\n ...rest\n }) => {\n const [fullscreen, setFullscreen] = useState(false);\n const { mobile } = useResponsive();\n const hideFooter = footer === false || footer === null;\n if (enableResponsive && mobile)\n return (\n <ConfigProvider\n theme={{\n token: {\n colorBgElevated: cssVar.colorBgContainer,\n },\n }}\n >\n <Drawer\n className={cx(styles.drawerContent, className)}\n closeIcon={<ActionIcon icon={X} />}\n destroyOnHidden={destroyOnHidden}\n height={fullscreen ? 'calc(100% - env(safe-area-inset-top))' : height}\n open={open}\n panelRef={panelRef}\n placement={'bottom'}\n title={title}\n classNames={\n typeof classNames === 'function'\n ? classNames\n : {\n ...classNames,\n wrapper: cx(styles.wrap, classNames?.wrapper),\n }\n }\n extra={\n allowFullscreen && (\n <ActionIcon\n icon={fullscreen ? Minimize2 : Maximize2}\n onClick={() => setFullscreen(!fullscreen)}\n />\n )\n }\n footer={\n hideFooter\n ? null\n : (footer as ReactNode) || (\n <>\n <Button\n color={'default'}\n variant={'filled'}\n onClick={onCancel as any}\n {...cancelButtonProps}\n >\n {cancelText || 'Cancel'}\n </Button>\n <Button\n loading={confirmLoading}\n type=\"primary\"\n onClick={onOk as any}\n {...okButtonProps}\n style={{\n marginInlineStart: 8,\n ...okButtonProps?.style,\n }}\n >\n {okText || 'OK'}\n </Button>\n </>\n )\n }\n styles={\n typeof customStyles === 'function'\n ? customStyles\n : {\n ...customStyles,\n body: {\n paddingBlock: `16px ${footer ? 0 : '16px'}`,\n paddingInline: paddings?.desktop ?? 16,\n ...customStyles?.body,\n },\n }\n }\n onClose={onCancel as any}\n {...rest}\n >\n {children}\n </Drawer>\n </ConfigProvider>\n );\n\n return (\n <ConfigProvider\n theme={{\n token: {\n colorBgElevated: cssVar.colorBgContainer,\n },\n }}\n >\n <AntModal\n closable\n cancelText={cancelText}\n className={cx(styles.content, className)}\n closeIcon={<Icon icon={X} size={20} />}\n confirmLoading={confirmLoading}\n destroyOnHidden={destroyOnHidden}\n footer={hideFooter ? null : footer}\n mask={{ closable: true }}\n okButtonProps={okButtonProps}\n okText={okText}\n open={open}\n panelRef={panelRef}\n title={title}\n width={width}\n cancelButtonProps={{\n color: 'default',\n variant: 'filled',\n ...cancelButtonProps,\n }}\n classNames={\n typeof classNames === 'function'\n ? classNames\n : {\n ...classNames,\n wrapper: cx(styles.wrap, classNames?.wrapper),\n }\n }\n styles={\n typeof customStyles === 'function'\n ? customStyles\n : {\n ...customStyles,\n body: {\n maxHeight: height,\n overflow: 'hidden auto',\n paddingBlock: `0 ${footer === null ? '16px' : 0}`,\n paddingInline: paddings?.desktop ?? 16,\n ...customStyles?.body,\n },\n }\n }\n onCancel={onCancel}\n onOk={onOk}\n {...rest}\n >\n {children}\n </AntModal>\n </ConfigProvider>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"mappings":";;;;;;;;;;AAaA,MAAMA,UAAQ,MACX,EACC,UACA,iBACA,UACA,QAAQ,KACR,WACA,YACA,QAAQ,KACR,UACA,MACA,iBACA,UACA,SAAS,SACT,mBAAmB,MACnB,QACA,QAAQ,cACR,QACA,MACA,YACA,eACA,mBACA,gBACA,GAAG,WACC;CACJ,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,EAAE,WAAW,eAAe;CAClC,MAAM,aAAa,WAAW,SAAS,WAAW;AAClD,KAAI,oBAAoB,OACtB,QACE,oBAAC,gBAAD;EACE,OAAO,EACL,OAAO,EACL,iBAAiB,OAAO,kBACzB,EACF;YAED,oBAAC,QAAD;GACE,WAAW,GAAG,OAAO,eAAe,UAAU;GAC9C,WAAW,oBAAC,YAAD,EAAY,MAAM,GAAK,CAAA;GACjB;GACjB,QAAQ,aAAa,0CAA0C;GACzD;GACI;GACV,WAAW;GACJ;GACP,YACE,OAAO,eAAe,aAClB,aACA;IACE,GAAG;IACH,SAAS,GAAG,OAAO,MAAM,YAAY,QAAQ;IAC9C;GAEP,OACE,mBACE,oBAAC,YAAD;IACE,MAAM,aAAa,YAAY;IAC/B,eAAe,cAAc,CAAC,WAAW;IACzC,CAAA;GAGN,QACE,aACI,OACC,UACC,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,QAAD;IACE,OAAO;IACP,SAAS;IACT,SAAS;IACT,GAAI;cAEH,cAAc;IACR,CAAA,EACT,oBAAC,QAAD;IACE,SAAS;IACT,MAAK;IACL,SAAS;IACT,GAAI;IACJ,OAAO;KACL,mBAAmB;KACnB,GAAG,eAAe;KACnB;cAEA,UAAU;IACJ,CAAA,CACR,EAAA,CAAA;GAGX,QACE,OAAO,iBAAiB,aACpB,eACA;IACE,GAAG;IACH,MAAM;KACJ,cAAc,QAAQ,SAAS,IAAI;KACnC,eAAe,UAAU,WAAW;KACpC,GAAG,cAAc;KAClB;IACF;GAEP,SAAS;GACT,GAAI;GAEH;GACM,CAAA;EACM,CAAA;AAGrB,QACE,oBAAC,gBAAD;EACE,OAAO,EACL,OAAO,EACL,iBAAiB,OAAO,kBACzB,EACF;YAED,oBAACC,OAAD;GACE,UAAA;GACY;GACZ,WAAW,GAAG,OAAO,SAAS,UAAU;GACxC,WAAW,oBAAC,MAAD;IAAM,MAAM;IAAG,MAAM;IAAM,CAAA;GACtB;GACC;GACjB,QAAQ,aAAa,OAAO;GAC5B,MAAM,EAAE,UAAU,MAAM;GACT;GACP;GACF;GACI;GACH;GACA;GACP,mBAAmB;IACjB,OAAO;IACP,SAAS;IACT,GAAG;IACJ;GACD,YACE,OAAO,eAAe,aAClB,aACA;IACE,GAAG;IACH,SAAS,GAAG,OAAO,MAAM,YAAY,QAAQ;IAC9C;GAEP,QACE,OAAO,iBAAiB,aACpB,eACA;IACE,GAAG;IACH,MAAM;KACJ,WAAW;KACX,UAAU;KACV,cAAc,KAAK,WAAW,OAAO,SAAS;KAC9C,eAAe,UAAU,WAAW;KACpC,GAAG,cAAc;KAClB;IACF;GAEG;GACJ;GACN,GAAI;GAEH;GACQ,CAAA;EACI,CAAA;EAGtB;AAED,QAAM,cAAc"}
@@ -12,7 +12,7 @@ const ModalStackItem = memo(({ id, props, onClose, onUpdate, onDestroy }) => {
12
12
  const stableAfterOpenChange = useEventCallback(afterOpenChange ?? noop);
13
13
  const stableOnCancel = useEventCallback(onCancel ?? noop);
14
14
  const close = useEventCallback(() => onClose(id));
15
- const setCanDismissByClickOutside = useEventCallback((value) => onUpdate(id, { maskClosable: value }));
15
+ const setCanDismissByClickOutside = useEventCallback((value) => onUpdate(id, { mask: { closable: value } }));
16
16
  const stableContextValue = useMemo(() => ({
17
17
  close,
18
18
  setCanDismissByClickOutside
@@ -1 +1 @@
1
- {"version":3,"file":"ModalStackItem.mjs","names":[],"sources":["../../src/Modal/ModalStackItem.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useMemo } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport Modal from './Modal';\nimport { ModalProvider } from './ModalProvider';\nimport type { ImperativeModalProps } from './type';\n\nexport type ModalStackItemProps = {\n id: string;\n onClose: (id: string) => void;\n onDestroy: (id: string) => void;\n onUpdate: (id: string, nextProps: Partial<ImperativeModalProps>) => void;\n props: ImperativeModalProps;\n};\n\nconst noop = () => {};\nexport const ModalStackItem = memo(\n ({ id, props, onClose, onUpdate, onDestroy }: ModalStackItemProps) => {\n const { afterClose, afterOpenChange, children, onCancel, open, ...rest } = props;\n const stableAfterClose = useEventCallback(afterClose ?? noop);\n const stableAfterOpenChange = useEventCallback(afterOpenChange ?? noop);\n const stableOnCancel = useEventCallback(onCancel ?? noop);\n const close = useEventCallback(() => onClose(id));\n const setCanDismissByClickOutside = useEventCallback((value: boolean) =>\n onUpdate(id, { maskClosable: value }),\n );\n const stableContextValue = useMemo(\n () => ({ close, setCanDismissByClickOutside }),\n [close, setCanDismissByClickOutside],\n );\n\n return (\n <Modal\n {...rest}\n open={open ?? true}\n afterClose={useCallback(() => {\n stableAfterClose?.();\n onDestroy(id);\n }, [stableAfterClose, onDestroy, id])}\n afterOpenChange={useCallback(\n (nextOpen: boolean) => {\n stableAfterOpenChange?.(nextOpen);\n if (!nextOpen) onDestroy(id);\n },\n [stableAfterOpenChange, onDestroy, id],\n )}\n onCancel={useCallback(\n (e: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLElement>) => {\n stableOnCancel?.(e);\n close();\n },\n [stableOnCancel, close],\n )}\n >\n <ModalProvider value={stableContextValue}>{children}</ModalProvider>\n </Modal>\n );\n },\n);\n\nModalStackItem.displayName = 'ModalStackItem';\n"],"mappings":";;;;;;;AAkBA,MAAM,aAAa;AACnB,MAAa,iBAAiB,MAC3B,EAAE,IAAI,OAAO,SAAS,UAAU,gBAAqC;CACpE,MAAM,EAAE,YAAY,iBAAiB,UAAU,UAAU,MAAM,GAAG,SAAS;CAC3E,MAAM,mBAAmB,iBAAiB,cAAc,KAAK;CAC7D,MAAM,wBAAwB,iBAAiB,mBAAmB,KAAK;CACvE,MAAM,iBAAiB,iBAAiB,YAAY,KAAK;CACzD,MAAM,QAAQ,uBAAuB,QAAQ,GAAG,CAAC;CACjD,MAAM,8BAA8B,kBAAkB,UACpD,SAAS,IAAI,EAAE,cAAc,OAAO,CAAC,CACtC;CACD,MAAM,qBAAqB,eAClB;EAAE;EAAO;EAA6B,GAC7C,CAAC,OAAO,4BAA4B,CACrC;AAED,QACE,oBAAC,OAAD;EACE,GAAI;EACJ,MAAM,QAAQ;EACd,YAAY,kBAAkB;AAC5B,uBAAoB;AACpB,aAAU,GAAG;KACZ;GAAC;GAAkB;GAAW;GAAG,CAAC;EACrC,iBAAiB,aACd,aAAsB;AACrB,2BAAwB,SAAS;AACjC,OAAI,CAAC,SAAU,WAAU,GAAG;KAE9B;GAAC;GAAuB;GAAW;GAAG,CACvC;EACD,UAAU,aACP,MAA8E;AAC7E,oBAAiB,EAAE;AACnB,UAAO;KAET,CAAC,gBAAgB,MAAM,CACxB;YAED,oBAAC,eAAD;GAAe,OAAO;GAAqB;GAAyB,CAAA;EAC9D,CAAA;EAGb;AAED,eAAe,cAAc"}
1
+ {"version":3,"file":"ModalStackItem.mjs","names":[],"sources":["../../src/Modal/ModalStackItem.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useMemo } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport Modal from './Modal';\nimport { ModalProvider } from './ModalProvider';\nimport type { ImperativeModalProps } from './type';\n\nexport type ModalStackItemProps = {\n id: string;\n onClose: (id: string) => void;\n onDestroy: (id: string) => void;\n onUpdate: (id: string, nextProps: Partial<ImperativeModalProps>) => void;\n props: ImperativeModalProps;\n};\n\nconst noop = () => {};\nexport const ModalStackItem = memo(\n ({ id, props, onClose, onUpdate, onDestroy }: ModalStackItemProps) => {\n const { afterClose, afterOpenChange, children, onCancel, open, ...rest } = props;\n const stableAfterClose = useEventCallback(afterClose ?? noop);\n const stableAfterOpenChange = useEventCallback(afterOpenChange ?? noop);\n const stableOnCancel = useEventCallback(onCancel ?? noop);\n const close = useEventCallback(() => onClose(id));\n const setCanDismissByClickOutside = useEventCallback((value: boolean) =>\n onUpdate(id, { mask: { closable: value } }),\n );\n const stableContextValue = useMemo(\n () => ({ close, setCanDismissByClickOutside }),\n [close, setCanDismissByClickOutside],\n );\n\n return (\n <Modal\n {...rest}\n open={open ?? true}\n afterClose={useCallback(() => {\n stableAfterClose?.();\n onDestroy(id);\n }, [stableAfterClose, onDestroy, id])}\n afterOpenChange={useCallback(\n (nextOpen: boolean) => {\n stableAfterOpenChange?.(nextOpen);\n if (!nextOpen) onDestroy(id);\n },\n [stableAfterOpenChange, onDestroy, id],\n )}\n onCancel={useCallback(\n (e: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLElement>) => {\n stableOnCancel?.(e);\n close();\n },\n [stableOnCancel, close],\n )}\n >\n <ModalProvider value={stableContextValue}>{children}</ModalProvider>\n </Modal>\n );\n },\n);\n\nModalStackItem.displayName = 'ModalStackItem';\n"],"mappings":";;;;;;;AAkBA,MAAM,aAAa;AACnB,MAAa,iBAAiB,MAC3B,EAAE,IAAI,OAAO,SAAS,UAAU,gBAAqC;CACpE,MAAM,EAAE,YAAY,iBAAiB,UAAU,UAAU,MAAM,GAAG,SAAS;CAC3E,MAAM,mBAAmB,iBAAiB,cAAc,KAAK;CAC7D,MAAM,wBAAwB,iBAAiB,mBAAmB,KAAK;CACvE,MAAM,iBAAiB,iBAAiB,YAAY,KAAK;CACzD,MAAM,QAAQ,uBAAuB,QAAQ,GAAG,CAAC;CACjD,MAAM,8BAA8B,kBAAkB,UACpD,SAAS,IAAI,EAAE,MAAM,EAAE,UAAU,OAAO,EAAE,CAAC,CAC5C;CACD,MAAM,qBAAqB,eAClB;EAAE;EAAO;EAA6B,GAC7C,CAAC,OAAO,4BAA4B,CACrC;AAED,QACE,oBAAC,OAAD;EACE,GAAI;EACJ,MAAM,QAAQ;EACd,YAAY,kBAAkB;AAC5B,uBAAoB;AACpB,aAAU,GAAG;KACZ;GAAC;GAAkB;GAAW;GAAG,CAAC;EACrC,iBAAiB,aACd,aAAsB;AACrB,2BAAwB,SAAS;AACjC,OAAI,CAAC,SAAU,WAAU,GAAG;KAE9B;GAAC;GAAuB;GAAW;GAAG,CACvC;EACD,UAAU,aACP,MAA8E;AAC7E,oBAAiB,EAAE;AACnB,UAAO;KAET,CAAC,gBAAgB,MAAM,CACxB;YAED,oBAAC,eAAD;GAAe,OAAO;GAAqB;GAAyB,CAAA;EAC9D,CAAA;EAGb;AAED,eAAe,cAAc"}
@@ -8,7 +8,7 @@ const RawModalStackItem = memo(({ component: Component, id, onClose, onUpdate, o
8
8
  const stableOnClose = useEventCallback(onClose);
9
9
  const close = useEventCallback(() => stableOnClose(id));
10
10
  const setCanDismissByClickOutside = useEventCallback((value) => {
11
- onUpdate(id, { maskClosable: value });
11
+ onUpdate(id, { mask: { closable: value } });
12
12
  });
13
13
  const contextValue = useMemo(() => ({
14
14
  close,
@@ -1 +1 @@
1
- {"version":3,"file":"RawModalStackItem.mjs","names":[],"sources":["../../src/Modal/RawModalStackItem.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentType } from 'react';\nimport { memo, useMemo } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport { ModalProvider } from './ModalProvider';\nimport type { ModalContextValue, RawModalOptions } from './type';\n\nexport type RawModalStackItemProps = {\n component: ComponentType<any>;\n id: string;\n onClose: (id: string) => void;\n onUpdate: (id: string, nextProps: Record<string, unknown>) => void;\n open: boolean;\n options?: RawModalOptions<PropertyKey, PropertyKey>;\n props: Record<string, unknown>;\n};\n\nexport const RawModalStackItem = memo(\n ({\n component: Component,\n id,\n onClose,\n onUpdate,\n open,\n options,\n props,\n }: RawModalStackItemProps) => {\n const stableOnClose = useEventCallback(onClose);\n const close = useEventCallback(() => stableOnClose(id));\n\n const setCanDismissByClickOutside = useEventCallback((value: boolean) => {\n onUpdate(id, { maskClosable: value });\n });\n const contextValue: ModalContextValue = useMemo(\n () => ({ close, setCanDismissByClickOutside }),\n [close, setCanDismissByClickOutside],\n );\n const openKey = options?.openKey ?? 'open';\n const onCloseKey = options?.onCloseKey ?? 'onClose';\n const injectedProps = {\n ...props,\n [onCloseKey]: close,\n [openKey]: open,\n };\n\n return (\n <ModalProvider value={contextValue}>\n <Component {...injectedProps} />\n </ModalProvider>\n );\n },\n);\n\nRawModalStackItem.displayName = 'RawModalStackItem';\n"],"mappings":";;;;;;AAoBA,MAAa,oBAAoB,MAC9B,EACC,WAAW,WACX,IACA,SACA,UACA,MACA,SACA,YAC4B;CAC5B,MAAM,gBAAgB,iBAAiB,QAAQ;CAC/C,MAAM,QAAQ,uBAAuB,cAAc,GAAG,CAAC;CAEvD,MAAM,8BAA8B,kBAAkB,UAAmB;AACvE,WAAS,IAAI,EAAE,cAAc,OAAO,CAAC;GACrC;CACF,MAAM,eAAkC,eAC/B;EAAE;EAAO;EAA6B,GAC7C,CAAC,OAAO,4BAA4B,CACrC;CACD,MAAM,UAAU,SAAS,WAAW;CACpC,MAAM,aAAa,SAAS,cAAc;AAO1C,QACE,oBAAC,eAAD;EAAe,OAAO;YACpB,oBAAC,WAAD;GAPF,GAAG;IACF,aAAa;IACb,UAAU;GAKuB,CAAA;EAClB,CAAA;EAGrB;AAED,kBAAkB,cAAc"}
1
+ {"version":3,"file":"RawModalStackItem.mjs","names":[],"sources":["../../src/Modal/RawModalStackItem.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentType } from 'react';\nimport { memo, useMemo } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport { ModalProvider } from './ModalProvider';\nimport type { ModalContextValue, RawModalOptions } from './type';\n\nexport type RawModalStackItemProps = {\n component: ComponentType<any>;\n id: string;\n onClose: (id: string) => void;\n onUpdate: (id: string, nextProps: Record<string, unknown>) => void;\n open: boolean;\n options?: RawModalOptions<PropertyKey, PropertyKey>;\n props: Record<string, unknown>;\n};\n\nexport const RawModalStackItem = memo(\n ({\n component: Component,\n id,\n onClose,\n onUpdate,\n open,\n options,\n props,\n }: RawModalStackItemProps) => {\n const stableOnClose = useEventCallback(onClose);\n const close = useEventCallback(() => stableOnClose(id));\n\n const setCanDismissByClickOutside = useEventCallback((value: boolean) => {\n onUpdate(id, { mask: { closable: value } });\n });\n const contextValue: ModalContextValue = useMemo(\n () => ({ close, setCanDismissByClickOutside }),\n [close, setCanDismissByClickOutside],\n );\n const openKey = options?.openKey ?? 'open';\n const onCloseKey = options?.onCloseKey ?? 'onClose';\n const injectedProps = {\n ...props,\n [onCloseKey]: close,\n [openKey]: open,\n };\n\n return (\n <ModalProvider value={contextValue}>\n <Component {...injectedProps} />\n </ModalProvider>\n );\n },\n);\n\nRawModalStackItem.displayName = 'RawModalStackItem';\n"],"mappings":";;;;;;AAoBA,MAAa,oBAAoB,MAC9B,EACC,WAAW,WACX,IACA,SACA,UACA,MACA,SACA,YAC4B;CAC5B,MAAM,gBAAgB,iBAAiB,QAAQ;CAC/C,MAAM,QAAQ,uBAAuB,cAAc,GAAG,CAAC;CAEvD,MAAM,8BAA8B,kBAAkB,UAAmB;AACvE,WAAS,IAAI,EAAE,MAAM,EAAE,UAAU,OAAO,EAAE,CAAC;GAC3C;CACF,MAAM,eAAkC,eAC/B;EAAE;EAAO;EAA6B,GAC7C,CAAC,OAAO,4BAA4B,CACrC;CACD,MAAM,UAAU,SAAS,WAAW;CACpC,MAAM,aAAa,SAAS,cAAc;AAO1C,QACE,oBAAC,eAAD;EAAe,OAAO;YACpB,oBAAC,WAAD;GAPF,GAAG;IACF,aAAa;IACb,UAAU;GAKuB,CAAA;EAClB,CAAA;EAGrB;AAED,kBAAkB,cAAc"}
@@ -158,7 +158,7 @@ const createModal = (props) => {
158
158
  return {
159
159
  close: () => closeModal(id),
160
160
  destroy: () => destroyModal(id),
161
- setCanDismissByClickOutside: (value) => updateModal(id, { maskClosable: value }),
161
+ setCanDismissByClickOutside: (value) => updateModal(id, { mask: { closable: value } }),
162
162
  update: (nextProps) => updateModal(id, nextProps)
163
163
  };
164
164
  };
@@ -176,7 +176,7 @@ function createRawModal(component, props, options) {
176
176
  return {
177
177
  close: () => closeModal(id),
178
178
  destroy: () => destroyModal(id),
179
- setCanDismissByClickOutside: (value) => updateRawProps(id, { maskClosable: value }),
179
+ setCanDismissByClickOutside: (value) => updateRawProps(id, { mask: { closable: value } }),
180
180
  update: (nextProps) => updateRawProps(id, nextProps)
181
181
  };
182
182
  }
@@ -1 +1 @@
1
- {"version":3,"file":"imperative.mjs","names":[],"sources":["../../src/Modal/imperative.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { memo, useEffect, useSyncExternalStore } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useIsClient } from '@/hooks/useIsClient';\nimport { useAppElement } from '@/ThemeProvider';\nimport { registerDevSingleton } from '@/utils/devSingleton';\n\nimport { ModalStackItem } from './ModalStackItem';\nimport { RawModalStackItem } from './RawModalStackItem';\nimport type {\n ImperativeModalProps,\n ModalInstance,\n RawModalComponent,\n RawModalComponentProps,\n RawModalInstance,\n RawModalKeyOptions,\n RawModalOptions,\n} from './type';\n\ntype ModalStackItemBase = {\n id: string;\n};\n\ntype ModalStackItemModal = ModalStackItemBase & {\n kind: 'modal';\n props: ImperativeModalProps;\n};\n\ntype ModalStackItemRaw = ModalStackItemBase & {\n component: RawModalComponent;\n kind: 'raw';\n open: boolean;\n options?: RawModalOptions<PropertyKey, PropertyKey>;\n props: Record<string, unknown>;\n};\n\ntype TModalStackItem = ModalStackItemModal | ModalStackItemRaw;\n\ntype ModalStackProps = {\n stack: TModalStackItem[];\n};\n\nexport type ModalHostProps = {\n root?: HTMLElement | ShadowRoot | null;\n};\n\nlet modalStack: TModalStackItem[] = [];\nlet modalSeed = 0;\nconst listeners = new Set<() => void>();\nconst rawDestroyTimers = new Map<string, number>();\n\nconst notify = () => {\n listeners.forEach((listener) => listener());\n};\n\nconst subscribe = (listener: () => void) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n};\n\nconst EMPTY_STACK: TModalStackItem[] = [];\nconst getSnapshot = () => modalStack;\nconst getServerSnapshot = () => EMPTY_STACK;\n\nconst ModalPortal = ({\n children,\n root,\n}: {\n children: ReactNode;\n root?: HTMLElement | ShadowRoot | null;\n}) => {\n const appElement = useAppElement();\n const container = root ?? appElement ?? document.body;\n\n return createPortal(children, container);\n};\n\nconst updateModal = (id: string, nextProps: Partial<ImperativeModalProps>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'modal') return item;\n changed = true;\n return {\n ...item,\n props: { ...item.props, ...nextProps },\n };\n });\n\n if (changed) notify();\n};\n\nconst updateRawProps = (id: string, nextProps: Record<string, unknown>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'raw') return item;\n changed = true;\n return {\n ...item,\n props: { ...item.props, ...nextProps },\n };\n });\n\n if (changed) notify();\n};\n\nconst setRawOpen = (id: string, open: boolean) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'raw') return item;\n if (item.open === open) return item;\n changed = true;\n return { ...item, open };\n });\n\n if (open) {\n const timer = rawDestroyTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n rawDestroyTimers.delete(id);\n }\n }\n\n if (changed) notify();\n};\n\nconst closeModal = (id: string) => {\n const target = modalStack.find((item) => item.id === id);\n if (!target) return;\n\n if (target.kind === 'modal') {\n updateModal(id, { open: false });\n return;\n }\n\n setRawOpen(id, false);\n\n const shouldDestroy = target.options?.destroyOnClose ?? true;\n if (!shouldDestroy) return;\n\n const delay = target.options?.destroyDelay ?? 200;\n const existing = rawDestroyTimers.get(id);\n if (existing) clearTimeout(existing);\n const timer = window.setTimeout(() => {\n rawDestroyTimers.delete(id);\n\n destroyModal(id);\n }, delay);\n rawDestroyTimers.set(id, timer);\n};\n\nconst destroyModal = (id: string) => {\n const timer = rawDestroyTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n rawDestroyTimers.delete(id);\n }\n const nextStack = modalStack.filter((item) => item.id !== id);\n if (nextStack.length === modalStack.length) return;\n modalStack = nextStack;\n notify();\n};\n\nconst ModalStack = memo(({ stack }: ModalStackProps) => {\n const isClient = useIsClient();\n if (!isClient) return null;\n return stack.map((item) => {\n if (item.kind === 'modal') {\n return (\n <ModalStackItem\n id={item.id}\n key={item.id}\n props={item.props}\n onClose={closeModal}\n onDestroy={destroyModal}\n onUpdate={updateModal}\n />\n );\n }\n\n return (\n <RawModalStackItem\n component={item.component}\n id={item.id}\n key={item.id}\n open={item.open}\n options={item.options}\n props={item.props}\n onClose={closeModal}\n onUpdate={updateRawProps}\n />\n );\n });\n});\n\nModalStack.displayName = 'ModalStack';\n\nexport const ModalHost = ({ root }: ModalHostProps) => {\n const stack = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const isClient = useIsClient();\n\n useEffect(() => {\n if (!isClient) return;\n // Enforce singleton per portal root (dev-only).\n const scope = root ?? document.body;\n return registerDevSingleton('ModalHost', scope);\n }, [isClient, root]);\n\n if (!isClient) return null;\n if (stack.length === 0) return null;\n\n return (\n <ModalPortal root={root}>\n <ModalStack stack={stack} />\n </ModalPortal>\n );\n};\n\nexport const createModal = (props: ImperativeModalProps): ModalInstance => {\n const id = `modal-${Date.now()}-${modalSeed++}`;\n modalStack = [\n ...modalStack,\n { id, kind: 'modal', props: { ...props, open: props.open ?? true } },\n ];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateModal(id, { maskClosable: value }),\n update: (nextProps) => updateModal(id, nextProps),\n };\n};\n\nexport function createRawModal<P extends RawModalComponentProps>(\n component: RawModalComponent<P>,\n props: Omit<P, 'open' | 'onClose'>,\n options?: RawModalOptions,\n): RawModalInstance<P>;\n\nexport function createRawModal<P, OpenKey extends keyof P, CloseKey extends keyof P>(\n component: RawModalComponent<P>,\n props: Omit<P, OpenKey | CloseKey>,\n options: RawModalKeyOptions<OpenKey, CloseKey>,\n): RawModalInstance<P, OpenKey, CloseKey>;\n\nexport function createRawModal<P, OpenKey extends keyof P, CloseKey extends keyof P>(\n component: RawModalComponent<P>,\n props: Omit<P, OpenKey | CloseKey>,\n options?: RawModalOptions<OpenKey, CloseKey>,\n): RawModalInstance<P, OpenKey, CloseKey> {\n const id = `modal-${Date.now()}-${modalSeed++}`;\n modalStack = [\n ...modalStack,\n {\n component,\n id,\n kind: 'raw',\n open: true,\n options,\n props: props as Record<string, unknown>,\n },\n ];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateRawProps(id, { maskClosable: value }),\n update: (nextProps) => updateRawProps(id, nextProps as Record<string, unknown>),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"imperative.mjs","names":[],"sources":["../../src/Modal/imperative.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { memo, useEffect, useSyncExternalStore } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useIsClient } from '@/hooks/useIsClient';\nimport { useAppElement } from '@/ThemeProvider';\nimport { registerDevSingleton } from '@/utils/devSingleton';\n\nimport { ModalStackItem } from './ModalStackItem';\nimport { RawModalStackItem } from './RawModalStackItem';\nimport type {\n ImperativeModalProps,\n ModalInstance,\n RawModalComponent,\n RawModalComponentProps,\n RawModalInstance,\n RawModalKeyOptions,\n RawModalOptions,\n} from './type';\n\ntype ModalStackItemBase = {\n id: string;\n};\n\ntype ModalStackItemModal = ModalStackItemBase & {\n kind: 'modal';\n props: ImperativeModalProps;\n};\n\ntype ModalStackItemRaw = ModalStackItemBase & {\n component: RawModalComponent;\n kind: 'raw';\n open: boolean;\n options?: RawModalOptions<PropertyKey, PropertyKey>;\n props: Record<string, unknown>;\n};\n\ntype TModalStackItem = ModalStackItemModal | ModalStackItemRaw;\n\ntype ModalStackProps = {\n stack: TModalStackItem[];\n};\n\nexport type ModalHostProps = {\n root?: HTMLElement | ShadowRoot | null;\n};\n\nlet modalStack: TModalStackItem[] = [];\nlet modalSeed = 0;\nconst listeners = new Set<() => void>();\nconst rawDestroyTimers = new Map<string, number>();\n\nconst notify = () => {\n listeners.forEach((listener) => listener());\n};\n\nconst subscribe = (listener: () => void) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n};\n\nconst EMPTY_STACK: TModalStackItem[] = [];\nconst getSnapshot = () => modalStack;\nconst getServerSnapshot = () => EMPTY_STACK;\n\nconst ModalPortal = ({\n children,\n root,\n}: {\n children: ReactNode;\n root?: HTMLElement | ShadowRoot | null;\n}) => {\n const appElement = useAppElement();\n const container = root ?? appElement ?? document.body;\n\n return createPortal(children, container);\n};\n\nconst updateModal = (id: string, nextProps: Partial<ImperativeModalProps>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'modal') return item;\n changed = true;\n return {\n ...item,\n props: { ...item.props, ...nextProps },\n };\n });\n\n if (changed) notify();\n};\n\nconst updateRawProps = (id: string, nextProps: Record<string, unknown>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'raw') return item;\n changed = true;\n return {\n ...item,\n props: { ...item.props, ...nextProps },\n };\n });\n\n if (changed) notify();\n};\n\nconst setRawOpen = (id: string, open: boolean) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'raw') return item;\n if (item.open === open) return item;\n changed = true;\n return { ...item, open };\n });\n\n if (open) {\n const timer = rawDestroyTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n rawDestroyTimers.delete(id);\n }\n }\n\n if (changed) notify();\n};\n\nconst closeModal = (id: string) => {\n const target = modalStack.find((item) => item.id === id);\n if (!target) return;\n\n if (target.kind === 'modal') {\n updateModal(id, { open: false });\n return;\n }\n\n setRawOpen(id, false);\n\n const shouldDestroy = target.options?.destroyOnClose ?? true;\n if (!shouldDestroy) return;\n\n const delay = target.options?.destroyDelay ?? 200;\n const existing = rawDestroyTimers.get(id);\n if (existing) clearTimeout(existing);\n const timer = window.setTimeout(() => {\n rawDestroyTimers.delete(id);\n\n destroyModal(id);\n }, delay);\n rawDestroyTimers.set(id, timer);\n};\n\nconst destroyModal = (id: string) => {\n const timer = rawDestroyTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n rawDestroyTimers.delete(id);\n }\n const nextStack = modalStack.filter((item) => item.id !== id);\n if (nextStack.length === modalStack.length) return;\n modalStack = nextStack;\n notify();\n};\n\nconst ModalStack = memo(({ stack }: ModalStackProps) => {\n const isClient = useIsClient();\n if (!isClient) return null;\n return stack.map((item) => {\n if (item.kind === 'modal') {\n return (\n <ModalStackItem\n id={item.id}\n key={item.id}\n props={item.props}\n onClose={closeModal}\n onDestroy={destroyModal}\n onUpdate={updateModal}\n />\n );\n }\n\n return (\n <RawModalStackItem\n component={item.component}\n id={item.id}\n key={item.id}\n open={item.open}\n options={item.options}\n props={item.props}\n onClose={closeModal}\n onUpdate={updateRawProps}\n />\n );\n });\n});\n\nModalStack.displayName = 'ModalStack';\n\nexport const ModalHost = ({ root }: ModalHostProps) => {\n const stack = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const isClient = useIsClient();\n\n useEffect(() => {\n if (!isClient) return;\n // Enforce singleton per portal root (dev-only).\n const scope = root ?? document.body;\n return registerDevSingleton('ModalHost', scope);\n }, [isClient, root]);\n\n if (!isClient) return null;\n if (stack.length === 0) return null;\n\n return (\n <ModalPortal root={root}>\n <ModalStack stack={stack} />\n </ModalPortal>\n );\n};\n\nexport const createModal = (props: ImperativeModalProps): ModalInstance => {\n const id = `modal-${Date.now()}-${modalSeed++}`;\n modalStack = [\n ...modalStack,\n { id, kind: 'modal', props: { ...props, open: props.open ?? true } },\n ];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateModal(id, { mask: { closable: value } }),\n update: (nextProps) => updateModal(id, nextProps),\n };\n};\n\nexport function createRawModal<P extends RawModalComponentProps>(\n component: RawModalComponent<P>,\n props: Omit<P, 'open' | 'onClose'>,\n options?: RawModalOptions,\n): RawModalInstance<P>;\n\nexport function createRawModal<P, OpenKey extends keyof P, CloseKey extends keyof P>(\n component: RawModalComponent<P>,\n props: Omit<P, OpenKey | CloseKey>,\n options: RawModalKeyOptions<OpenKey, CloseKey>,\n): RawModalInstance<P, OpenKey, CloseKey>;\n\nexport function createRawModal<P, OpenKey extends keyof P, CloseKey extends keyof P>(\n component: RawModalComponent<P>,\n props: Omit<P, OpenKey | CloseKey>,\n options?: RawModalOptions<OpenKey, CloseKey>,\n): RawModalInstance<P, OpenKey, CloseKey> {\n const id = `modal-${Date.now()}-${modalSeed++}`;\n modalStack = [\n ...modalStack,\n {\n component,\n id,\n kind: 'raw',\n open: true,\n options,\n props: props as Record<string, unknown>,\n },\n ];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateRawProps(id, { mask: { closable: value } }),\n update: (nextProps) => updateRawProps(id, nextProps as Record<string, unknown>),\n };\n}\n"],"mappings":"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"}
@@ -17,10 +17,10 @@ const loadMermaid = () => {
17
17
  if (!mermaidPromise) mermaidPromise = import("mermaid").then((mod) => mod.default);
18
18
  return mermaidPromise;
19
19
  };
20
- const createMermaidConfig = (theme, customTheme) => ({
20
+ const createMermaidConfig = (theme, customTheme, securityLevel = "strict") => ({
21
21
  fontFamily: theme.fontFamilyCode,
22
22
  gantt: { useWidth: 1920 },
23
- securityLevel: "loose",
23
+ securityLevel,
24
24
  startOnLoad: false,
25
25
  theme: customTheme || (theme.isDarkMode ? "dark" : "neutral"),
26
26
  themeVariables: customTheme ? void 0 : {
@@ -50,10 +50,10 @@ const createMermaidConfig = (theme, customTheme) => ({
50
50
  /**
51
51
  * 验证并处理 Mermaid 图表内容 - 优化版本(移除 SWR)
52
52
  */
53
- const useMermaid = (content, { id, theme: customTheme }) => {
53
+ const useMermaid = (content, { id, theme: customTheme, securityLevel }) => {
54
54
  const theme = useTheme();
55
55
  const [data, setData] = useState("");
56
- const mermaidConfig = useMemo(() => createMermaidConfig(theme, customTheme), [
56
+ const mermaidConfig = useMemo(() => createMermaidConfig(theme, customTheme, securityLevel), [
57
57
  theme.fontFamilyCode,
58
58
  theme.isDarkMode,
59
59
  theme.colorTextDescription,
@@ -72,7 +72,8 @@ const useMermaid = (content, { id, theme: customTheme }) => {
72
72
  theme.colorSuccessBg,
73
73
  theme.colorSuccessText,
74
74
  theme.colorText,
75
- customTheme
75
+ customTheme,
76
+ securityLevel
76
77
  ]);
77
78
  const cacheKey = useMemo(() => {
78
79
  const hash = content.length < 1e4 ? content : Md5.hashStr(content);
@@ -1 +1 @@
1
- {"version":3,"file":"useMermaid.mjs","names":[],"sources":["../../src/hooks/useMermaid.ts"],"sourcesContent":["'use client';\n\nimport { useTheme } from 'antd-style';\nimport type { MermaidConfig } from 'mermaid';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Md5 } from 'ts-md5';\n\n// 缓存已验证的图表内容\nexport const MD5_LENGTH_THRESHOLD = 10_000;\n\n// Application-level cache for rendered Mermaid SVG\n// Key: cacheKey string, Value: Promise<string>\nconst mermaidCache = new Map<string, Promise<string>>();\n\n// Maximum cache size to prevent memory leaks\nconst MAX_CACHE_SIZE = 500;\n\n// Clean up old cache entries when limit is reached\nconst cleanupCache = () => {\n if (mermaidCache.size > MAX_CACHE_SIZE) {\n // Remove oldest 20% of entries\n const entriesToRemove = Math.floor(MAX_CACHE_SIZE * 0.2);\n const keysToRemove = Array.from(mermaidCache.keys()).slice(0, entriesToRemove);\n for (const key of keysToRemove) {\n mermaidCache.delete(key);\n }\n }\n};\n\n// 懒加载 mermaid 实例\nlet mermaidPromise: Promise<typeof import('mermaid').default | null> | null = null;\n\nexport const loadMermaid = (): Promise<typeof import('mermaid').default | null> => {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n if (!mermaidPromise) {\n mermaidPromise = import('mermaid').then((mod) => mod.default);\n }\n\n return mermaidPromise;\n};\n\n// Helper to create mermaid config\nexport const createMermaidConfig = (\n theme: ReturnType<typeof useTheme>,\n customTheme?: MermaidConfig['theme'],\n): MermaidConfig => ({\n fontFamily: theme.fontFamilyCode,\n gantt: {\n useWidth: 1920,\n },\n securityLevel: 'loose',\n startOnLoad: false,\n theme: customTheme || (theme.isDarkMode ? 'dark' : 'neutral'),\n themeVariables: customTheme\n ? undefined\n : {\n errorBkgColor: theme.colorTextDescription,\n errorTextColor: theme.colorTextDescription,\n fontFamily: theme.fontFamily,\n lineColor: theme.colorTextSecondary,\n mainBkg: theme.colorBgContainer,\n noteBkgColor: theme.colorInfoBg,\n noteTextColor: theme.colorInfoText,\n pie1: theme.geekblue,\n pie2: theme.colorWarning,\n pie3: theme.colorSuccess,\n pie4: theme.colorError,\n primaryBorderColor: theme.colorBorder,\n primaryColor: theme.colorBgContainer,\n primaryTextColor: theme.colorText,\n secondaryBorderColor: theme.colorInfoBorder,\n secondaryColor: theme.colorInfoBg,\n secondaryTextColor: theme.colorInfoText,\n tertiaryBorderColor: theme.colorSuccessBorder,\n tertiaryColor: theme.colorSuccessBg,\n tertiaryTextColor: theme.colorSuccessText,\n textColor: theme.colorText,\n },\n});\n\n/**\n * 验证并处理 Mermaid 图表内容 - 优化版本(移除 SWR)\n */\nexport const useMermaid = (\n content: string,\n {\n id,\n theme: customTheme,\n }: {\n id: string;\n theme?: MermaidConfig['theme'];\n },\n): string => {\n const theme = useTheme();\n const [data, setData] = useState<string>('');\n\n // 提取主题相关配置到 useMemo 中 - 只依赖实际使用的 theme 属性\n const mermaidConfig = useMemo(\n () => createMermaidConfig(theme, customTheme),\n [\n theme.fontFamilyCode,\n theme.isDarkMode,\n theme.colorTextDescription,\n theme.fontFamily,\n theme.colorTextSecondary,\n theme.colorBgContainer,\n theme.colorInfoBg,\n theme.colorInfoText,\n theme.geekblue,\n theme.colorWarning,\n theme.colorSuccess,\n theme.colorError,\n theme.colorBorder,\n theme.colorInfoBorder,\n theme.colorSuccessBorder,\n theme.colorSuccessBg,\n theme.colorSuccessText,\n theme.colorText,\n customTheme,\n ],\n );\n\n // 为长内容生成哈希键\n const cacheKey = useMemo((): string => {\n const hash = content.length < MD5_LENGTH_THRESHOLD ? content : Md5.hashStr(content);\n return [id, customTheme || (theme.isDarkMode ? 'd' : 'l'), hash].filter(Boolean).join('-');\n }, [content, id, theme.isDarkMode, customTheme]);\n\n useEffect(() => {\n // Check cache first\n const cachedPromise = mermaidCache.get(cacheKey);\n if (cachedPromise) {\n cachedPromise\n .then((svg) => {\n setData(svg);\n })\n .catch(() => {\n // Silently handle errors, fallback will be handled in the promise\n });\n return;\n }\n\n // Create new promise for rendering\n const renderPromise = (async (): Promise<string> => {\n try {\n const mermaidInstance = await loadMermaid();\n if (!mermaidInstance) return '';\n\n // 验证语法\n const isValid = await mermaidInstance.parse(content);\n\n if (isValid) {\n // 初始化并渲染\n mermaidInstance.initialize(mermaidConfig);\n const { svg } = await mermaidInstance.render(id, content);\n return svg;\n } else {\n throw new Error('Mermaid 语法无效');\n }\n } catch (error_) {\n console.error('Mermaid 解析错误:', error_);\n return '';\n }\n })();\n\n // Cache the promise\n mermaidCache.set(cacheKey, renderPromise);\n cleanupCache();\n\n // Handle promise result\n renderPromise\n .then((svg) => {\n // Only update if this is still the current cache key\n if (mermaidCache.get(cacheKey) === renderPromise) {\n setData(svg);\n }\n })\n .catch(() => {\n // Remove failed promise from cache\n if (mermaidCache.get(cacheKey) === renderPromise) {\n mermaidCache.delete(cacheKey);\n }\n });\n }, [cacheKey, content, id, mermaidConfig]);\n\n return data;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useMermaid.mjs","names":[],"sources":["../../src/hooks/useMermaid.ts"],"sourcesContent":["'use client';\n\nimport { useTheme } from 'antd-style';\nimport type { MermaidConfig } from 'mermaid';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Md5 } from 'ts-md5';\n\n// 缓存已验证的图表内容\nexport const MD5_LENGTH_THRESHOLD = 10_000;\n\n// Application-level cache for rendered Mermaid SVG\n// Key: cacheKey string, Value: Promise<string>\nconst mermaidCache = new Map<string, Promise<string>>();\n\n// Maximum cache size to prevent memory leaks\nconst MAX_CACHE_SIZE = 500;\n\n// Clean up old cache entries when limit is reached\nconst cleanupCache = () => {\n if (mermaidCache.size > MAX_CACHE_SIZE) {\n // Remove oldest 20% of entries\n const entriesToRemove = Math.floor(MAX_CACHE_SIZE * 0.2);\n const keysToRemove = Array.from(mermaidCache.keys()).slice(0, entriesToRemove);\n for (const key of keysToRemove) {\n mermaidCache.delete(key);\n }\n }\n};\n\n// 懒加载 mermaid 实例\nlet mermaidPromise: Promise<typeof import('mermaid').default | null> | null = null;\n\nexport const loadMermaid = (): Promise<typeof import('mermaid').default | null> => {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n if (!mermaidPromise) {\n mermaidPromise = import('mermaid').then((mod) => mod.default);\n }\n\n return mermaidPromise;\n};\n\n// Helper to create mermaid config\nexport const createMermaidConfig = (\n theme: ReturnType<typeof useTheme>,\n customTheme?: MermaidConfig['theme'],\n // SECURITY: Keep 'strict' as the default. Using 'loose' causes Mermaid to render\n // node labels via innerHTML, enabling XSS when diagram content is user-controlled.\n // Only pass 'loose' if your use case explicitly requires HTML labels and you control\n // the diagram source entirely.\n securityLevel: MermaidConfig['securityLevel'] = 'strict',\n): MermaidConfig => ({\n fontFamily: theme.fontFamilyCode,\n gantt: {\n useWidth: 1920,\n },\n securityLevel,\n startOnLoad: false,\n theme: customTheme || (theme.isDarkMode ? 'dark' : 'neutral'),\n themeVariables: customTheme\n ? undefined\n : {\n errorBkgColor: theme.colorTextDescription,\n errorTextColor: theme.colorTextDescription,\n fontFamily: theme.fontFamily,\n lineColor: theme.colorTextSecondary,\n mainBkg: theme.colorBgContainer,\n noteBkgColor: theme.colorInfoBg,\n noteTextColor: theme.colorInfoText,\n pie1: theme.geekblue,\n pie2: theme.colorWarning,\n pie3: theme.colorSuccess,\n pie4: theme.colorError,\n primaryBorderColor: theme.colorBorder,\n primaryColor: theme.colorBgContainer,\n primaryTextColor: theme.colorText,\n secondaryBorderColor: theme.colorInfoBorder,\n secondaryColor: theme.colorInfoBg,\n secondaryTextColor: theme.colorInfoText,\n tertiaryBorderColor: theme.colorSuccessBorder,\n tertiaryColor: theme.colorSuccessBg,\n tertiaryTextColor: theme.colorSuccessText,\n textColor: theme.colorText,\n },\n});\n\n/**\n * 验证并处理 Mermaid 图表内容 - 优化版本(移除 SWR)\n */\nexport const useMermaid = (\n content: string,\n {\n id,\n theme: customTheme,\n securityLevel,\n }: {\n id: string;\n // SECURITY: Defaults to 'strict'. Set to 'loose' only when you fully control\n // the diagram source and intentionally need HTML rendering in node labels.\n securityLevel?: MermaidConfig['securityLevel'];\n theme?: MermaidConfig['theme'];\n },\n): string => {\n const theme = useTheme();\n const [data, setData] = useState<string>('');\n\n // 提取主题相关配置到 useMemo 中 - 只依赖实际使用的 theme 属性\n const mermaidConfig = useMemo(\n () => createMermaidConfig(theme, customTheme, securityLevel),\n [\n theme.fontFamilyCode,\n theme.isDarkMode,\n theme.colorTextDescription,\n theme.fontFamily,\n theme.colorTextSecondary,\n theme.colorBgContainer,\n theme.colorInfoBg,\n theme.colorInfoText,\n theme.geekblue,\n theme.colorWarning,\n theme.colorSuccess,\n theme.colorError,\n theme.colorBorder,\n theme.colorInfoBorder,\n theme.colorSuccessBorder,\n theme.colorSuccessBg,\n theme.colorSuccessText,\n theme.colorText,\n customTheme,\n securityLevel,\n ],\n );\n\n // 为长内容生成哈希键\n const cacheKey = useMemo((): string => {\n const hash = content.length < MD5_LENGTH_THRESHOLD ? content : Md5.hashStr(content);\n return [id, customTheme || (theme.isDarkMode ? 'd' : 'l'), hash].filter(Boolean).join('-');\n }, [content, id, theme.isDarkMode, customTheme]);\n\n useEffect(() => {\n // Check cache first\n const cachedPromise = mermaidCache.get(cacheKey);\n if (cachedPromise) {\n cachedPromise\n .then((svg) => {\n setData(svg);\n })\n .catch(() => {\n // Silently handle errors, fallback will be handled in the promise\n });\n return;\n }\n\n // Create new promise for rendering\n const renderPromise = (async (): Promise<string> => {\n try {\n const mermaidInstance = await loadMermaid();\n if (!mermaidInstance) return '';\n\n // 验证语法\n const isValid = await mermaidInstance.parse(content);\n\n if (isValid) {\n // 初始化并渲染\n mermaidInstance.initialize(mermaidConfig);\n const { svg } = await mermaidInstance.render(id, content);\n return svg;\n } else {\n throw new Error('Mermaid 语法无效');\n }\n } catch (error_) {\n console.error('Mermaid 解析错误:', error_);\n return '';\n }\n })();\n\n // Cache the promise\n mermaidCache.set(cacheKey, renderPromise);\n cleanupCache();\n\n // Handle promise result\n renderPromise\n .then((svg) => {\n // Only update if this is still the current cache key\n if (mermaidCache.get(cacheKey) === renderPromise) {\n setData(svg);\n }\n })\n .catch(() => {\n // Remove failed promise from cache\n if (mermaidCache.get(cacheKey) === renderPromise) {\n mermaidCache.delete(cacheKey);\n }\n });\n }, [cacheKey, content, id, mermaidConfig]);\n\n return data;\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/ui",
3
- "version": "5.6.5",
3
+ "version": "5.7.0",
4
4
  "description": "Lobe UI is an open-source UI component library for building AIGC web apps",
5
5
  "keywords": [
6
6
  "lobehub",