Skip to content
Snippets Groups Projects
maarch-flat-tree.component.ts 11.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • import { NestedTreeControl } from '@angular/cdk/tree';
    import { Component, Input, OnInit, HostListener, Output, EventEmitter } from '@angular/core';
    import { MatTreeNestedDataSource } from '@angular/material/tree';
    import { SortPipe } from '../../plugins/sorting.pipe';
    import { FormControl } from '@angular/forms';
    import { tap, debounceTime } from 'rxjs/operators';
    import { LatinisePipe } from 'ngx-pipes';
    
    
    /** Flat node with expandable and level information */
    interface ExampleFlatNode {
        expandable: boolean;
        item: string;
        parent_id: string;
        level: number;
    }
    
    /**
     * @title Tree with flat nodes
     */
    @Component({
        selector: 'app-maarch-flat-tree',
        templateUrl: 'maarch-flat-tree.component.html',
        styleUrls: ['maarch-flat-tree.component.scss'],
        providers: [SortPipe],
    })
    export class MaarchFlatTreeComponent implements OnInit {
    
        @Input() rawData: any = [];
    
        @Output() afterSelectNode = new EventEmitter<any>();
        @Output() afterDeselectNode = new EventEmitter<any>();
    
        holdShift: boolean = false;
    
        treeControl = new NestedTreeControl<any>(node => node.children);
        dataSource = new MatTreeNestedDataSource<any>();
    
        searchMode: boolean = false;
        searchTerm: FormControl = new FormControl('');
    
        lastSelectedNodeIds: any[] = [];
    
        @HostListener('document:keydown.Shift', ['$event']) onKeydownHandler(event: KeyboardEvent) {
            this.holdShift = true;
        }
        @HostListener('document:keyup.Shift', ['$event']) onKeyupHandler(event: KeyboardEvent) {
            this.holdShift = false;
        }
    
        constructor(
            private sortPipe: SortPipe,
            private latinisePipe: LatinisePipe,
        ) { }
    
        ngOnInit(): void {
            // SAMPLE
            /* this.rawData = [
                {
                    id: '46',
                    text: 'bonjour',
                    parent_id: null,
                    icon: 'fa fa-building',
                    state: {
                        selected: true,
                    }
                },
                {
                    id: '42',
                    text: 'coucou',
                    parent_id: '46',
                    icon: 'fa fa-building',
                    state: {
                        selected: true,
                    }
                },
                {
                    id: '41',
                    text: 'coucou',
                    parent_id: '42',
                    icon: 'fa fa-building',
                    state: {
                        selected: true,
                    }
                },
                {
                    id: '1',
                    text: 'Compétences fonctionnelles',
                    parent_id: null,
                    icon: 'fa fa-building',
                    state: {
                        selected: true,
                    }
                },
                {
                    id: '232',
                    text: 'Compétences technique',
                    parent_id: null,
                    icon: 'fa fa-building',
                    state: {
                        selected: true,
                    }
                }
            ]; */
            if (this.rawData.length > 0) {
                this.initData();
            }
        }
    
        initData(data: any = this.rawData) {
            this.rawData = data.map((item: any) => {
                return {
                    ...item,
                    parent_id : item.parent_id === '#' ? null : item.parent_id,
                    state: {
                        selected: item.state.selected,
                        opened: item.state.opened,
                        disabled: item.state.disabled,
                    }
                };
            });
    
            this.rawData = this.sortPipe.transform(this.rawData, 'parent_id');
    
            const nestedData = this.flatToNestedObject(this.rawData);
    
            this.dataSource.data = nestedData;
            this.treeControl.dataNodes = nestedData;
    
            this.searchTerm.valueChanges
                .pipe(
                    debounceTime(300),
                    // filter(value => value.length > 2),
                    tap((filterValue: any) => {
                        filterValue = filterValue.trim(); // Remove whitespace
                        filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
                        this.searchNode(this.dataSource.data, filterValue);
                    }),
                ).subscribe();
        }
    
        getData(id: any) {
            return this.rawData.filter((elem: any) => elem.id === id)[0];
        }
    
        getIteration(it: number) {
            return Array(it).fill(0).map((x, i) => i);
        }
    
        flatToNestedObject(data: any) {
            const nested = data.reduce((initial: any, value: any, index: any, original: any) => {
                if (value.parent_id === '') {
                    if (initial.left.length) {
                        this.checkLeftOvers(initial.left, value);
                    }
                    delete value.parent_id;
                    value.root = true;
                    initial.nested.push(value);
                    initial.nested = this.sortPipe.transform(initial.nested, 'text');
                    initial.nested = initial.nested.map((info: any, indexPar: number) => {
                        return {
                            ...info,
                            last: initial.nested.length - 1 === indexPar,
                        };
                    });
                } else {
                    const parentFound = this.findParent(initial.nested, value);
                    if (parentFound) {
                        this.checkLeftOvers(initial.left, value);
                    } else {
                        initial.left.push(value);
                    }
                }
                return index < original.length - 1 ? initial : initial.nested;
            }, { nested: [], left: [] });
            return nested;
        }
    
        checkLeftOvers(leftOvers: any, possibleParent: any) {
            for (let i = 0; i < leftOvers.length; i++) {
                if (leftOvers[i].parent_id === possibleParent.id) {
                    // delete leftOvers[i].parent_id;
                    possibleParent.children ? possibleParent.children.push(leftOvers[i]) : possibleParent.children = [leftOvers[i]];
                    possibleParent.count = possibleParent.children.length;
                    const addedObj = leftOvers.splice(i, 1);
                    this.checkLeftOvers(leftOvers, addedObj[0]);
                }
            }
        }
    
        findParent(possibleParents: any, possibleChild: any): any {
            let found = false;
            for (let i = 0; i < possibleParents.length; i++) {
                if (possibleParents[i].id === possibleChild.parent_id) {
                    found = true;
                    // delete possibleChild.parent_id;
                    if (possibleParents[i].children) {
                        possibleParents[i].children.push(possibleChild);
                    } else {
                        possibleParents[i].children = [possibleChild];
                    }
                    possibleParents[i].count = possibleParents[i].children.length;
                    possibleParents[i].children = this.sortPipe.transform(possibleParents[i].children, 'text');
                    possibleParents[i].children = possibleParents[i].children.map((info: any, index: number) => {
                        return {
                            ...info,
                            last: possibleParents[i].children.length - 1 === index,
                        };
                    });
                    return true;
                } else if (possibleParents[i].children) {
                    found = this.findParent(possibleParents[i].children, possibleChild);
                }
            }
            return found;
        }
    
        hasChild = (_: number, node: any) => !!node.children && node.children.length > 0;
    
        selectNode(node: any) {
            if (!node.state.disabled) {
                if (this.searchMode) {
                    this.searchMode = false;
                    this.searchTerm.setValue('');
                }
    
                this.lastSelectedNodeIds = [];
    
                if (this.holdShift) {
                    this.toggleNode(
                        this.dataSource.data,
                        {
                            selected: !node.state.selected,
                            opened: true
                        },
                        [node.id]
                    );
                } else {
                    node.state.selected = !node.state.selected;
                    this.lastSelectedNodeIds = [node];
                }
    
                if (node.state.selected) {
                    this.afterSelectNode.emit(this.lastSelectedNodeIds);
                } else {
                    this.afterDeselectNode.emit(this.lastSelectedNodeIds);
                }
            }
        }
    
        toggleNode(data, state, nodeIds) {
            // traverse throuh each node
            if (Array.isArray(data)) { // if data is an array
                data.forEach((d) => {
    
                    if (nodeIds.indexOf(d.id) > -1 || (this.holdShift && nodeIds.indexOf(d.parent_id) > -1)) {
                        Object.keys(state).forEach(key => {
                            if (d.state.disabled && key === 'opened') {
                                d.state[key] = state[key];
                            } else if (!d.state.disabled) {
                                d.state[key] = state[key];
                                if (key === 'selected') {
                                    this.lastSelectedNodeIds.push(d);
                                }
                            }
                        });
                    }
                    if (this.holdShift && nodeIds.indexOf(d.parent_id) > -1) {
                        nodeIds.push(d.id);
                    }
    
                    this.toggleNode(d, state, nodeIds);
    
                }); // call the function on each item
            } else if (data instanceof Object) { // otherwise, if data is an object
                (data.children || []).forEach((f) => {
                    if (nodeIds.indexOf(f.id) > -1 || (this.holdShift && nodeIds.indexOf(f.parent_id) > -1)) {
                        Object.keys(state).forEach(key => {
                            if (f.state.disabled && key === 'opened') {
                                f.state[key] = state[key];
                            } else if (!f.state.disabled) {
                                f.state[key] = state[key];
                                if (key === 'selected') {
                                    this.lastSelectedNodeIds.push(f);
                                }
                            }
                        });
                    }
                    if (this.holdShift && nodeIds.indexOf(f.parent_id) > -1) {
                        nodeIds.push(f.id);
                    }
                    this.toggleNode(f, state, nodeIds);
    
                }); // and call function on each child
            }
        }
    
        searchNode(data, term) {
            this.searchMode = term !== '';
            // traverse throuh each node
            if (Array.isArray(data)) { // if data is an array
                data.forEach((d) => {
                    d.state.opened = true;
                    if (this.latinisePipe.transform(d.text.toLowerCase()).indexOf(this.latinisePipe.transform(term)) > -1) {
                        d.state.search = true;
                    } else if (term === '') {
                        delete d.state.search;
                    } else {
                        d.state.search = false;
                    }
                    this.searchNode(d, term);
    
                }); // call the function on each item
            } else if (data instanceof Object) { // otherwise, if data is an object
                (data.children || []).forEach((f) => {
                    f.state.opened = true;
                    if (this.latinisePipe.transform(f.text.toLowerCase()).indexOf(this.latinisePipe.transform(term)) > -1) {
                        f.state.search = true;
                    } else if (term === '') {
                        delete f.state.search;
                    } else {
                        f.state.search = false;
                    }
                    this.searchNode(f, term);
    
                }); // and call function on each child
            }
        }
    
        getSelectedNodes() {
            return this.rawData.filter((data: any) => data.state.selected);
        }
    }