11import { Layers } from "constants/Layers" ;
2- import React , { ReactNode , useCallback , useRef , useState , useEffect } from "react" ;
2+ import React , { ReactNode , useCallback , useRef , useEffect } from "react" ;
33
44export type CheckSelectFn = (
55item ?:HTMLDivElement | null ,
@@ -41,7 +41,7 @@ const createInitialState = (): SectionState => ({
4141
4242export const DragSelector = React . memo ( ( props :SectionProps ) => {
4343const selectAreaRef = useRef < HTMLDivElement > ( null ) ;
44- const [ state , setState ] = useState < SectionState > ( createInitialState ( ) ) ;
44+ const stateRef = useRef < SectionState > ( createInitialState ( ) ) ;
4545const mountedRef = useRef ( true ) ;
4646
4747// Cleanup on unmount
@@ -54,18 +54,62 @@ export const DragSelector = React.memo((props: SectionProps) => {
5454} ;
5555} , [ ] ) ;
5656
57+ const rectIntersect = useCallback ( (
58+ selectionBox :Rect | undefined ,
59+ item :HTMLElement | null | undefined
60+ ) :boolean => {
61+ if ( ! selectionBox || ! item || ! selectAreaRef . current ) return false ;
62+
63+ const containerRect = selectAreaRef . current . getBoundingClientRect ( ) ;
64+ const itemBox = {
65+ top :item . getBoundingClientRect ( ) . top - containerRect . top ,
66+ left :item . getBoundingClientRect ( ) . left - containerRect . left ,
67+ width :item . getBoundingClientRect ( ) . width ,
68+ height :item . getBoundingClientRect ( ) . height ,
69+ } ;
70+
71+ return (
72+ selectionBox . left <= itemBox . left + itemBox . width &&
73+ selectionBox . left + selectionBox . width >= itemBox . left &&
74+ selectionBox . top <= itemBox . top + itemBox . height &&
75+ selectionBox . top + selectionBox . height >= itemBox . top
76+ ) ;
77+ } , [ ] ) ;
78+
79+ const calculateSelectionBox = useCallback ( ( startPoint :Point | undefined , endPoint :Point ) => {
80+ if ( ! stateRef . current . mouseDown || ! startPoint || ! endPoint ) return undefined ;
81+
82+ return {
83+ left :Math . min ( startPoint . x , endPoint . x ) ,
84+ top :Math . min ( startPoint . y , endPoint . y ) ,
85+ width :Math . abs ( startPoint . x - endPoint . x ) ,
86+ height :Math . abs ( startPoint . y - endPoint . y ) ,
87+ } ;
88+ } , [ ] ) ;
89+
90+ const childrenViewCheckFunc = useCallback ( (
91+ item ?:HTMLDivElement | null ,
92+ afterCheck ?:( checkResult :boolean ) => void
93+ ) => {
94+ const result = rectIntersect ( stateRef . current . selectionBox , item ) ;
95+ if ( afterCheck ) {
96+ afterCheck ( result ) ;
97+ }
98+ return result ;
99+ } , [ rectIntersect ] ) ;
100+
57101const handleMouseMove = useCallback ( ( e :MouseEvent ) => {
58- if ( ! mountedRef . current || ! state . mouseDown ) return ;
102+ if ( ! mountedRef . current || ! stateRef . current . mouseDown ) return ;
59103
60104const endPoint = {
61105x :e . pageX - ( selectAreaRef . current ?. getBoundingClientRect ( ) . left ?? 0 ) ,
62106y :e . pageY - ( selectAreaRef . current ?. getBoundingClientRect ( ) . top ?? 0 ) ,
63107} ;
64108
65- setState ( prevState => ( {
66- ...prevState ,
67- selectionBox :calculateSelectionBox ( prevState . startPoint , endPoint ) ,
68- } ) ) ;
109+ stateRef . current = {
110+ ...stateRef . current ,
111+ selectionBox :calculateSelectionBox ( stateRef . current . startPoint , endPoint ) ,
112+ } ;
69113
70114// Clean up selection properly
71115const selection = window . getSelection ( ) ;
@@ -74,83 +118,37 @@ export const DragSelector = React.memo((props: SectionProps) => {
74118}
75119
76120props . onMouseMove ( childrenViewCheckFunc ) ;
77- } , [ state . mouseDown , state . startPoint , props . onMouseMove ] ) ;
121+ } , [ props . onMouseMove , calculateSelectionBox , childrenViewCheckFunc ] ) ;
78122
79123const handleMouseUp = useCallback ( ( ) => {
80- if ( ! mountedRef . current ) return ;
81-
82124window . document . removeEventListener ( "mousemove" , handleMouseMove ) ;
83125window . document . removeEventListener ( "mouseup" , handleMouseUp ) ;
84126props . onMouseUp ( ) ;
85- setState ( createInitialState ( ) ) ;
127+ stateRef . current = createInitialState ( ) ;
86128} , [ handleMouseMove , props . onMouseUp ] ) ;
87129
88130const handleMouseDown = useCallback ( ( e :React . MouseEvent < HTMLDivElement > ) => {
89- if ( ! mountedRef . current || e . button === 2 || e . nativeEvent . which === 2 ) return ;
131+ if ( e . button === 2 || e . nativeEvent . which === 2 ) return ;
90132
91133const startPoint = {
92134x :e . pageX - ( selectAreaRef . current ?. getBoundingClientRect ( ) . left ?? 0 ) ,
93135y :e . pageY - ( selectAreaRef . current ?. getBoundingClientRect ( ) . top ?? 0 ) ,
94136} ;
95137
96- setState ( {
138+ stateRef . current = {
97139mouseDown :true ,
98140 startPoint,
99141selectionBox :undefined ,
100142appendMode :false ,
101- } ) ;
143+ } ;
102144
103145window . document . addEventListener ( "mousemove" , handleMouseMove ) ;
104146window . document . addEventListener ( "mouseup" , handleMouseUp ) ;
105147props . onMouseDown ( ) ;
106148} , [ handleMouseMove , handleMouseUp , props . onMouseDown ] ) ;
107149
108- const rectIntersect = useCallback ( (
109- selectionBox :Rect | undefined ,
110- item :HTMLElement | null | undefined
111- ) :boolean => {
112- if ( ! selectionBox || ! item || ! selectAreaRef . current ) return false ;
113-
114- const containerRect = selectAreaRef . current . getBoundingClientRect ( ) ;
115- const itemBox = {
116- top :item . getBoundingClientRect ( ) . top - containerRect . top ,
117- left :item . getBoundingClientRect ( ) . left - containerRect . left ,
118- width :item . getBoundingClientRect ( ) . width ,
119- height :item . getBoundingClientRect ( ) . height ,
120- } ;
121-
122- return (
123- selectionBox . left <= itemBox . left + itemBox . width &&
124- selectionBox . left + selectionBox . width >= itemBox . left &&
125- selectionBox . top <= itemBox . top + itemBox . height &&
126- selectionBox . top + selectionBox . height >= itemBox . top
127- ) ;
128- } , [ ] ) ;
129-
130- const childrenViewCheckFunc = useCallback ( (
131- item ?:HTMLDivElement | null ,
132- afterCheck ?:( checkResult :boolean ) => void
133- ) => {
134- const result = rectIntersect ( state . selectionBox , item ) ;
135- if ( afterCheck ) {
136- afterCheck ( result ) ;
137- }
138- return result ;
139- } , [ state . selectionBox , rectIntersect ] ) ;
140-
141- const calculateSelectionBox = useCallback ( ( startPoint :Point | undefined , endPoint :Point ) => {
142- if ( ! state . mouseDown || ! startPoint || ! endPoint ) return undefined ;
143-
144- return {
145- left :Math . min ( startPoint . x , endPoint . x ) ,
146- top :Math . min ( startPoint . y , endPoint . y ) ,
147- width :Math . abs ( startPoint . x - endPoint . x ) ,
148- height :Math . abs ( startPoint . y - endPoint . y ) ,
149- } ;
150- } , [ state . mouseDown ] ) ;
151-
152150const renderSelectionBox = useCallback ( ( ) => {
153- if ( ! state . mouseDown || ! state . startPoint || ! state . selectionBox || ! selectAreaRef . current ) {
151+ if ( ! stateRef . current . mouseDown || ! stateRef . current . startPoint || ! stateRef . current . selectionBox || ! selectAreaRef . current ) {
154152return null ;
155153}
156154
@@ -160,14 +158,14 @@ export const DragSelector = React.memo((props: SectionProps) => {
160158background :"rgba(51, 119, 255, 0.1)" ,
161159position :"absolute" ,
162160zIndex :Layers . dragSelectBox ,
163- left :state . selectionBox . left ,
164- top :state . selectionBox . top ,
165- height :state . selectionBox . height ,
166- width :state . selectionBox . width ,
161+ left :stateRef . current . selectionBox . left ,
162+ top :stateRef . current . selectionBox . top ,
163+ height :stateRef . current . selectionBox . height ,
164+ width :stateRef . current . selectionBox . width ,
167165} }
168166/>
169167) ;
170- } , [ state . mouseDown , state . startPoint , state . selectionBox ] ) ;
168+ } , [ ] ) ;
171169
172170return (
173171< div