1- import React , { useCallback , useMemo } from 'react'
1+ import React , { useCallback , useMemo , useState } from 'react'
22import { Entity } from '@dcl/ecs'
33
44import { CAMERA , PLAYER , ROOT } from '../../lib/sdk/tree'
@@ -86,6 +86,7 @@ const Hierarchy: React.FC = () => {
8686 centerViewOnEntity
8787 } = useTree ( )
8888 const selectedEntities = useEntitiesWith ( ( components ) => components . Selection )
89+ const [ lastSelectedItem , setLastSelectedItem ] = useState < Entity | undefined > ( undefined )
8990
9091 const isSelected = useCallback (
9192 ( entity : Entity ) => {
@@ -94,13 +95,58 @@ const Hierarchy: React.FC = () => {
9495 [ selectedEntities ]
9596 )
9697
98+ const getAllVisibleEntities = useCallback ( ( ) => {
99+ const entities : Entity [ ] = [ ]
100+
101+ const traverse = ( entity : Entity ) => {
102+ if ( ! isHidden ( entity ) ) {
103+ entities . push ( entity )
104+ if ( isOpen ( entity ) ) {
105+ getChildren ( entity ) . forEach ( child => traverse ( child ) )
106+ }
107+ }
108+ }
109+
110+ traverse ( ROOT )
111+
112+ return entities
113+ } , [ getChildren , isOpen , isHidden ] )
114+
115+ const handleRangeSelection = useCallback ( ( fromEntity : Entity , toEntity : Entity ) => {
116+ const allEntities = getAllVisibleEntities ( )
117+ const fromIndex = allEntities . findIndex ( e => getId ( e ) === getId ( fromEntity ) )
118+ const toIndex = allEntities . findIndex ( e => getId ( e ) === getId ( toEntity ) )
119+
120+ const startIndex = Math . min ( fromIndex , toIndex )
121+ const endIndex = Math . max ( fromIndex , toIndex )
122+
123+ allEntities . forEach ( ( entity , index ) => {
124+ if ( index >= startIndex && index <= endIndex ) {
125+ select ( entity , index > startIndex ) // first item replaces selection, others add to selection
126+ }
127+ } )
128+ } , [ getAllVisibleEntities , getId , select ] )
129+
130+ const handleSelect = useCallback ( ( entity : Entity , multiple ?: boolean ) => {
131+ // if this is a shift-click and we have a last selected item, do range selection
132+ if ( multiple && lastSelectedItem && lastSelectedItem !== entity ) {
133+ handleRangeSelection ( lastSelectedItem , entity )
134+ } else {
135+ select ( entity , multiple )
136+ }
137+ } , [ select , lastSelectedItem , handleRangeSelection ] )
138+
139+ const handleLastSelectedChange = useCallback ( ( entity : Entity ) => {
140+ if ( entity !== lastSelectedItem ) setLastSelectedItem ( entity )
141+ } , [ lastSelectedItem ] )
142+
97143 const props = {
98144 getExtraContextMenu : ContextMenu ,
99145 onAddChild : addChild ,
100146 onDrop : setParent ,
101147 onRemove : remove ,
102148 onRename : rename ,
103- onSelect : select ,
149+ onSelect : handleSelect ,
104150 onDoubleSelect : centerViewOnEntity ,
105151 onSetOpen : setOpen ,
106152 onDuplicate : duplicate ,
@@ -115,7 +161,8 @@ const Hierarchy: React.FC = () => {
115161 canRemove : canRemove ,
116162 canDuplicate : canDuplicate ,
117163 canDrag : canDrag ,
118- canReorder : canReorder
164+ canReorder : canReorder ,
165+ onLastSelectedChange : handleLastSelectedChange
119166 }
120167
121168 return (
0 commit comments