11"use client" ;
2- import { useEffect , useState } from "react" ;
2+ import { useCallback , useEffect , useState } from "react" ;
33import {
44 Button ,
55 buttonVariants ,
@@ -18,6 +18,7 @@ import {
1818import { cn } from "@akashnetwork/ui/utils" ;
1919import { Refresh , Rocket , Xmark } from "iconoir-react" ;
2020import { useAtom } from "jotai" ;
21+ import { uniq } from "lodash" ;
2122import Link from "next/link" ;
2223import { NextSeo } from "next-seo" ;
2324
@@ -64,6 +65,9 @@ export const DeploymentList: React.FunctionComponent = () => {
6465 const [ isSignedInWithTrial ] = useAtom ( walletStore . isSignedInWithTrial ) ;
6566 const { user } = useCustomUser ( ) ;
6667
68+ const [ lastSelectedDeploymentDseq , setLastSelectedDeploymentDseq ] = useState < string | null > ( null ) ;
69+ const [ hoveredDelpoymentDseq , setHoveredDelpoymentDseq ] = useState < string | null > ( null ) ;
70+
6771 useEffect ( ( ) => {
6872 if ( isWalletLoaded && isSettingsInit ) {
6973 getDeployments ( ) ;
@@ -110,10 +114,35 @@ export const DeploymentList: React.FunctionComponent = () => {
110114 setSearch ( value ) ;
111115 } ;
112116
113- const onSelectDeployment = ( checked : boolean , dseq : string ) => {
117+ const isBetweenDseqs = useCallback (
118+ ( dseq : string , dseqA : string , dseqB : string ) => {
119+ const dseqIndex = currentPageDeployments ?. findIndex ( d => d . dseq === dseq ) ;
120+ const dseqAIndex = currentPageDeployments ?. findIndex ( d => d . dseq === dseqA ) ;
121+ const dseqBIndex = currentPageDeployments ?. findIndex ( d => d . dseq === dseqB ) ;
122+
123+ return (
124+ dseqIndex !== undefined &&
125+ dseqAIndex !== undefined &&
126+ dseqBIndex !== undefined &&
127+ ( ( dseqAIndex <= dseqIndex && dseqIndex <= dseqBIndex ) || ( dseqAIndex >= dseqIndex && dseqIndex >= dseqBIndex ) )
128+ ) ;
129+ } ,
130+ [ currentPageDeployments ]
131+ ) ;
132+
133+ const onSelectDeployment = ( checked : boolean , dseq : string , eventShiftPressed : boolean ) => {
134+ const dseqs =
135+ lastSelectedDeploymentDseq && eventShiftPressed
136+ ? currentPageDeployments . filter ( deployment => isBetweenDseqs ( deployment . dseq , dseq , lastSelectedDeploymentDseq ) ) . map ( d => d . dseq )
137+ : [ dseq ] ;
138+
114139 setSelectedDeploymentDseqs ( prev => {
115- return checked ? prev . concat ( [ dseq ] ) : prev . filter ( x => x !== dseq ) ;
140+ return checked ? uniq ( prev . concat ( dseqs ) ) : prev . filter ( x => ! dseqs . includes ( x ) ) ;
116141 } ) ;
142+
143+ if ( ! eventShiftPressed ) {
144+ setLastSelectedDeploymentDseq ( dseq ) ;
145+ }
117146 } ;
118147
119148 const onCloseSelectedDeployments = async ( ) => {
@@ -148,6 +177,45 @@ export const DeploymentList: React.FunctionComponent = () => {
148177 setPageIndex ( 0 ) ;
149178 } ;
150179
180+ const [ isShiftPressed , setIsShiftPressed ] = useState ( false ) ;
181+ useEffect ( ( ) => {
182+ const handleKeyDown = ( event : KeyboardEvent ) => {
183+ if ( event . key === "Shift" ) {
184+ setIsShiftPressed ( true ) ;
185+ }
186+ } ;
187+
188+ const handleKeyUp = ( event : KeyboardEvent ) => {
189+ if ( event . key === "Shift" ) {
190+ setIsShiftPressed ( false ) ;
191+ }
192+ } ;
193+
194+ window . addEventListener ( "keydown" , handleKeyDown ) ;
195+ window . addEventListener ( "keyup" , handleKeyUp ) ;
196+
197+ return ( ) => {
198+ window . removeEventListener ( "keydown" , handleKeyDown ) ;
199+ window . removeEventListener ( "keyup" , handleKeyUp ) ;
200+ } ;
201+ } , [ ] ) ;
202+
203+ const onRowMouseEnter = ( dseq : string ) => {
204+ setHoveredDelpoymentDseq ( dseq ) ;
205+ } ;
206+
207+ const isRowHighlightedForMultipleSelection = useCallback (
208+ ( dseq : string ) => {
209+ if ( ! isShiftPressed || ! hoveredDelpoymentDseq || ! lastSelectedDeploymentDseq ) {
210+ return false ;
211+ }
212+
213+ const dseqIndex = currentPageDeployments ?. findIndex ( d => d . dseq === dseq ) ;
214+ return dseqIndex !== undefined && isBetweenDseqs ( dseq , hoveredDelpoymentDseq , lastSelectedDeploymentDseq ) ;
215+ } ,
216+ [ isShiftPressed , hoveredDelpoymentDseq , lastSelectedDeploymentDseq , currentPageDeployments , isBetweenDseqs ]
217+ ) ;
218+
151219 return (
152220 < Layout isLoading = { isLoadingDeployments || isLoadingProviders } isUsingSettings isUsingWallet >
153221 < NextSeo title = "Deployments" />
@@ -293,6 +361,8 @@ export const DeploymentList: React.FunctionComponent = () => {
293361 isSelectable
294362 onSelectDeployment = { onSelectDeployment }
295363 checked = { selectedDeploymentDseqs . includes ( deployment . dseq ) }
364+ onMouseEnter = { onRowMouseEnter }
365+ isHighlighted = { isRowHighlightedForMultipleSelection ( deployment . dseq ) }
296366 />
297367 ) ) }
298368 </ TableBody >
0 commit comments