UNPKG

@linid-dm/directory-manager-client-core

Version:

Core package by providing a set of angular components for the Directory Manager app.

591 lines 147 kB
/** * Copyright (C) 2020-2024 Linagora * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version, provided you comply with the Additional Terms applicable for * LinID Directory Manager software by LINAGORA pursuant to Section 7 of the GNU * Affero General Public License, subsections (b), (c), and (e), pursuant to * which these Appropriate Legal Notices must notably (i) retain the display of * the "LinID™" trademark/logo at the top of the interface window, the display * of the “You are using the Open Source and free version of LinID™, powered by * Linagora © 2009–2013. Contribute to LinID R&D by subscribing to an Enterprise * offer!” infobox and in the e-mails sent with the Program, notice appended to * any type of outbound messages (e.g. e-mail and meeting requests) as well as * in the LinID Directory Manager user interface, (ii) retain all hypertext * links between LinID Directory Manager and https://linid.org/, as well as * between LINAGORA and LINAGORA.com, and (iii) refrain from infringing LINAGORA * intellectual property rights over its trademarks and commercial brands. Other * Additional Terms apply, see <http://www.linagora.com/licenses/> for more * details. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License and * its applicable Additional Terms for LinID Directory Manager along with this * program. If not, see <http://www.gnu.org/licenses/> for the GNU Affero * General Public License version 3 and <http://www.linagora.com/licenses/> for * the Additional Terms applicable to the LinID Directory Manager software. */ import { trigger } from '@angular/animations'; import { SelectionModel } from '@angular/cdk/collections'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { FlatTreeControl } from '@angular/cdk/tree'; import { Component, EventEmitter, Input, Output, ViewChild, } from '@angular/core'; import { MatTreeFlatDataSource, MatTreeFlattener, } from '@angular/material/tree'; import _ from 'lodash'; import { Subject, delay, filter, takeUntil, tap } from 'rxjs'; import { Data, ETreeSelectMode, EUserAction, addNodeInDataTree, cloneDataTree, fadeTree, filterTree, getAllChildrenLength, getFirstDisplayedIndex, getFlattenDisplayedTreeIds, getFlattenTree, getGreatestNodesMinWidth, getNodeById, getSelectedNodes, isNodeMatchingSearch, setNodeIdWidthMap, updateNodeName, updateSubTreelinks, } from '../../shared'; import * as i0 from "@angular/core"; import * as i1 from "@ngxs/store"; import * as i2 from "@angular/common"; import * as i3 from "@angular/router"; import * as i4 from "@angular/flex-layout/flex"; import * as i5 from "@angular/flex-layout/extended"; import * as i6 from "@angular/cdk/scrolling"; import * as i7 from "@angular/material/button"; import * as i8 from "@angular/material/checkbox"; import * as i9 from "@angular/material/icon"; import * as i10 from "@angular/material/tree"; import * as i11 from "../../shared/directives/color-search-letters.directive"; export class GenericTreeComponent { set initialDataTree(initialDataTree) { if (initialDataTree) { this._initialDataTree = initialDataTree; this.updateDataSourceOnInitAndOnSearch(); } } get initialDataTree() { return this._initialDataTree; } set search(search) { this._search = search; if (!this.isSettingData) { this.updateDataSourceOnInitAndOnSearch(); setTimeout(() => { this.enableAnimation = false; this.addedChildrenIds = []; }, 1); } } get search() { return this._search; } set selectedResourceRootTypeId(selectedResourceRootTypeId) { if (selectedResourceRootTypeId) { this.isSettingData = true; this.dataSource.data = []; this.flattenDataTreeIds = []; this.nodesIdWidth = new Map(); this.nodesMinWidthAsNumber = 0; this._selectedResourceRootTypeId = selectedResourceRootTypeId; } } get selectedResourceRootTypeId() { return this._selectedResourceRootTypeId; } set refresh(refresh) { if (refresh) { this.isSettingData = true; this._refresh = refresh; refresh .pipe(takeUntil(this._onDestroy$), filter((_) => !this.isSettingData), delay(10), tap((refreshActionDetail) => { if (!!refreshActionDetail) { this.updateDataTreeOnRefresh(refreshActionDetail); this.updateFlattenTreePropertiesOnRefresh(refreshActionDetail); this.updateDataSource(); } }), delay(1), tap((refreshActionDetail) => { if (refreshActionDetail.actionType === EUserAction.TREE_ADD || (refreshActionDetail.actionType === EUserAction.TREE_DELETE && refreshActionDetail.hasRemovedSelectedNode) || (refreshActionDetail.actionType === EUserAction.TREE_MOVE_NODE && refreshActionDetail.hasMovedSelectedNode)) { this.scrollToSelectedNode(); } })) .subscribe(); } } get refresh() { return this._refresh; } set advancedSearch(_advancedSearch) { if (!this.isSettingData && this.isAdvancedSearchActivated) { this.updateDataSourceOnInitAndOnSearch(); setTimeout(() => { this.enableAnimation = false; this.addedChildrenIds = []; }, 1); } } set isLargeScreen(isLargeScreen) { this._isLargeScreen = isLargeScreen; if (!this.isSettingData) { this.viewportMinWidth = 'inherit'; setTimeout((_) => this.getVirtualScrollViewportMinWidth(), 1); } } get isLargeScreen() { return this._isLargeScreen; } constructor(_store$) { this._store$ = _store$; this.isSelectOnlyModeEnabled = false; this.isAdvancedSearchActivated = false; this.nodesIdsMatchingAdvancedSearch = {}; this.selectedNodesId = []; this.isFilteringOnExternalId = false; this.isScrollDisable = false; this.widthUpdated = new EventEmitter(); this.selectedNode = new EventEmitter(); this.selectedNodes = new EventEmitter(); this._onDestroy$ = new Subject(); this._initialDataTree = []; this._flattenDataTree = []; this._mapDataNodeById = {}; this._hasMadeInitialScroll = false; this.isSettingData = false; this.viewportMinWidth = '100%'; this.STANDARD_NODE_PADDING_LEFT = 20; this.STANDARD_NODE_HEIGHT = 40; // Workaround to avoid scrollbar in mat-dialog-content this.MAX_WINDOW_HEIGHT_PERCENTAGE = 0.5; this.MAX_NB_DISPLAYED_NODES = 50; this.nbDisplayedNodes = 0; this.firstDisplayedIndex = 0; this.enableAnimation = true; this.addedChildrenIds = []; this.nodesIdWidth = new Map(); this.nodesMinWidthAsNumber = 0; this.allTreeSelectMode = ETreeSelectMode; this.checklistSelection = new SelectionModel(true); this._getLevel = (node) => node.level; this._isExpandable = (node) => node.expandable; this._getChildren = (node) => node.children; this.hasChild = (_, node) => node.expandable; this._transformer = (node, level) => { return { ...node, expandable: !!node.children && node.children.length > 0, level, }; }; this.treeControl = new FlatTreeControl(this._getLevel, this._isExpandable); this.treeFlattener = new MatTreeFlattener(this._transformer, this._getLevel, this._isExpandable, this._getChildren); this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); } ngAfterViewInit() { this.virtualScroll.renderedRangeStream.subscribe((range) => { if (this.firstDisplayedIndex !== range.start) { this.firstDisplayedIndex = range.start; this.updateDataSource(); } }); } ngOnDestroy() { this._onDestroy$.next(); this._onDestroy$.complete(); } expand(selectedNodesIds) { const mapFlatDataNodeById = _.fromPairs(this.treeControl.dataNodes.map((node) => [node.ids.id, node])); selectedNodesIds.forEach((id) => { if (!!mapFlatDataNodeById[id]) { this.treeControl.expand(mapFlatDataNodeById[id]); } }); } getNodesForDeleteActions(node) { let actionsKeys = Object.keys(this.actions[node.type].primitives); if (node.children.length === 0 || (!actionsKeys.includes('deleteChild') && !actionsKeys.includes('deleteChildren'))) { return [{ ...node.ids, label: node.name }]; } else { return node.children.map((child) => ({ ...child.ids, label: child.name, })); } } getNbChildren(children) { return children.length > 1 ? children.length : children.some((child) => child.children.length > 0) ? 2 : children.length; } getLink(node) { return node.link.toLowerCase(); } getActiveNode(node) { this.activeNode = node; this.selectedNode.emit(this.activeNode); } onToggleFold(toggledNode) { this.enableAnimation = true; this.updateFlattenTreePropertiesOnToggle(toggledNode); const isCollapsing = this.treeControl.isExpanded(toggledNode); this.updateDataSourceOnToggle(isCollapsing); if (isCollapsing) { this.treeControl.collapse(toggledNode); } this.expand([...this.expandedNodesIds]); if (this.isSelectOnlyModeEnabled) { this._setVirtualScrollViewportHeight(); } else { this._store$.dispatch(new Data.ToggleFoldTreeNode({ treeNodeId: toggledNode.ids.id, isFolding: !this.treeControl.isExpanded(toggledNode), dataTypeId: this.selectedResourceRootTypeId, })); } this.getVirtualScrollViewportMinWidth(); this.enableAnimation = false; this.addedChildrenIds = []; } setEntryIdsInStore(ids, parentIds) { this._store$.dispatch([ new Data.SetSelectedEntryIds({ ids }), new Data.SetSelectedEntryParentIds({ ids: parentIds }), ]); } buildDisplayedTree(nodesByParentId, parentIdForDisplay = 'root') { return nodesByParentId[parentIdForDisplay].map((node) => { let children = node.children; if (this.expandedNodesIds.has(node.ids.id) && !!nodesByParentId[node.ids.id]) { const displayedChildren = this.buildDisplayedTree(nodesByParentId, node.ids.id); children = displayedChildren.length > 0 ? displayedChildren : children; } return { ...node, children, }; }); } setExpandedNodesIds() { if (!!this.expandedTreeNodesUuids) { this.expandedNodesIds = new Set(this.expandedTreeNodesUuids); } if (!!this.selectedNodeId) { getSelectedNodes(this.selectedNodeId, this.initialDataTree).forEach((id) => this.expandedNodesIds.add(id)); } } updateFlattenTreePropertiesOnToggle(node) { const updateStartIndex = this.flattenDataTreeIds.indexOf(node.ids.id) + 1; if (this.treeControl.isExpanded(node)) { const allChidrenLength = getAllChildrenLength(getNodeById(node.ids.id, this.dataTree), [...this.expandedNodesIds]); this.flattenDataTreeIds.splice(updateStartIndex, allChidrenLength); this.expandedNodesIds.delete(node.ids.id); } else { this.addedChildrenIds = getFlattenDisplayedTreeIds(getNodeById(node.ids.id, this.dataTree).children, this.expandedNodesIds); this.flattenDataTreeIds.splice(updateStartIndex, 0, ...this.addedChildrenIds); this.nbDisplayedNodes = this.flattenDataTreeIds.length - this.firstDisplayedIndex > this.MAX_NB_DISPLAYED_NODES ? this.MAX_NB_DISPLAYED_NODES : this.flattenDataTreeIds.length - this.firstDisplayedIndex; this.expandedNodesIds.add(node.ids.id); } this.flattenDataTreeIds = [...this.flattenDataTreeIds]; } updateDataTreeOnSetAndOnSearch() { if (!this.isSelectOnlyModeEnabled && (this.search !== '' || this.isAdvancedSearchActivated)) { this.filteredDataTree = filterTree(this.initialDataTree, this.allNodesIdsNames, this.search, this.isFilteringOnExternalId, this.isAdvancedSearchActivated, this.nodesIdsMatchingAdvancedSearch); this.dataTree = cloneDataTree(this.filteredDataTree); } else { this.dataTree = cloneDataTree(this.initialDataTree); } } updateFlattenTreePropertiesOnSetAndOnSearch() { if (!this.isSelectOnlyModeEnabled && (this.search !== '' || this.isAdvancedSearchActivated)) { this.expandedNodesIds = new Set(this.allNodesIdsNames.map((nodeIds) => nodeIds.ids.id)); } else { this.expandedNodesIds = new Set(); if (!this.isSelectOnlyModeEnabled) { this.setExpandedNodesIds(); } } this._flattenDataTree = this.search !== '' || this.isAdvancedSearchActivated ? getFlattenTree(this.dataTree) : this.flattenInitialDataTree; this._mapDataNodeById = _.fromPairs(this._flattenDataTree.map((node) => [node.ids.id, node])); this.flattenDataTreeIds = getFlattenDisplayedTreeIds(this.dataTree, this.expandedNodesIds); } updateDataTreeOnRefresh(refreshActionDetail) { if (this.search !== '' || this.isAdvancedSearchActivated) { switch (refreshActionDetail.actionType) { case EUserAction.TREE_ADD: if (isNodeMatchingSearch(this.search, refreshActionDetail.addedNode.name, this.isFilteringOnExternalId, refreshActionDetail.addedNode.ids, this.isAdvancedSearchActivated, this.nodesIdsMatchingAdvancedSearch)) { addNodeInDataTree(this.filteredDataTree, refreshActionDetail.triggerNodeId, refreshActionDetail.addedNode); } break; case EUserAction.TREE_DELETE: this.filteredDataTree = filterTree(this.initialDataTree, this.allNodesIdsNames, this.search, this.isFilteringOnExternalId, this.isAdvancedSearchActivated, this.nodesIdsMatchingAdvancedSearch); break; case EUserAction.TREE_UPDATE: const selectedNodeIds = this.allNodesIdsNames.find((nodeIds) => nodeIds.ids.id === this.selectedNodeId); const isUpdatedNodeMatchingSearch = isNodeMatchingSearch(this.search, refreshActionDetail.newNodeName ?? selectedNodeIds.name, this.isFilteringOnExternalId, { id: refreshActionDetail.triggerNodeId, externalId: refreshActionDetail.newNodeExternalId, } ?? selectedNodeIds.ids, this.isAdvancedSearchActivated, this.nodesIdsMatchingAdvancedSearch); if (refreshActionDetail.newNodeName && isUpdatedNodeMatchingSearch && this.flattenDataTreeIds.includes(refreshActionDetail.triggerNodeId)) { updateNodeName(refreshActionDetail.triggerNodeId, this.filteredDataTree, refreshActionDetail.newNodeName); } else if (refreshActionDetail.newNodeExternalId && isUpdatedNodeMatchingSearch && this.flattenDataTreeIds.includes(refreshActionDetail.triggerNodeId)) { updateSubTreelinks(refreshActionDetail.triggerNodeId, this.filteredDataTree, refreshActionDetail.newNodeExternalId); } else { this.filteredDataTree = filterTree(this.initialDataTree, this.allNodesIdsNames, this.search, this.isFilteringOnExternalId, this.isAdvancedSearchActivated, this.nodesIdsMatchingAdvancedSearch); } break; case EUserAction.TREE_MOVE_NODE: this.filteredDataTree = filterTree(this.initialDataTree, this.allNodesIdsNames, this.search, this.isFilteringOnExternalId, this.isAdvancedSearchActivated, this.nodesIdsMatchingAdvancedSearch); break; default: break; } } this.dataTree = cloneDataTree(this.search === '' && !this.isAdvancedSearchActivated ? this.initialDataTree : this.filteredDataTree); } updateFlattenTreePropertiesOnRefresh(refreshActionDetail) { let flattenDataTreeIdsBeforeUpdate = []; switch (refreshActionDetail.actionType) { case EUserAction.TREE_ADD: if (isNodeMatchingSearch(this.search, refreshActionDetail.addedNode.name, this.isFilteringOnExternalId, refreshActionDetail.addedNode.ids, this.isAdvancedSearchActivated, this.nodesIdsMatchingAdvancedSearch)) { if (this.search !== '' || this.isAdvancedSearchActivated) { this.expandedNodesIds.add(refreshActionDetail.addedNode.ids.id); } this.expandedNodesIds.add(refreshActionDetail.addedNode.parentIds.id); this.enableAnimation = true; this.addedChildrenIds.push(refreshActionDetail.addedNode.ids.id); } break; case EUserAction.TREE_DELETE: this.expandedNodesIds.forEach((nodeId) => { if (!this.allNodesIdsNames .map((nodeIds) => nodeIds.ids.id) .includes(nodeId)) { this.expandedNodesIds.delete(nodeId); } }); break; case EUserAction.TREE_UPDATE: const selectedNodeIds = this.allNodesIdsNames.find((nodeIds) => nodeIds.ids.id === this.selectedNodeId); const isUpdatedNodeMatchingSearch = isNodeMatchingSearch(this.search, refreshActionDetail.newNodeName ?? selectedNodeIds.name, this.isFilteringOnExternalId, { id: refreshActionDetail.triggerNodeId, externalId: refreshActionDetail.newNodeExternalId, } ?? selectedNodeIds.ids, this.isAdvancedSearchActivated, this.nodesIdsMatchingAdvancedSearch); if (isUpdatedNodeMatchingSearch && !this.flattenDataTreeIds.includes(refreshActionDetail.triggerNodeId)) { flattenDataTreeIdsBeforeUpdate = [...this.flattenDataTreeIds]; } break; case EUserAction.TREE_MOVE_NODE: const movedNode = getNodeById(refreshActionDetail.triggerNodeId, this.dataTree); const allMovedNodesIds = [ movedNode.ids.id, ...getFlattenDisplayedTreeIds(movedNode.children, this.expandedNodesIds), ]; if (this.search !== '' || this.isAdvancedSearchActivated || refreshActionDetail.destinationNode === null || (this.expandedNodesIds.has(refreshActionDetail.destinationNode.ids.id) && this.flattenDataTreeIds.includes(refreshActionDetail.destinationNode.ids.id))) { flattenDataTreeIdsBeforeUpdate = [...this.flattenDataTreeIds]; this.addedChildrenIds.push(...allMovedNodesIds); } break; default: break; } this._flattenDataTree = this.search !== '' || this.isAdvancedSearchActivated ? getFlattenTree(this.dataTree) : this.flattenInitialDataTree; this._mapDataNodeById = _.fromPairs(this._flattenDataTree.map((node) => [node.ids.id, node])); this.flattenDataTreeIds = getFlattenDisplayedTreeIds(this.dataTree, this.expandedNodesIds); if (flattenDataTreeIdsBeforeUpdate.length > 0) { this.enableAnimation = true; this.addedChildrenIds.push(..._.difference(this.flattenDataTreeIds, flattenDataTreeIdsBeforeUpdate)); } } getDisplayedNodes() { this.nbDisplayedNodes = 0; const displayedNodesIds = this.flattenDataTreeIds.slice(this.firstDisplayedIndex, this.firstDisplayedIndex + this.MAX_NB_DISPLAYED_NODES); this.nbDisplayedNodes = displayedNodesIds.length; return displayedNodesIds.map((id) => ({ ...this._mapDataNodeById[id], parentIdForDisplay: displayedNodesIds.includes(this._mapDataNodeById[id].parentIdForDisplay) ? this._mapDataNodeById[id].parentIdForDisplay : 'root', })); } _getTreeForDataSource() { const displayedNodes = this.getDisplayedNodes(); const nodesByParentIdForDisplay = _.groupBy(displayedNodes, 'parentIdForDisplay'); return Object.keys(nodesByParentIdForDisplay).length > 0 ? this.buildDisplayedTree(nodesByParentIdForDisplay) : []; } // TODO updateDataSource is call too many times on init tree updateDataSource() { this.setDataSource(); // TODO maybe find a solution for this setTimeout(() => { if (this.isSelectOnlyModeEnabled) { this._setVirtualScrollViewportHeight(); } this.getVirtualScrollViewportMinWidth(); }, 1); } setDataSource() { if (this.isSettingData && !this.isSelectOnlyModeEnabled) { this.firstDisplayedIndex = getFirstDisplayedIndex(this.flattenDataTreeIds, this.selectedNodeId, this.MAX_NB_DISPLAYED_NODES); setTimeout(() => { if (this.firstDisplayedIndex > 0 && !this._hasMadeInitialScroll) { this.scrollToSelectedNode(); this._hasMadeInitialScroll = true; } else if (this.firstDisplayedIndex === 0 || this._hasMadeInitialScroll) { this.dataSource.data = this._getTreeForDataSource(); this.expand([...this.expandedNodesIds]); if (!this._hasMadeInitialScroll) { this.scrollToSelectedNode(); } this._hasMadeInitialScroll = false; this.enableAnimation = false; this.addedChildrenIds = []; this.isSettingData = false; } }, 1); } else { this.dataSource.data = this._getTreeForDataSource(); this.checklistSelection = new SelectionModel(true, this.treeControl.dataNodes.filter((node) => this.selectedNodesId.includes(node.ids.id))); this.expand([...this.expandedNodesIds]); this.enableAnimation = false; this.addedChildrenIds = []; } } updateDataSourceOnToggle(isCollapsing) { if (isCollapsing) { this.treeControl.dataNodes.push(..._.differenceBy(this.treeFlattener.flattenNodes(this._getTreeForDataSource()), this.treeControl.dataNodes, (node) => node.ids.id)); } else { const lastDisplayedNodeIndex = this.firstDisplayedIndex + this.nbDisplayedNodes - 1; const mapFlatDataNodeIndexById = _.fromPairs(this.treeControl.dataNodes.map((node, index) => [ node.ids.id, { node, index }, ])); this.treeControl.dataNodes.splice(mapFlatDataNodeIndexById[this.flattenDataTreeIds[lastDisplayedNodeIndex]].index + getAllChildrenLength(mapFlatDataNodeIndexById[this.flattenDataTreeIds[lastDisplayedNodeIndex]].node) + 1); } } getVirtualScrollViewportMinWidth() { setNodeIdWidthMap(this.nodesIdWidth, this.flattenDataTreeIds, this.isSelectOnlyModeEnabled); this.nodesMinWidthAsNumber = getGreatestNodesMinWidth(this.nodesIdWidth, this.nodesMinWidthAsNumber); let standardMinWidth = 0; if (this.divMatTree) { if (!this.isSelectOnlyModeEnabled) { standardMinWidth = Number(getComputedStyle(this.divMatTree.nativeElement) .getPropertyValue('min-width') .replace('px', '')); } else { standardMinWidth = this.divMatTree.nativeElement.offsetWidth; } } if (this.nodesMinWidthAsNumber < standardMinWidth) { if (this.isSelectOnlyModeEnabled) { this.viewportMinWidth = '100%'; } else { this.nodesMinWidthAsNumber = standardMinWidth; this.viewportMinWidth = this.nodesMinWidthAsNumber + 'px'; } } else { if (this.isSelectOnlyModeEnabled || (!this.isSelectOnlyModeEnabled && this.isLargeScreen)) { this.viewportMinWidth = this.nodesMinWidthAsNumber + 'px'; } else { this.viewportMinWidth = '100%'; } } this.widthUpdated.emit(this.viewportMinWidth); } updateDataSourceOnInitAndOnSearch() { this.updateDataTreeOnSetAndOnSearch(); this.updateFlattenTreePropertiesOnSetAndOnSearch(); this.enableAnimation = true; this.addedChildrenIds = [...this.flattenDataTreeIds]; this.updateDataSource(); } scrollToSelectedNode() { if (this.virtualScroll) { const indexToScroll = this.flattenDataTreeIds.includes(this.selectedNodeId) ? this.flattenDataTreeIds.indexOf(this.selectedNodeId) : 0; this.virtualScroll.scrollToIndex(indexToScroll); } } _setVirtualScrollViewportHeight() { const displayedNodesHeight = (this.nbDisplayedNodes + 1) * this.STANDARD_NODE_HEIGHT; const maxTreeHeightInDialog = this.MAX_WINDOW_HEIGHT_PERCENTAGE * window.innerHeight; this.viewportHeight = displayedNodesHeight < maxTreeHeightInDialog || this.isScrollDisable ? displayedNodesHeight : maxTreeHeightInDialog; } isActiveNodeLink(nodeId, type = null, childrenType = null) { return ((!this.isSelectOnlyModeEnabled && nodeId === this.selectedNodeId) || (this.isSelectOnlyModeEnabled && this.selectMode === ETreeSelectMode.SINGLE && this.activeNode && this.activeNode.ids.id === nodeId && (childrenType === null || type === childrenType))); } onCheckMatBox(node) { this.checklistSelection.toggle(node); this.selectedNodes.emit(this.checklistSelection.selected); } onClickOnTreeNode(node) { if (this.isSelectOnlyModeEnabled && node.ids.id !== this.selectedNodeId && this.selectMode === ETreeSelectMode.SINGLE && node.type === this.selectedResourceTypeId) { this.getActiveNode(node); } else if (!this.isSelectOnlyModeEnabled) { this.setEntryIdsInStore(node.ids, node.parentIds); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.4", ngImport: i0, type: GenericTreeComponent, deps: [{ token: i1.Store }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.4", type: GenericTreeComponent, selector: "dm-generic-tree", inputs: { viewportHeight: "viewportHeight", allNodesIdsNames: "allNodesIdsNames", selectedNodeId: "selectedNodeId", flattenInitialDataTree: "flattenInitialDataTree", initialDataTree: "initialDataTree", search: "search", expandedTreeNodesUuids: "expandedTreeNodesUuids", actions: "actions", selectedResourceRootTypeId: "selectedResourceRootTypeId", selectedResourceTypeId: "selectedResourceTypeId", isSelectOnlyModeEnabled: "isSelectOnlyModeEnabled", isAdvancedSearchActivated: "isAdvancedSearchActivated", selectMode: "selectMode", refresh: "refresh", nodesIdsMatchingAdvancedSearch: "nodesIdsMatchingAdvancedSearch", advancedSearch: "advancedSearch", isLargeScreen: "isLargeScreen", selectedNodesId: "selectedNodesId", isFilteringOnExternalId: "isFilteringOnExternalId", isScrollDisable: "isScrollDisable", actionsMenuTemplate: "actionsMenuTemplate" }, outputs: { widthUpdated: "widthUpdated", selectedNode: "selectedNode", selectedNodes: "selectedNodes" }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true }, { propertyName: "divGenericTree", first: true, predicate: ["divGenericTree"], descendants: true }, { propertyName: "divMatTree", first: true, predicate: ["divMatTree"], descendants: true }, { propertyName: "virtualScroll", first: true, predicate: CdkVirtualScrollViewport, descendants: true }], ngImport: i0, template: "<!-- Copyright (C) 2020-2024 Linagora\n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU Affero General Public License as published by the Free\nSoftware Foundation, either version 3 of the License, or (at your option) any\nlater version, provided you comply with the Additional Terms applicable for\nLinID Directory Manager software by LINAGORA pursuant to Section 7 of the GNU\nAffero General Public License, subsections (b), (c), and (e), pursuant to\nwhich these Appropriate Legal Notices must notably (i) retain the display of\nthe \"LinID\u2122\" trademark/logo at the top of the interface window, the display\nof the \u201CYou are using the Open Source and free version of LinID\u2122, powered by\nLinagora \u00A9 2009\u20132013. Contribute to LinID R&D by subscribing to an Enterprise\noffer!\u201D infobox and in the e-mails sent with the Program, notice appended to\nany type of outbound messages (e.g. e-mail and meeting requests) as well as\nin the LinID Directory Manager user interface, (ii) retain all hypertext\nlinks between LinID Directory Manager and https://linid.org/, as well as\nbetween LINAGORA and LINAGORA.com, and (iii) refrain from infringing LINAGORA\nintellectual property rights over its trademarks and commercial brands. Other\nAdditional Terms apply, see <http://www.linagora.com/licenses/> for more\ndetails.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\ndetails.\n\nYou should have received a copy of the GNU Affero General Public License and\nits applicable Additional Terms for LinID Directory Manager along with this\nprogram. If not, see <http://www.gnu.org/licenses/> for the GNU Affero\nGeneral Public License version 3 and <http://www.linagora.com/licenses/> for\nthe Additional Terms applicable to the LinID Directory Manager software. -->\n\n<div\n #divGenericTree\n fxLayout=\"column\"\n [ngClass]=\"\n isSelectOnlyModeEnabled\n ? 'div-generic-tree-dialog'\n : 'div-generic-tree-component'\n \"\n>\n <div\n [ngClass]=\"\n isSelectOnlyModeEnabled\n ? 'div-mat-tree-dialog'\n : isLargeScreen\n ? 'div-mat-tree-lg-screen'\n : 'div-mat-tree-sm-screen'\n \"\n #divMatTree\n >\n <cdk-virtual-scroll-viewport\n [style.height.px]=\"viewportHeight\"\n [style.width]=\"viewportMinWidth\"\n [itemSize]=\"STANDARD_NODE_HEIGHT\"\n [minBufferPx]=\"viewportHeight\"\n >\n <ng-container\n *cdkVirtualFor=\"let item of flattenDataTreeIds\"\n ></ng-container>\n <mat-tree\n #tree\n [dataSource]=\"dataSource\"\n [treeControl]=\"treeControl\"\n class=\"gnc-tree\"\n >\n <!-- This is the template for the common part of any node -->\n <ng-template #nodeInfo let-node=\"node\">\n <div\n fxFlex\n fxLayout=\"row\"\n [ngClass]=\"\n isSelectOnlyModeEnabled &&\n (node.ids.id === selectedNodeId ||\n node.type !== selectedResourceTypeId)\n ? 'disabled-div'\n : ''\n \"\n class=\"data-info\"\n >\n <button\n fxFlexFill\n fxLayout=\"row\"\n fxLayoutAlign=\"start center\"\n [title]=\"node.name\"\n class=\"btn-node\"\n >\n <span\n appColorSearchedLetters\n [name]=\"node.name\"\n [nodeIds]=\"node.ids\"\n [title]=\"node.name\"\n [search]=\"search\"\n [isFilteringOnExternalId]=\"isFilteringOnExternalId\"\n [isAdvancedSearchActivated]=\"isAdvancedSearchActivated\"\n [nodesIdsMatchingAdvancedSearch]=\"\n nodesIdsMatchingAdvancedSearch\n \"\n classToApply=\"highlight\"\n nodesMatchingSearchClass=\"node-matching-search\"\n class=\"data-name ellipsis\"\n id=\"{{ node.ids.id }}\"\n ></span>\n </button>\n </div>\n </ng-template>\n\n <!-- mat-tree-node is used for ending nodes (without children) -->\n <mat-tree-node\n *matTreeNodeDef=\"let node\"\n @fadeTree\n [@.disabled]=\"\n !(enableAnimation && addedChildrenIds.includes(node.ids.id))\n \"\n class=\"gnc-tree-node parent-node-without-children\"\n [style.paddingLeft.px]=\"\n node.levelForPadding * STANDARD_NODE_PADDING_LEFT\n \"\n id=\"{{\n !isSelectOnlyModeEnabled\n ? 'node-' + node.ids.id\n : 'selected-mode-node-' + node.ids.id\n }}\"\n >\n <div\n fxFlex\n fxLayout=\"row\"\n fxLayoutAlign=\"start center\"\n tabindex=\"-1\"\n [routerLink]=\"!isSelectOnlyModeEnabled ? [getLink(node)] : []\"\n [ngClass]=\"{\n 'active-node-link': isActiveNodeLink(\n node.ids.id,\n node.type,\n node.childrenType\n )\n }\"\n (click)=\"onClickOnTreeNode(node)\"\n class=\"gnc-tree-node-content\"\n >\n <button mat-icon-button disabled></button>\n <mat-checkbox\n *ngIf=\"\n isSelectOnlyModeEnabled &&\n selectMode === allTreeSelectMode.MULTIPLE &&\n node.type === selectedResourceTypeId\n \"\n [checked]=\"checklistSelection.isSelected(node)\"\n (change)=\"onCheckMatBox(node)\"\n ></mat-checkbox>\n <ng-container\n [ngTemplateOutlet]=\"nodeInfo\"\n [ngTemplateOutletContext]=\"{ node: node }\"\n ></ng-container>\n </div>\n <div\n *ngIf=\"!isSelectOnlyModeEnabled\"\n fxLayout=\"row\"\n class=\"ellipsis-menu\"\n >\n <ng-container\n [ngTemplateOutlet]=\"actionsMenuTemplate\"\n [ngTemplateOutletContext]=\"{\n node,\n selectedElementsUiIds: [\n {\n id: node.ids.id,\n externalId: node.ids.externalId,\n label: node.name\n }\n ]\n }\"\n ></ng-container>\n </div>\n </mat-tree-node>\n\n <!-- mat-nested-tree-node is used for branching nodes (with childrens) -->\n <mat-tree-node\n *matTreeNodeDef=\"let node; when: hasChild\"\n @fadeTree\n [@.disabled]=\"\n !(enableAnimation && addedChildrenIds.includes(node.ids.id))\n \"\n [ngClass]=\"\n !treeControl.isExpandable(node)\n ? 'parent-node-without-children'\n : ''\n \"\n class=\"gnc-tree-node\"\n [style.paddingLeft.px]=\"\n node.levelForPadding * STANDARD_NODE_PADDING_LEFT\n \"\n id=\"{{\n !isSelectOnlyModeEnabled\n ? 'node-' + node.ids.id\n : 'selected-mode-node-' + node.ids.id\n }}\"\n >\n <div fxLayout=\"row\">\n <div\n fxFlex\n fxLayout=\"row\"\n fxLayoutAlign=\"start center\"\n tabindex=\"-1\"\n [routerLink]=\"!isSelectOnlyModeEnabled ? [getLink(node)] : []\"\n [ngClass]=\"{\n 'active-node-link': isActiveNodeLink(\n node.ids.id,\n node.type,\n node.childrenType\n )\n }\"\n (click)=\"onClickOnTreeNode(node)\"\n class=\"gnc-tree-node-content\"\n >\n <button\n *ngIf=\"treeControl.isExpandable(node)\"\n mat-icon-button\n (click)=\"$event.stopPropagation(); onToggleFold(node)\"\n class=\"btn-toggle\"\n >\n <mat-icon aria-hidden=\"true\" class=\"mat-icon-rtl-mirror\">\n {{\n treeControl.isExpanded(node)\n ? 'arrow_drop_down'\n : 'arrow_right'\n }}\n </mat-icon>\n </button>\n <mat-checkbox\n *ngIf=\"\n isSelectOnlyModeEnabled &&\n selectMode === allTreeSelectMode.MULTIPLE &&\n node.type === selectedResourceTypeId\n \"\n [checked]=\"checklistSelection.isSelected(node)\"\n (click)=\"onClickOnTreeNode(node)\"\n ></mat-checkbox>\n <ng-container\n [ngTemplateOutlet]=\"nodeInfo\"\n [ngTemplateOutletContext]=\"{ node: node }\"\n ></ng-container>\n </div>\n <div\n *ngIf=\"!isSelectOnlyModeEnabled\"\n fxLayout=\"row\"\n class=\"ellipsis-menu\"\n >\n <ng-container\n [ngTemplateOutlet]=\"actionsMenuTemplate\"\n [ngTemplateOutletContext]=\"{\n node,\n selectedElementsUiIds: getNodesForDeleteActions(node)\n }\"\n ></ng-container>\n </div>\n </div>\n <div *ngIf=\"treeControl.isExpanded(node)\" class=\"data-info\">\n <ng-container matTreeNodeOutlet></ng-container>\n </div>\n </mat-tree-node>\n </mat-tree>\n </cdk-virtual-scroll-viewport>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";.gnc-tree{padding:0 10px 16px;z-index:1;position:relative}.gnc-tree-node{min-height:40px;height:40px;outline:none;border:none;display:block}.gnc-tree-node div{height:100%}.gnc-tree-node-content:before{content:\"\";min-height:40px;height:40px;position:absolute;left:0;right:0;z-index:-1;transition:all .2s ease}.gnc-tree-node-content:hover:before{background-color:#0000000a}.disabled-div{pointer-events:none;opacity:.4}.gnc-tree-node-content{min-width:0}.data-info:not(:first-of-type){padding-inline-start:20px}.data-name{text-align:left}.data-name:after{display:block;content:attr(title);font-weight:700;height:0;overflow:hidden;visibility:hidden}.btn-toggle{height:100%;background-color:transparent}.btn-toggle span{line-height:24px;height:100%}.data-info{overflow:hidden}.btn-node{background:transparent;outline:none;border:none}div.active-node-link:before{content:\"\";box-shadow:inset 4px 0;background-color:#0000000a;min-height:40px;height:40px;position:absolute;z-index:-1;left:0;right:0}mat-tree-node.parent-node-without-children{padding-inline-start:40px}.tree-node-hidden{display:none!important}.div-mat-tree-lg-screen{width:fit-content;min-width:290px;max-width:474px}.div-mat-tree-sm-screen{width:290px;min-width:290px;max-width:290px}.div-generic-tree-component{height:100%;width:fit-content}.div-generic-tree-dialog{height:100%}.div-mat-tree-dialog{min-width:100%}.active-node-link div:not(.disabled-div) button,.active-node-link mat-icon,.gnc-tree-node-content:hover div:not(.disabled-div) button,.gnc-tree-node-content:hover mat-icon{cursor:pointer;transition:.5s}.active-node-link .btn-node:focus{background:transparent;outline:none;border:none}div.div-mat-tree-dialog cdk-virtual-scroll-viewport{max-width:100%}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i4.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { kind: "directive", type: i4.DefaultLayoutAlignDirective, selector: " [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]", inputs: ["fxLayoutAlign", "fxLayoutAlign.xs", "fxLayoutAlign.sm", "fxLayoutAlign.md", "fxLayoutAlign.lg", "fxLayoutAlign.xl", "fxLayoutAlign.lt-sm", "fxLayoutAlign.lt-md", "fxLayoutAlign.lt-lg", "fxLayoutAlign.lt-xl", "fxLayoutAlign.gt-xs", "fxLayoutAlign.gt-sm", "fxLayoutAlign.gt-md", "fxLayoutAlign.gt-lg"] }, { kind: "directive", type: i4.FlexFillDirective, selector: "[fxFill], [fxFlexFill]" }, { kind: "directive", type: i4.DefaultFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { kind: "directive", type: i5.DefaultClassDirective, selector: " [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]", inputs: ["ngClass", "ngClass.xs", "ngClass.sm", "ngClass.md", "ngClass.lg", "ngClass.xl", "ngClass.lt-sm", "ngClass.lt-md", "ngClass.lt-lg", "ngClass.lt-xl", "ngClass.gt-xs", "ngClass.gt-sm", "ngClass.gt-md", "ngClass.gt-lg"] }, { kind: "directive", type: i6.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i6.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i6.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: i7.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i8.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i9.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i10.MatTreeNodeDef, selector: "[matTreeNodeDef]", inputs: ["matTreeNodeDefWhen", "matTreeNode"] }, { kind: "component", type: i10.MatTree, selector: "mat-tree", exportAs: ["matTree"] }, { kind: "directive", type: i10.MatTreeNode, selector: "mat-tree-node", inputs: ["disabled", "tabIndex"], exportAs: ["matTreeNode"] }, { kind: "directive", type: i10.MatTreeNodeOutlet, selector: "[matTreeNodeOutlet]" }, { kind: "directive", type: i11.ColorSearchedLettersDirective, selector: "[appColorSearchedLetters]", inputs: ["search", "name", "isFilteringOnExternalId", "nodeIds", "classToApply", "nodesMatchingSearchClass", "isAdvancedSearchActivated", "nodesIdsMatchingAdvancedSearch"] }], animations: [trigger('fadeTree', fadeTree(':enter'))] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.4", ngImport: i0, type: GenericTreeComponent, decorators: [{ type: Component, args: [{ selector: 'dm-generic-tree', animations: [trigger('fadeTree', fadeTree(':enter'))], template: "<!-- Copyright (C) 2020-2024 Linagora\n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU Affero General Public License as published by the Free\nSoftware Foundation, either version 3 of the License, or (at your option) any\nlater version, provided you comply with the Additional Terms applicable for\nLinID Directory Manager software by LINAGORA pursuant to Section 7 of the GNU\nAffero General Public License, subsections (b), (c), and (e), pursuant to\nwhich these Appropriate Legal Notices must notably (i) retain the display of\nthe \"LinID\u2122\" trademark/logo at the top of the interface window, the display\nof the \u201CYou are using the Open Source and free version of LinID\u2122, powered by\nLinagora \u00A9 2009\u20132013. Contribute to LinID R&D by subscribing to an Enterprise\noffer!\u201D infobox and in the e-mails sent with the Program, notice appended to\nany type of outbound messages (e.g. e-mail and meeting requests) as well as\nin the LinID Directory Manager user interface, (ii) retain all hypertext\nlinks between LinID Directory Manager and https://linid.org/, as well as\nbetween LINAGORA and LINAGORA.com, and (iii) refrain from infringing LINAGORA\nintellectual property rights over its trademarks and commercial brands. Other\nAdditional Terms apply, see <http://www.linagora.com/licenses/> for more\ndetails.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\ndetails.\n\nYou should have received a copy of the GNU Affero General Public License and\nits applicable Additional Terms for LinID Directory Manager along with this\nprogram. If not, see <http://www.gnu.org/licenses/> for the GNU Affero\nGeneral Public License version 3 and <http://www.linagora.com/licenses/> for\nthe Additional Terms applicable to the LinID Directory Manager software. -->\n\n<div\n #divGenericTree\n fxLayout=\"column\"\n [ngClass]=\"\n isSelectOnlyModeEnabled\n ? 'div-generic-tree-dialog'\n : 'div-generic-tree-component'\n \"\n>\n <div\n [ngClass]=\"\n isSelectOnlyModeEnabled\n ? 'div-mat-tree-dialog'\n : isLargeScreen\n ? 'div-mat-tree-lg-screen'\n : 'div-mat-tree-sm-screen'\n \"\n #divMatTree\n >\n <cdk-virtual-scroll-viewport\n [style.height.px]=\"viewportHeight\"\n [style.width]=\"viewportMinWidth\"\n [itemSize]=\"STANDARD_NODE_HEIGHT\"\n [minBufferPx]=\"viewportHeight\"\n >\n <ng-container\n *cdkVirtualFor=\"let item of flattenDataTreeIds\"\n ></ng-container>\n <mat-tree\n #tree\n [dataSource]=\"dataSource\"\n [treeControl]=\"treeControl\"\n class=\"gnc-tree\"\n >\n <!-- This is the template for the common part of any node -->\n <ng-template #no