|
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