From f302574e84d2e6d5df1b6d325243f651dc0a9651 Mon Sep 17 00:00:00 2001 From: Alex ORLUC <alex.orluc@maarch.org> Date: Fri, 26 Jul 2019 02:01:16 +0200 Subject: [PATCH] FEAT #11291 TIME 3 begin dnd res in folders --- .../app/folder/folder-tree.component.html | 75 ++++++++++--------- .../app/folder/folder-tree.component.scss | 14 ++++ .../app/folder/folder-tree.component.ts | 63 ++++++++++++++-- .../folder/panel/panel-folder.component.html | 2 +- .../folder/panel/panel-folder.component.ts | 8 +- .../app/list/basket-list.component.html | 9 ++- .../app/list/basket-list.component.scss | 11 +++ .../app/list/basket-list.component.ts | 6 ++ 8 files changed, 142 insertions(+), 46 deletions(-) diff --git a/src/frontend/app/folder/folder-tree.component.html b/src/frontend/app/folder/folder-tree.component.html index f155fca6bf6..3d073b208f5 100644 --- a/src/frontend/app/folder/folder-tree.component.html +++ b/src/frontend/app/folder/folder-tree.component.html @@ -1,26 +1,15 @@ -<mat-list role="list"> - <mat-list-item role="listitem"> - <form> - <mat-form-field floatLabel="never" style="width: 100%;"> - <input matInput #itemValue placeholder="Nouveau dossier..."> - </mat-form-field> - <button type="submit" mat-icon-button (click)="createRoot(itemValue.value);itemValue.value = ''" - [disabled]="itemValue.value === ''"> - <mat-icon class="fa fa-check" color="accent" [title]="lang.add"></mat-icon> - </button> - </form> - </mat-list-item> -</mat-list> <mat-nav-list> <mat-tree [dataSource]="dataSource" [treeControl]="treeControl"> <!-- This is the tree node template for leaf nodes --> - <mat-tree-node [@hideShow] *matTreeNodeDef="let node" matTreeNodePadding matTreeNodePaddingIndent="20px"> + <mat-tree-node cdkDropList [id]="'folder-list-'+node.id" cdkDropListConnectedTo="list-1" (cdkDropListDropped)="drop($event, node)" + (cdkDropListEntered)="dragEnter(node)" (cdkDropListExited)="node.drag=false" [@hideShow] *matTreeNodeDef="let node" + matTreeNodePadding matTreeNodePaddingIndent="20px"> <!-- use a disabled button to provide padding for tree leaf --> <button class="expandButton" mat-icon-button disabled></button> <a mat-list-item disableRipple [class.selectedFolder]="node.selected" (mouseenter)="showAction(node)" (mouseleave)="hideAction(node)" (click)="selectFolder(node)"> <span style="flex:1;overflow: hidden;text-overflow: ellipsis;"> - <span class="treeLabel" [title]="node.label"> + <span class="treeLabel" [class.private]="!node.public" [title]="node.label" [class.drag]="node.drag"> {{node.label}} </span> </span> @@ -28,54 +17,70 @@ [disabled]="!node.showAction"> <mat-icon *ngIf="node.showAction" class="folderAction fa fa-ellipsis-v"></mat-icon> <span *ngIf="!node.showAction"> - 4 + {{node.countRes}} </span> </button> <mat-menu #menu="matMenu"> - <button mat-menu-item (click)="addNewItem(node)">Nouveau sous-dossier</button> + <button mat-menu-item [disabled]="createItemNode" (click)="addNewItem(node)">Nouveau sous-dossier</button> <button mat-menu-item (click)="deleteNode(node)">Supprimer</button> </mat-menu> </a> </mat-tree-node> - <mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding - matTreeNodePaddingIndent="45px"> + <mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding matTreeNodePaddingIndent="45px"> <form> <mat-form-field floatLabel="never" style="width: 100%;"> - <input matInput #itemValue placeholder="Nouveau dossier..."> + <input matInput id="itemValue" #itemValue placeholder="Nouveau dossier..."> + <button type="submit" matSuffix mat-icon-button (click)="saveNode(node, itemValue.value)" + [disabled]="itemValue.value === ''"> + <mat-icon class="fa fa-check" color="accent" [title]="lang.add"></mat-icon> + </button> </mat-form-field> - <button type="submit" mat-icon-button (click)="saveNode(node, itemValue.value)" - [disabled]="itemValue.value === ''"> - <mat-icon class="fa fa-check" color="accent" [title]="lang.add"></mat-icon> - </button> </form> </mat-tree-node> <!-- This is the tree node template for expandable nodes --> - <mat-tree-node [@hideShow] *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding - matTreeNodePaddingIndent="20px"> - <button class="expandButton" mat-icon-button matTreeNodeToggle [attr.aria-label]="'toggle ' + node.name"> - <mat-icon - class="mat-icon-rtl-mirror fa {{treeControl.isExpanded(node) ? 'fa-chevron-down' : 'fa-chevron-right'}}"> + <mat-tree-node cdkDropList [id]="'folder-list-'+node.id" cdkDropListConnectedTo="list-1" (cdkDropListDropped)="drop($event, node)" + (cdkDropListEntered)="dragEnter(node)" (cdkDropListExited)="node.drag=false" [@hideShow] *matTreeNodeDef="let node;when: hasChild" + matTreeNodePadding matTreeNodePaddingIndent="20px"> + <button class="expandButton" [class.private]="!node.public" mat-icon-button matTreeNodeToggle [attr.aria-label]="'toggle ' + node.name"> + <mat-icon class="mat-icon-rtl-mirror fa {{treeControl.isExpanded(node) ? 'fa-chevron-down' : 'fa-chevron-right'}}"> </mat-icon> </button> <a mat-list-item disableRipple [class.selectedFolder]="node.selected" (mouseleave)="hideAction(node)" (mouseenter)="showAction(node)" (click)="selectFolder(node)"> <span style="flex:1;overflow: hidden;text-overflow: ellipsis;"> - <span class="treeLabel" [title]="node.label"> + <span class="treeLabel" [class.private]="!node.public" [title]="node.label" [class.drag]="node.drag"> {{node.label}} </span> </span> - <button mat-icon-button [matMenuTriggerFor]="menu" (click)="$event.stopPropagation();" - [disabled]="!node.showAction"> + <button mat-icon-button [matMenuTriggerFor]="menu" (click)="$event.stopPropagation();" [disabled]="!node.showAction"> <mat-icon *ngIf="node.showAction" class="folderAction fa fa-ellipsis-v"></mat-icon> <span *ngIf="!node.showAction"> - 4 + {{node.countRes}} </span> </button> <mat-menu #menu="matMenu"> - <button mat-menu-item (click)="addNewItem(node)">Nouveau sous-dossier</button> + <button mat-menu-item [disabled]="createItemNode" (click)="addNewItem(node)">Nouveau sous-dossier</button> <button mat-menu-item (click)="deleteNode(node)">Supprimer</button> </mat-menu> </a> </mat-tree-node> </mat-tree> -</mat-nav-list> \ No newline at end of file +</mat-nav-list> +<mat-nav-list> + <mat-list-item role="listitem" disableRipple (click)="toggleInput()" class="more"> + <mat-icon class="fa {{createRootNode ? 'fa-chevron-up' : 'fa-chevron-down'}}"></mat-icon> {{createRootNode ? 'Moins' : 'Plus'}} + </mat-list-item> +</mat-nav-list> +<mat-list *ngIf="createRootNode"> + <mat-list-item role="listitem"> + <form> + <mat-form-field floatLabel="never" style="width: 100%;"> + <input matInput id="itemValue" #itemValue placeholder="Nouveau dossier racine..."> + <button type="submit" matSuffix mat-icon-button (click)="createRoot(itemValue.value);createRootNode = !createRootNode" + [disabled]="itemValue.value === ''"> + <mat-icon class="fa fa-check" color="accent" [title]="lang.add"></mat-icon> + </button> + </mat-form-field> + </form> + </mat-list-item> +</mat-list> \ No newline at end of file diff --git a/src/frontend/app/folder/folder-tree.component.scss b/src/frontend/app/folder/folder-tree.component.scss index 0ef026b6193..852ef3b3eef 100644 --- a/src/frontend/app/folder/folder-tree.component.scss +++ b/src/frontend/app/folder/folder-tree.component.scss @@ -72,4 +72,18 @@ form { .treeLabel { white-space: pre; color: $primary; +} + +.drag { + color: $secondary; +} + +.more { + ::ng-deep.mat-list-item-content { + justify-content: center; + } +} + +.private { + color: $warn; } \ No newline at end of file diff --git a/src/frontend/app/folder/folder-tree.component.ts b/src/frontend/app/folder/folder-tree.component.ts index 24636d70b87..b9461d25fb3 100644 --- a/src/frontend/app/folder/folder-tree.component.ts +++ b/src/frontend/app/folder/folder-tree.component.ts @@ -1,10 +1,10 @@ -import { Component, OnInit, ViewChild, Input } from '@angular/core'; +import { Component, OnInit, ViewChild, Input, Renderer2 } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { LANG } from '../translate.component'; -import { map, tap, catchError, filter, exhaustMap } from 'rxjs/operators'; +import { map, tap, catchError, filter, exhaustMap, finalize } from 'rxjs/operators'; import { FlatTreeControl } from '@angular/cdk/tree'; import { trigger, transition, style, animate } from '@angular/animations'; -import { MatTreeFlatDataSource, MatTreeFlattener, MatDialog, MatDialogRef } from '@angular/material'; +import { MatTreeFlatDataSource, MatTreeFlattener, MatDialog, MatDialogRef, MatInput } from '@angular/material'; import { BehaviorSubject, of } from 'rxjs'; import { NotificationService } from '../notification.service'; import { ConfirmComponent } from '../../plugins/modal/confirm.component'; @@ -19,6 +19,8 @@ export class ItemNode { children: ItemNode[]; label: string; parent_id: number; + public: boolean; + countRes: number; } /** Flat to-do item node with expandable and level information */ @@ -26,7 +28,9 @@ export class ItemFlatNode { id: number; label: string; parent_id: number; + countRes: number; level: number; + public: boolean; expandable: boolean; } @Component({ @@ -56,9 +60,13 @@ export class FolderTreeComponent implements OnInit { lang: any = LANG; TREE_DATA: any[] = []; dialogRef: MatDialogRef<any>; + createRootNode: boolean = false; + createItemNode: boolean = false; dataChange = new BehaviorSubject<ItemNode[]>([]); @Input('selectedId') seletedId: number; + @ViewChild('itemValue') itemValue: MatInput; + get data(): ItemNode[] { return this.dataChange.value; } @@ -74,6 +82,8 @@ export class FolderTreeComponent implements OnInit { ? existingNode : new ItemFlatNode(); flatNode.label = node.label; + flatNode.countRes = node.countRes; + flatNode.public = node.public; flatNode.parent_id = node.parent_id; flatNode.id = node.id; flatNode.level = level; @@ -98,6 +108,7 @@ export class FolderTreeComponent implements OnInit { private notify: NotificationService, private dialog: MatDialog, private router: Router, + private renderer: Renderer2 ) { } ngOnInit(): void { @@ -124,8 +135,6 @@ export class FolderTreeComponent implements OnInit { openTree(id: number) { let indexSelectedFolder = this.treeControl.dataNodes.map((folder: any) => folder.id).indexOf(id); - //this.treeControl.dataNodes[indexSelectedFolder].selected = true; - console.log(indexSelectedFolder); while (indexSelectedFolder != -1) { indexSelectedFolder = this.treeControl.dataNodes.map((folder: any) => folder.id).indexOf(this.treeControl.dataNodes[indexSelectedFolder].parent_id); @@ -217,6 +226,7 @@ export class FolderTreeComponent implements OnInit { } addNewItem(node: any) { + this.createItemNode = true; const currentNode = this.flatNodeMap.get(node); if (currentNode.children === undefined) { currentNode['children'] = []; @@ -225,10 +235,10 @@ export class FolderTreeComponent implements OnInit { this.dataChange.next(this.data); this.treeControl.expand(node); + this.renderer.selectRootElement('#itemValue').focus(); } saveNode(node: any, value: any) { - console.log(node); this.http.post("../../rest/folders", { label: value, parent_id: node.parent_id }).pipe( tap((data: any) => { const nestedNode = this.flatNodeMap.get(node); @@ -237,6 +247,7 @@ export class FolderTreeComponent implements OnInit { this.dataChange.next(this.data); this.treeControl.collapseAll(); this.openTree(nestedNode.id); + this.createItemNode = false; }), tap(() => this.notify.success(this.lang.folderAdded)), catchError((err) => { @@ -308,4 +319,44 @@ export class FolderTreeComponent implements OnInit { } return null; } + + drop(ev: any, node: any) { + this.dialogRef = this.dialog.open(ConfirmComponent, { autoFocus: false, disableClose: true, data: { title: 'Classer '+ ev.item.data.alt_identifier, msg: 'Voulez-vous classer <b>'+ ev.item.data.alt_identifier+'</b> dans <b>' + node.label+ '</b> ?'} }); + + this.dialogRef.afterClosed().pipe( + filter((data: string) => data === 'ok'), + //exhaustMap(() => this.http.post("../../rest/folders/" + node.id)), + tap(() => { + node.countRes = node.countRes + 1; + }), + tap(() => this.notify.success('Courrier classé')), + finalize(() => node.drag = false), + catchError((err) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + + dragEnter(node: any) { + node.drag=true; + } + + getDragIds() { + if (this.treeControl.dataNodes !== undefined) { + return this.treeControl.dataNodes.map(node => 'folder-list-'+node.id); + } else { + return []; + } + + } + + toggleInput() { + this.createRootNode = !this.createRootNode; + if (this.createRootNode) { + setTimeout(() => { + this.renderer.selectRootElement('#itemValue').focus(); + }, 0); + } + } } diff --git a/src/frontend/app/folder/panel/panel-folder.component.html b/src/frontend/app/folder/panel/panel-folder.component.html index d69b693f83b..1236cb99dd2 100644 --- a/src/frontend/app/folder/panel/panel-folder.component.html +++ b/src/frontend/app/folder/panel/panel-folder.component.html @@ -4,5 +4,5 @@ {{lang.folders}} </mat-panel-title> </mat-expansion-panel-header> - <folder-tree [selectedId]="id"></folder-tree> + <folder-tree #folderTree [selectedId]="id"></folder-tree> </mat-expansion-panel> diff --git a/src/frontend/app/folder/panel/panel-folder.component.ts b/src/frontend/app/folder/panel/panel-folder.component.ts index 884195a3656..416b8abc671 100644 --- a/src/frontend/app/folder/panel/panel-folder.component.ts +++ b/src/frontend/app/folder/panel/panel-folder.component.ts @@ -1,5 +1,6 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit, Input, ViewChild } from '@angular/core'; import { LANG } from '../../translate.component'; +import { FolderTreeComponent } from '../folder-tree.component'; declare function $j(selector: any): any; @@ -13,8 +14,13 @@ export class PanelFolderComponent implements OnInit { lang: any = LANG; @Input('selectedId') id: number; + @ViewChild('folderTree') folderTree: FolderTreeComponent; constructor() { } ngOnInit(): void { } + + getDragIds() { + return this.folderTree.getDragIds(); + } } diff --git a/src/frontend/app/list/basket-list.component.html b/src/frontend/app/list/basket-list.component.html index 3b23acbbc0a..574210807d3 100644 --- a/src/frontend/app/list/basket-list.component.html +++ b/src/frontend/app/list/basket-list.component.html @@ -8,7 +8,7 @@ <basket-home *ngIf="homeData" #basketHome (refreshEvent)="refreshDao()" [homeData]="homeData" [snavL]="snav" [currentBasketInfo]="currentBasketInfo"></basket-home> <mat-divider></mat-divider> - <panel-folder></panel-folder> + <panel-folder #panelFolder></panel-folder> </mat-sidenav> <mat-sidenav-content> <mat-card id="viewThumbnail" style="display:none;position: fixed;z-index: 2;margin-left: 1px;"><img style="max-height: 100vh;" src="{{thumbnailUrl}}" /></mat-card> @@ -62,7 +62,7 @@ </div> </div> <div style="height:90%;overflow:auto;position:absolute;width:100%;"> - <table #tableBasketListSort="matSort" mat-table [dataSource]="data" matSort + <table cdkDropList id="list-1" [cdkDropListConnectedTo]="panelFolder.getDragIds()" [cdkDropListData]="data" #tableBasketListSort="matSort" mat-table [dataSource]="data" matSort matSortActive="res_id" matSortDisableClear matSortDirection="asc" style="width:100%;"> <ng-container matColumnDef="res_id"> @@ -169,7 +169,10 @@ </td> </ng-container> <tr mat-row *matRowDef="let row; columns: displayedColumnsBasket;" - (contextmenu)="open($event,row);" [class.locked]="row.isLocked == true"></tr> + (contextmenu)="open($event,row);" class="rowData" [class.locked]="row.isLocked == true" cdkDrag [cdkDragData]="row" > + <div class="example-custom-placeholder" *cdkDragPlaceholder></div> + <div class="dragPreview" *cdkDragPreview><i class="fas fa-envelope-open-text fa-2x"></i><br/>Classer <b>{{row.alt_identifier}}</b> dans un dossier</div> + </tr> </table> </div> <div class="table-head"> diff --git a/src/frontend/app/list/basket-list.component.scss b/src/frontend/app/list/basket-list.component.scss index 20fd4a85a61..6f5e9e40301 100644 --- a/src/frontend/app/list/basket-list.component.scss +++ b/src/frontend/app/list/basket-list.component.scss @@ -1,3 +1,5 @@ +@import '../../css/vars.scss'; + .align_leftData { text-align: left; } @@ -46,3 +48,12 @@ font-weight: bold; opacity: 0.6; } + +.dragPreview { + text-align: center; + border-radius:5px; + background: white; + padding: 10px; + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),0 8px 10px 1px rgba(0, 0, 0, 0.14),0 3px 14px 2px rgba(0, 0, 0, 0.12); + color: $primary; +} \ No newline at end of file diff --git a/src/frontend/app/list/basket-list.component.ts b/src/frontend/app/list/basket-list.component.ts index 5f0237936c2..72c98b60018 100755 --- a/src/frontend/app/list/basket-list.component.ts +++ b/src/frontend/app/list/basket-list.component.ts @@ -17,6 +17,7 @@ import { Overlay } from '@angular/cdk/overlay'; import { BasketHomeComponent } from '../basket/basket-home.component'; import { PanelListComponent } from './panel/panel-list.component'; import { AppService } from '../../service/app.service'; +import { PanelFolderComponent } from '../folder/panel/panel-folder.component'; declare function $j(selector: any): any; @@ -87,6 +88,7 @@ export class BasketListComponent implements OnInit { @ViewChild('filtersTool') filtersTool: FiltersToolComponent; @ViewChild('appPanelList') appPanelList: PanelListComponent; @ViewChild('basketHome') basketHome: BasketHomeComponent; + @ViewChild('panelFolder') panelFolder: PanelFolderComponent; currentSelectedChrono: string = ''; @@ -173,6 +175,10 @@ export class BasketListComponent implements OnInit { this.currentBasketInfo.basket_id = data.basket_id; this.defaultAction = data.defaultAction; this.headerService.setHeader(data.basketLabel); + setTimeout(() => { + console.log(this.panelFolder.getDragIds()); + }, 400); + return data.resources; }), catchError((err: any) => { -- GitLab