@@ -3346,91 +3346,89 @@ class Activity {
33463346 const restoreTrash = ( activity ) => {
33473347 if ( ! activity . blocks || ! activity . blocks . trashStacks || activity . blocks . trashStacks . length === 0 ) {
33483348 activity . textMsg (
3349- _ ( "Nothing in the trash to restore ." ) ,
3349+ _ ( "Trash can is empty ." ) ,
33503350 3000
33513351 ) ;
33523352 return ;
33533353 }
3354- activity . _restoreTrash ( ) ;
3354+
3355+ if ( docById ( "helpfulWheelDiv" ) . style . display !== "none" ) {
3356+ docById ( "helpfulWheelDiv" ) . style . display = "none" ;
3357+ activity . __tick ( ) ;
3358+ }
3359+ } ;
3360+
3361+ const restoreTrashPop = ( activity ) => {
3362+ if ( ! activity . blocks || ! activity . blocks . trashStacks || activity . blocks . trashStacks . length === 0 ) {
3363+ activity . textMsg (
3364+ _ ( "Trash can is empty." ) ,
3365+ 3000
3366+ ) ;
3367+ return ;
3368+ }
3369+ this . _restoreTrashById ( this . blocks . trashStacks [ this . blocks . trashStacks . length - 1 ] ) ;
33553370 activity . textMsg (
33563371 _ ( "Item restored from the trash." ) ,
33573372 3000
33583373 ) ;
3359-
3374+
33603375 if ( docById ( "helpfulWheelDiv" ) . style . display !== "none" ) {
33613376 docById ( "helpfulWheelDiv" ) . style . display = "none" ;
33623377 activity . __tick ( ) ;
33633378 }
3364- } ;
3379+
3380+ }
33653381
3366- this . _restoreTrash = ( ) => {
3382+ this . _restoreTrashById = ( blockId ) => {
3383+ const blockIndex = this . blocks . trashStacks . indexOf ( blockId ) ;
3384+ if ( blockIndex === - 1 ) return ; // Block not found in trash
3385+
3386+ this . blocks . trashStacks . splice ( blockIndex , 1 ) ; // Remove from trash
3387+
33673388 for ( const name in this . palettes . dict ) {
33683389 this . palettes . dict [ name ] . hideMenu ( true ) ;
33693390 }
3370-
3391+
33713392 this . blocks . activeBlock = null ;
33723393 this . refreshCanvas ( ) ;
33733394
33743395 const dx = 0 ;
33753396 const dy = - this . cellSize * 3 ; // Reposition
3376-
3377- if ( this . blocks . trashStacks . length === 0 ) {
3378- return ;
3379- }
3380-
3381- const thisBlock = this . blocks . trashStacks . pop ( ) ;
3382-
3383- // Restore drag group in trash
3384- this . blocks . findDragGroup ( thisBlock ) ;
3397+
3398+ // Restore drag group
3399+ this . blocks . findDragGroup ( blockId ) ;
33853400 for ( let b = 0 ; b < this . blocks . dragGroup . length ; b ++ ) {
33863401 const blk = this . blocks . dragGroup [ b ] ;
33873402 this . blocks . blockList [ blk ] . trash = false ;
33883403 this . blocks . moveBlockRelative ( blk , dx , dy ) ;
33893404 this . blocks . blockList [ blk ] . show ( ) ;
33903405 }
3391-
3392- this . blocks . raiseStackToTop ( thisBlock ) ;
3393-
3394- if (
3395- this . blocks . blockList [ thisBlock ] . name === "start" ||
3396- this . blocks . blockList [ thisBlock ] . name === "drum"
3397- ) {
3398- const turtle = this . blocks . blockList [ thisBlock ] . value ;
3406+
3407+ this . blocks . raiseStackToTop ( blockId ) ;
3408+ const restoredBlock = this . blocks . blockList [ blockId ] ;
3409+
3410+ if ( restoredBlock . name === 'start' || restoredBlock . name === 'drum' ) {
3411+ const turtle = restoredBlock . value ;
33993412 this . turtles . turtleList [ turtle ] . inTrash = false ;
34003413 this . turtles . turtleList [ turtle ] . container . visible = true ;
3401- } else if ( this . blocks . blockList [ thisBlock ] . name === "action" ) {
3402- // We need to add a palette entry for this action.
3403- // But first we need to ensure we have a unqiue name,
3404- // as the name could have been taken in the interim.
3405- const actionArg = this . blocks . blockList [
3406- this . blocks . blockList [ thisBlock ] . connections [ 1 ]
3407- ] ;
3414+ } else if ( restoredBlock . name === 'action' ) {
3415+ const actionArg = this . blocks . blockList [ restoredBlock . connections [ 1 ] ] ;
34083416 if ( actionArg !== null ) {
34093417 let label ;
34103418 const oldName = actionArg . value ;
3411- // Mark the action block as still being in the
3412- // trash so that its name won't be considered when
3413- // looking for a unique name.
3414- this . blocks . blockList [ thisBlock ] . trash = true ;
3419+ restoredBlock . trash = true ;
34153420 const uniqueName = this . blocks . findUniqueActionName ( oldName ) ;
3416- this . blocks . blockList [ thisBlock ] . trash = false ;
3421+ restoredBlock . trash = false ;
34173422
34183423 if ( uniqueName !== actionArg ) {
34193424 actionArg . value = uniqueName ;
3420-
3421- label = actionArg . value . toString ( ) ;
3422- if ( label . length > 8 ) {
3423- label = label . substr ( 0 , 7 ) + "..." ;
3424- }
3425+ label = uniqueName . length > 8 ? uniqueName . substr ( 0 , 7 ) + '...' : uniqueName ;
34253426 actionArg . text . text = label ;
34263427
34273428 if ( actionArg . label !== null ) {
34283429 actionArg . label . value = uniqueName ;
34293430 }
3430-
34313431 actionArg . container . updateCache ( ) ;
3432-
3433- // Check the drag group to ensure any do blocks are updated (in case of recursion).
34343432 for ( let b = 0 ; b < this . blocks . dragGroup . length ; b ++ ) {
34353433 const me = this . blocks . blockList [ this . blocks . dragGroup [ b ] ] ;
34363434 if (
@@ -3441,11 +3439,7 @@ class Activity {
34413439 ) {
34423440 me . privateData = uniqueName ;
34433441 me . value = uniqueName ;
3444-
3445- label = me . value . toString ( ) ;
3446- if ( label . length > 8 ) {
3447- label = label . substr ( 0 , 7 ) + "..." ;
3448- }
3442+ label = uniqueName . length > 8 ? uniqueName . substr ( 0 , 7 ) + '...' : uniqueName ;
34493443 me . text . text = label ;
34503444 me . overrideName = label ;
34513445 me . regenerateArtwork ( ) ;
@@ -3455,21 +3449,110 @@ class Activity {
34553449 }
34563450 }
34573451 }
3458-
3452+ activity . textMsg (
3453+ _ ( "Item restored from the trash." ) ,
3454+ 3000
3455+ ) ;
3456+
34593457 this . refreshCanvas ( ) ;
3460- } ;
3458+ } ;
3459+
3460+ // Add event listener for trash icon click
3461+ document . getElementById ( 'restoreIcon' ) . addEventListener ( 'click' , ( ) => {
3462+ this . _renderTrashView ( ) ;
3463+ } ) ;
3464+
3465+ // function to hide trashView from canvas
3466+ function handleClickOutsideTrashView ( trashView ) {
3467+ let firstClick = true ;
3468+ document . addEventListener ( 'click' , ( event ) => {
3469+ if ( firstClick ) {
3470+ firstClick = false ;
3471+ return ;
3472+ }
3473+ if ( ! trashView . contains ( event . target ) && event . target !== trashView ) {
3474+ trashView . style . display = 'none' ;
3475+ }
3476+ } ) ;
3477+ }
34613478
3462- this . handleKeyDown = ( event ) => {
3463-
3464- if ( event . ctrlKey && event . key === "z" ) {
3465- this . _restoreTrash ( activity ) ;
3466- activity . __tick ( ) ;
3467- event . preventDefault ( ) ;
3479+ this . _renderTrashView = ( ) => {
3480+ if ( ! activity . blocks || ! activity . blocks . trashStacks || activity . blocks . trashStacks . length === 0 ) {
3481+ return ;
34683482 }
3469- } ;
3470-
3471- // Attach keydown event listener to document
3472- document . addEventListener ( "keydown" , this . handleKeyDown ) ;
3483+ const trashList = document . getElementById ( 'trashList' ) ;
3484+ const trashView = document . createElement ( 'div' ) ;
3485+ trashView . id = 'trashView' ;
3486+ trashView . classList . add ( 'trash-view' ) ;
3487+
3488+ // Sticky icons
3489+ const buttonContainer = document . createElement ( 'div' ) ;
3490+ buttonContainer . classList . add ( 'button-container' ) ;
3491+
3492+ const restoreLastIcon = document . createElement ( 'a' ) ;
3493+ restoreLastIcon . id = 'restoreLastIcon' ;
3494+ restoreLastIcon . classList . add ( 'restore-last-icon' ) ;
3495+ restoreLastIcon . innerHTML = '<i class="material-icons md-48">restore_from_trash</i>' ;
3496+ restoreLastIcon . addEventListener ( 'click' , ( ) => {
3497+ this . _restoreTrashById ( this . blocks . trashStacks [ this . blocks . trashStacks . length - 1 ] ) ;
3498+ trashView . classList . add ( 'hidden' ) ;
3499+ } ) ;
3500+
3501+ const restoreAllIcon = document . createElement ( 'a' ) ;
3502+ restoreAllIcon . id = 'restoreAllIcon' ;
3503+ restoreAllIcon . classList . add ( 'restore-all-icon' ) ;
3504+ restoreAllIcon . innerHTML = '<i class="material-icons md-48">delete_sweep</i>' ;
3505+ restoreAllIcon . addEventListener ( 'click' , ( ) => {
3506+ while ( this . blocks . trashStacks . length > 0 ) {
3507+ this . _restoreTrashById ( this . blocks . trashStacks [ 0 ] ) ;
3508+ }
3509+ trashView . classList . add ( 'hidden' ) ;
3510+ } ) ;
3511+ restoreLastIcon . setAttribute ( "title" , _ ( "Restore last item" ) ) ;
3512+ restoreAllIcon . setAttribute ( "title" , _ ( "Restore all items" ) ) ;
3513+
3514+ buttonContainer . appendChild ( restoreLastIcon ) ;
3515+ buttonContainer . appendChild ( restoreAllIcon ) ;
3516+ trashView . appendChild ( buttonContainer ) ;
3517+
3518+ // Render trash items
3519+ this . blocks . trashStacks . forEach ( ( blockId ) => {
3520+ const block = this . blocks . blockList [ blockId ] ;
3521+ const listItem = document . createElement ( 'div' ) ;
3522+ listItem . classList . add ( 'trash-item' ) ;
3523+
3524+ const svgData = block . artwork ;
3525+ const encodedData = 'data:image/svg+xml;utf8,' + encodeURIComponent ( svgData ) ;
3526+
3527+ const img = document . createElement ( 'img' ) ;
3528+ img . src = encodedData ;
3529+ img . alt = 'Block Icon' ;
3530+ img . classList . add ( 'trash-item-icon' ) ;
3531+
3532+ const textNode = document . createTextNode ( block . name ) ;
3533+
3534+ listItem . appendChild ( img ) ;
3535+ listItem . appendChild ( textNode ) ;
3536+ listItem . dataset . blockId = blockId ;
3537+
3538+ listItem . addEventListener ( 'mouseover' , ( ) => listItem . classList . add ( 'hover' ) ) ;
3539+ listItem . addEventListener ( 'mouseout' , ( ) => listItem . classList . remove ( 'hover' ) ) ;
3540+ listItem . addEventListener ( 'click' , ( ) => {
3541+ this . _restoreTrashById ( blockId ) ;
3542+ trashView . classList . add ( 'hidden' ) ;
3543+ } ) ;
3544+ handleClickOutsideTrashView ( trashView ) ;
3545+
3546+ trashView . appendChild ( listItem ) ;
3547+ } ) ;
3548+
3549+ const existingView = document . getElementById ( 'trashView' ) ;
3550+ if ( existingView ) {
3551+ trashList . replaceChild ( trashView , existingView ) ;
3552+ } else {
3553+ trashList . appendChild ( trashView ) ;
3554+ }
3555+ } ;
34733556
34743557 /*
34753558 * Open aux menu
@@ -5840,7 +5923,7 @@ class Activity {
58405923 this . helpfulWheelItems . push ( { label : "Increase block size" , icon : "imgsrc:data:image/svg+xml;base64," + window . btoa ( base64Encode ( BIGGERBUTTON ) ) , display : true , fn : doLargerBlocks } ) ;
58415924
58425925 if ( ! this . helpfulWheelItems . find ( ele => ele . label === "Restore" ) )
5843- this . helpfulWheelItems . push ( { label : "Restore" , icon : "imgsrc:header-icons/restore-from-trash.svg" , display : true , fn : restoreTrash } ) ;
5926+ this . helpfulWheelItems . push ( { label : "Restore" , icon : "imgsrc:header-icons/restore-from-trash.svg" , display : true , fn : restoreTrashPop } ) ;
58445927
58455928 if ( ! this . helpfulWheelItems . find ( ele => ele . label === "Turtle Wrap Off" ) )
58465929 this . helpfulWheelItems . push ( { label : "Turtle Wrap Off" , icon : "imgsrc:header-icons/wrap-text.svg" , display : true , fn : this . toolbar . changeWrap } ) ;
0 commit comments