import { Component , Input , Optional , SkipSelf , Output , EventEmitter } from '@angular/core' ; import { File , LabDirectory , Directory , instanceOfFile , instanceOfDirectory } from '@machinelabs/models' ; import { deleteFromDirectory } from '@machinelabs/core' ; import { NameDialogService } from '../name-dialog/name-dialog-service' ; import { FileTreeService } from './file-tree.service' ; export interface FileTreeEventArgs { target : File | Directory ; parentDirectory : Directory ; path : string ; isFile : boolean ; isDirectory : boolean ; } type EventEmitterSelectorFn = ( instance : FileTreeComponent ) => EventEmitter < FileTreeEventArgs > ; type EventEmitterSetterFn = ( instance : FileTreeComponent , emitter : EventEmitter < FileTreeEventArgs > ) => void ; @ Component ( { selector : 'ml-file-tree' , templateUrl : './file-tree.component.html' , styleUrls : [ './file-tree.component.scss' ] } ) export class FileTreeComponent { @ Input ( ) showActionButtons = true ; @ Input ( ) directory = null as Directory ; @ Input ( ) mandatoryFiles : Array < string > = [ ] ; _remove : EventEmitter < FileTreeEventArgs > = null ; _addFile : EventEmitter < FileTreeEventArgs > = null ; _editFile : EventEmitter < FileTreeEventArgs > = null ; _selectFile : EventEmitter < FileTreeEventArgs > = null ; @ Output ( ) get remove ( ) { return this . lookupOrCreateReturn ( compInstance => compInstance . remove , compInstance => compInstance . _remove , ( compInstance , val ) => ( compInstance . _remove = val ) ) ; } @ Output ( ) get addFile ( ) { return this . lookupOrCreateReturn ( compInstance => compInstance . addFile , compInstance => compInstance . _addFile , ( compInstance , val ) => ( compInstance . _addFile = val ) ) ; } @ Output ( ) get editFile ( ) { return this . lookupOrCreateReturn ( compInstance => compInstance . editFile , compInstance => compInstance . _editFile , ( compInstance , val ) => ( compInstance . _editFile = val ) ) ; } @ Output ( ) get selectFile ( ) { return this . lookupOrCreateReturn ( compInstance => compInstance . selectFile , compInstance => compInstance . _selectFile , ( compInstance , val ) => ( compInstance . _selectFile = val ) ) ; } constructor ( private nameDialogService , @ Optional ( ) @ SkipSelf ( ) public parent , public fileTreeService ) { } get level ( ) { return this . parent ? this . parent . level + 1 : 0 ; } private createEventArgs ( target ) { const isFile = instanceOfFile ( target ) ; const path = isFile ? `${ this . getFileTreePath ( ) }/${ target . name }` : this . getFileTreePath ( ) ; return { target : target , parentDirectory : this . directory , isFile : isFile , isDirectory : ! isFile , path : path } ; } isRemovable ( file ) { return ! this . isMandatoryFile ( file ) ; } isEditable ( file ) { return ! this . isMandatoryFile ( file ) ; } select ( event , file ) { if ( instanceOfFile ( file ) ) { this . selectFile . emit ( this . createEventArgs ( file ) ) ; } } deleteFileOrDirectory ( event , fileOrDirectory ) { this . stopEventPropagation ( event ) ; deleteFromDirectory ( fileOrDirectory , this . directory ) ; this . remove . emit ( this . createEventArgs ( fileOrDirectory ) ) ; } openAddFolderNameDialog ( event , parentDirectory ) { this . stopPropagationAndExpandDirectory ( event , parentDirectory ) ; this . nameDialogService . openAddFolderNameDialog ( parentDirectory ) ; } openEditFolderNameDialog ( event , parentDirectory , directory ) { this . stopPropagationAndExpandDirectory ( event , parentDirectory ) ; this . nameDialogService . openEditFolderNameDialog ( parentDirectory , directory ) ; } stopPropagationAndExpandDirectory ( event , parentDirectory ) { this . stopEventPropagation ( event ) ; this . fileTreeService . expandDirectory ( parentDirectory ) ; } openEditFileNameDialog ( event , parentDirectory , file ) { this . stopEventPropagation ( event ) ; this . nameDialogService . openEditFileNameDialog ( parentDirectory , file ) . subscribe ( editedFile => this . editFile . emit ( this . createEventArgs ( editedFile ) ) ) ; } openAddFileNameDialog ( event , parentDirectory ) { this . stopEventPropagation ( event ) ; this . nameDialogService . openAddFileNameDialog ( parentDirectory ) . subscribe ( file => this . addFile . emit ( this . createEventArgs ( file ) ) ) ; } private stopEventPropagation ( event ) { if ( event ) { event . stopPropagation ( ) ; } } private isMandatoryFile ( file ) { return this . mandatoryFiles . indexOf ( file . name ) > - 1 ; } private lookupOrCreateReturn ( propertySelector , backingFieldSelector , setter ) { if ( this . parent ) { return propertySelector ( this . parent ) ; } if ( ! backingFieldSelector ( this ) ) { setter ( this , new EventEmitter ( ) ) ; } return backingFieldSelector ( this ) ; } public getFileTreePath ( ) { const path = [ ] ; let p = this . parent ; path . push ( this . directory . name ) ; while ( p !== null ) { path . push ( p . directory . name ) ; p = p . parent ; } path . reverse ( ) ; return path . join ( '/' ) ; } }