|
1 | 1 | import { default as AntdDrawer, DrawerProps as AntdDrawerProps } from "antd/es/drawer";
|
2 | 2 | import Handle from "./Modal/handler";
|
3 |
| -import { useEffect, useMemo, useState } from "react"; |
| 3 | +import { useEffect, useMemo, useState, useCallback, useRef } from "react"; |
4 | 4 | import { Resizable, ResizeHandle } from "react-resizable";
|
5 | 5 | import { useResizeDetector } from "react-resize-detector";
|
6 | 6 | import styled from "styled-components";
|
7 | 7 |
|
8 | 8 | const StyledDrawer = styled(AntdDrawer)`
|
9 | 9 | & .ant-drawer-content-wrapper {
|
10 |
| - transition-duration: 0s; |
| 10 | + transition: transform 0.3s cubic-bezier(0.7, 0.3, 0.1, 1) !important; |
| 11 | + will-change: transform; |
| 12 | + transform: translate3d(0, 0, 0); |
| 13 | + } |
| 14 | +
|
| 15 | + & .ant-drawer-content { |
| 16 | + transition: none !important; |
| 17 | + } |
| 18 | +
|
| 19 | + & .ant-drawer-mask { |
| 20 | + transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1) !important; |
| 21 | + will-change: opacity; |
| 22 | + } |
| 23 | +
|
| 24 | + & .ant-drawer-header { |
| 25 | + transition: none !important; |
| 26 | + } |
| 27 | +
|
| 28 | + & .ant-drawer-body { |
| 29 | + transition: none !important; |
11 | 30 | }
|
12 | 31 | `;
|
13 | 32 |
|
@@ -53,36 +72,68 @@ export function Drawer(props: DrawerProps) {
|
53 | 72 | () => (resizable ? [getResizeHandle(placement)] : []),
|
54 | 73 | [placement, resizable]
|
55 | 74 | );
|
56 |
| - const isTopBom = ["top", "bottom"].includes(placement); |
| 75 | + const isTopBom = useMemo(() => ["top", "bottom"].includes(placement), [placement]); |
57 | 76 | const [width, setWidth] = useState<number>();
|
58 | 77 | const [height, setHeight] = useState<number>();
|
| 78 | + const mountedRef = useRef(true); |
| 79 | + |
| 80 | + // Combined effect for width and height cleanup |
59 | 81 | useEffect(() => {
|
60 |
| - setWidth(undefined); |
61 |
| - // eslint-disable-next-line react-hooks/exhaustive-deps |
62 |
| - }, [drawerWidth]); |
| 82 | + if (drawerWidth !== undefined) { |
| 83 | + setWidth(undefined); |
| 84 | + } |
| 85 | + if (drawerHeight !== undefined) { |
| 86 | + setHeight(undefined); |
| 87 | + } |
| 88 | + }, [drawerWidth, drawerHeight]); |
| 89 | + |
| 90 | + // Cleanup on unmount |
63 | 91 | useEffect(() => {
|
64 |
| - setHeight(undefined); |
65 |
| - // eslint-disable-next-line react-hooks/exhaustive-deps |
66 |
| - }, [drawerHeight]); |
67 |
| - const { width: detectWidth, height: detectHeight, ref } = useResizeDetector(); |
68 |
| - // log.info("Drawer. drawerWidth: ", drawerWidth, " width: ", width, "detectWidth: ", detectWidth); |
| 92 | + return () => { |
| 93 | + mountedRef.current = false; |
| 94 | + }; |
| 95 | + }, []); |
| 96 | + |
| 97 | + const { width: detectWidth, height: detectHeight, ref } = useResizeDetector({ |
| 98 | + onResize: () => { |
| 99 | + // Only update if component is still mounted |
| 100 | + if (!mountedRef.current) return; |
| 101 | + } |
| 102 | + }); |
| 103 | + |
| 104 | + const handleResizeStart = useCallback( |
| 105 | + (event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { |
| 106 | + props.onResizeStart?.(event, node, size, handle); |
| 107 | + }, |
| 108 | + [props.onResizeStart] |
| 109 | + ); |
| 110 | + |
| 111 | + const handleResize = useCallback( |
| 112 | + (event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { |
| 113 | + if (!mountedRef.current) return; |
| 114 | + isTopBom ? setHeight(size.height) : setWidth(size.width); |
| 115 | + props.onResize?.(event, node, size, handle); |
| 116 | + }, |
| 117 | + [isTopBom, props.onResize] |
| 118 | + ); |
| 119 | + |
| 120 | + const handleResizeStop = useCallback( |
| 121 | + (event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { |
| 122 | + props.onResizeStop?.(event, node, size, handle); |
| 123 | + }, |
| 124 | + [props.onResizeStop] |
| 125 | + ); |
| 126 | + |
69 | 127 | return (
|
70 | 128 | <StyledDrawer width={width ?? drawerWidth} height={height ?? drawerHeight} {...otherProps}>
|
71 | 129 | <Resizable
|
72 | 130 | width={width ?? detectWidth ?? 0}
|
73 | 131 | height={height ?? detectHeight ?? 0}
|
74 | 132 | resizeHandles={resizeHandles}
|
75 | 133 | handle={Handle}
|
76 |
| - onResizeStart={(event, { node, size, handle }) => |
77 |
| - props.onResizeStart?.(event, node, size, handle) |
78 |
| - } |
79 |
| - onResize={(event, { node, size, handle }) => { |
80 |
| - isTopBom ? setHeight(size.height) : setWidth(size.width); |
81 |
| - props.onResize?.(event, node, size, handle); |
82 |
| - }} |
83 |
| - onResizeStop={(event, { node, size, handle }) => |
84 |
| - props.onResizeStop?.(event, node, size, handle) |
85 |
| - } |
| 134 | + onResizeStart={handleResizeStart} |
| 135 | + onResize={handleResize} |
| 136 | + onResizeStop={handleResizeStop} |
86 | 137 | >
|
87 | 138 | <div ref={ref} style={{ height: "100%" }}>
|
88 | 139 | {children}
|
|
0 commit comments