@WidgetComponent

nm-worklist-select

File

src/app/shared/widgets/worklist-select/worklist-select.component.ts

Implements

OnDestroy

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector nm-worklist-select
styleUrls worklist-select.component.scss
templateUrl ./worklist-select.component.html

Index

Widget inputs
Widget outputs
Properties
Methods

Constructor

constructor(dialogService: DialogService, halService: HalService, notificationService: CustomNotificationService, appstore: AppdataStore, widgetFrameService: WidgetframeService, cd: ChangeDetectorRef, appContext: AppContext, translateService: TranslateService, currentLocaleService: CurrentLocaleService)
Parameters :
Name Type Optional
dialogService DialogService no
halService HalService no
notificationService CustomNotificationService no
appstore AppdataStore no
widgetFrameService WidgetframeService no
cd ChangeDetectorRef no
appContext AppContext no
translateService TranslateService no
currentLocaleService CurrentLocaleService no

Methods

addActions
addActions()
Returns : void
createAction
createAction(name: , icon: , label: , labelFromAction: , isDisableable: )
Parameters :
Name Optional
name no
icon no
label no
labelFromAction no
isDisableable no
Returns : { name: any; icon: any; label: any; labelFromAction: any; isDisableable: any; }
Private executeAction
executeAction(actionName: string, action: Action, node: any, reload: boolean)
Parameters :
Name Type Optional Default value
actionName string no
action Action no
node any no
reload boolean no true
Returns : Observable<ActionEvent>
Public executeActionAndShowMessage
executeActionAndShowMessage(name: string, action: Action, node: any, reload: boolean)
Parameters :
Name Type Optional Default value
name string no
action Action no
node any no
reload boolean no true
Returns : any
filteredActions
filteredActions(data: )
Parameters :
Name Optional
data no
Returns : any[]
Private getAvailableActions
getAvailableActions()
Returns : any[]
Private getDefaultRefreshActions
getDefaultRefreshActions()
Returns : any[]
Public getWorkListRowContent
getWorkListRowContent(data: any)
Parameters :
Name Type Optional
data any no
Returns : string
Public isFolder
isFolder(index: , row: any)
Parameters :
Name Type Optional
index no
row any no
Returns : boolean
Public isNode
isNode(index: , row: any)
Parameters :
Name Type Optional
index no
row any no
Returns : boolean
Public isStatic
isStatic(index: , row: any)
Parameters :
Name Type Optional
index no
row any no
Returns : boolean
Public isStaticChild
isStaticChild(index: , row: any)
Parameters :
Name Type Optional
index no
row any no
Returns : boolean
Private loadNodes
loadNodes()
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
Public onAddElement
onAddElement()
Returns : void
onClickAction
onClickAction(data: , actionName: )
Parameters :
Name Optional
data no
actionName no
Returns : void
Public onClickElementIcon
onClickElementIcon(event: , data: )
Parameters :
Name Optional
event no
data no
Returns : void
onDeleteClick
onDeleteClick(node: , action: )
Parameters :
Name Optional
node no
action no
Returns : void
Public onDrag
onDrag(event: OnDragEvent)
Parameters :
Name Type Optional
event OnDragEvent no
Returns : void
Public onDrop
onDrop(event: OnDropEvent)
Parameters :
Name Type Optional
event OnDropEvent no
Returns : void
Public onDropped
onDropped(node: , event: OnDroppedEvent)
Parameters :
Name Type Optional
node no
event OnDroppedEvent no
Returns : void
Public onEdit
onEdit(data: any)
Parameters :
Name Type Optional
data any no
Returns : void
Public onEditButtonClick
onEditButtonClick(event: )
Parameters :
Name Optional
event no
Returns : void
Public onEdited
onEdited(data: any)
Parameters :
Name Type Optional
data any no
Returns : void
Public onElementMove
onElementMove(event: )
Parameters :
Name Optional
event no
Returns : void
onSelect
onSelect(data: )
Parameters :
Name Optional
data no
Returns : void
Private reload
reload(node: any, actionResponse?: HttpResponse)
Parameters :
Name Type Optional
node any no
actionResponse HttpResponse<any> yes
Returns : void
widgetConfigure
widgetConfigure()
Decorators : WidgetConfigure
Returns : void

Properties

Public actions
actions: any[]
Type : any[]
Public addElementAction
addElementAction:
Default value : new Subject()
Decorators : WidgetOutput
Public addElementConfig
addElementConfig: any
Type : any

Add new element configuration

Public availableActions
availableActions: any[]
Type : any[]
Default value : []
Public changeElementStateAction
changeElementStateAction:
Default value : new Subject<any>()
Decorators : WidgetOutput
Public configuration
configuration: WidgetConfig<WorklistSelectConfiguration>
Type : WidgetConfig<WorklistSelectConfiguration>
Decorators : WidgetConfiguration
Public descriptionField
descriptionField: string
Type : string
Public disableAddNewElementButton
disableAddNewElementButton: boolean
Type : boolean
Default value : false
Public disableAddNewElementButtonChannel
disableAddNewElementButtonChannel:
Default value : new Subject<boolean>()
Decorators : WidgetInput
Public disableDeselect
disableDeselect: boolean
Type : boolean
Public disableElementAction
disableElementAction:
Default value : new Subject<{ node: string; action: string; value: boolean; }>()
Decorators : WidgetInput
Public dynamicHeight
dynamicHeight: boolean
Type : boolean
Default value : false
Public dynamicHeightAdditionalHeight
dynamicHeightAdditionalHeight: string
Type : string
Public dynamicTreeHeightInDialog
dynamicTreeHeightInDialog: boolean
Type : boolean
Default value : false
Public editClicked
editClicked:
Default value : new Subject<any>()
Decorators : WidgetOutput
Public elementMoved
elementMoved:
Default value : new Subject<any>()
Decorators : WidgetOutput
Public elementsDataType
elementsDataType: string
Type : string
Public elementSelected
elementSelected:
Default value : new ReplaySubject<any>(1)
Decorators : WidgetOutput
Public emitElementMoved
emitElementMoved: boolean
Type : boolean
Public emitOnFolderSelect
emitOnFolderSelect: boolean
Type : boolean
Public focusSearchInput
focusSearchInput: boolean
Type : boolean
Public folderIconsSelectors
folderIconsSelectors: Selectors
Type : Selectors
Public folderMenuSelectors
folderMenuSelectors: Selectors
Type : Selectors
Public footer
footer: any
Type : any

Footer configuration

Public forcedElementSelection
forcedElementSelection:
Public height
height: string
Type : string
Public identifierField
identifierField: string
Type : string
Public isFolderHierarchy
isFolderHierarchy: boolean
Type : boolean
Public listFolderType
listFolderType: string
Type : string
Public listSelected
listSelected:
Default value : new Subject<number>()
Decorators : WidgetOutput
Public loadElements
loadElements: boolean
Type : boolean
Public localStateRecovery
localStateRecovery: boolean
Type : boolean
Public localStorageSearch
localStorageSearch: string
Type : string
Public localStorageState
localStorageState: string
Type : string
Public multi
multi: boolean
Type : boolean

Element selector configuration

Public nodeIconsSelectors
nodeIconsSelectors: Selectors
Type : Selectors
Private nodeLoader
nodeLoader: NodeLoader
Type : NodeLoader
Public nodeMenuSelectors
nodeMenuSelectors: Selectors
Type : Selectors
Public nodes
nodes: any[]
Type : any[]
Private originalName
originalName: String
Type : String
Public refreshElementsActions
refreshElementsActions: any[]
Type : any[]
Public reloadElements
reloadElements:
Default value : new Subject<boolean>()
Decorators : WidgetInput
Public removeNodeSelection
removeNodeSelection:
Default value : new Subject<boolean>()
Decorators : WidgetInput
Public reset
reset:
Default value : new Subject()
Decorators : WidgetOutput
Public selectInitialized
selectInitialized:
Default value : true
Public selectItem
selectItem:
Default value : new Subject<any>()
Decorators : WidgetInput
Public showActions
showActions: boolean
Type : boolean
Public showElementIcon
showElementIcon: boolean
Type : boolean
Public showFolderActions
showFolderActions: boolean
Type : boolean
Public showStaticFoldersOnly
showStaticFoldersOnly: boolean
Type : boolean
Public staticFolders
staticFolders: any[]
Type : any[]
Private treeSelector
treeSelector: TreeSelectorComponent
Type : TreeSelectorComponent
Decorators : ViewChild
Private unsubscribe
unsubscribe:
Default value : NgUnsubscribe.create()
Public updateElements
updateElements:
Default value : new Subject<any>()
Decorators : WidgetInput
Public updateFolders
updateFolders:
Default value : new Subject<any>()
Decorators : WidgetInput
Public widgetId
widgetId: string
Type : string
Decorators : WidgetId
Public withHeader
withHeader: boolean
Type : boolean
import {
  WidgetComponent,
  WidgetConfiguration,
  WidgetConfigure,
  WidgetId,
  WidgetInput,
  WidgetOutput,
} from "../widget.metadata";
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  Input,
  OnDestroy,
  ViewChild,
} from "@angular/core";
import { getOrDefault, WidgetConfig } from "../widget.configuration";
import {
  concatAll,
  concatMap,
  filter,
  map,
  mergeAll,
  mergeMap,
  take,
  takeUntil,
  throttleTime,
} from "rxjs/operators";
import { AppdataStore } from "../../components/appdata/appdata.store";
import { WidgetframeService } from "../widgetframe/widgetframe.service";
import { CustomNotificationService } from "../../components/notification/customnotification.service";
import { HalService } from "../../components/hal/hal.service";

import { MatDialog } from "@angular/material/dialog";
import { MatMenuTrigger } from "@angular/material/menu";
import { CreateFolderEntryDialogComponent } from "./worklist-create-dialog/create-folder-entry-dialog.component";
import {
  Subject,
  Observable,
  ReplaySubject,
  from,
  of,
  combineLatest,
} from "rxjs";
import { NgUnsubscribe } from "../../ng-unsubscribe";
import { AppContext } from "../../components/app-context/app.context";
import { BaseConfiguration } from "../widgetframe/widgetframe.component";
import { Content, Selectors } from "../../components/app-context/api";

import * as uriTemplates_ from "uri-templates";
import { TranslateService } from "@ngx-translate/core";
import { TreeSelectorComponent } from "../../components/tree-selector/tree-selector.component";
import { Action } from "../../components/hal/action";
import { ActionEvent } from "../../components/hal/hal";
import { CurrentLocaleService } from "../../components/i18n/currentLocale.service";
import { NodeLoader } from "./node-loader";
import {
  OnDragEvent,
  OnDropEvent,
  OnDroppedEvent,
} from "../../components/tree-selector/tree-selector.api";
import { HttpResponse } from "@angular/common/http";
import { DialogService } from "../../components/dialog";

const uriTemplates = uriTemplates_;

export interface WorklistSelectConfiguration extends BaseConfiguration {
  allowDrag: boolean;
  multi: boolean;
  emitElementMoved: boolean;
  localStorageState: string;
  localStorageSearch: string;
  listFolderType: string;
  elementsDataType: string;
  staticFolders: any[];
  refreshElementsActions: any[];
  addElementConfig: any;
  showFolderActions: boolean;
  showElementIcon: boolean;
  emitOnFolderSelect: boolean;
  disableDeselect: boolean;
  footer: any;
  loadElements: boolean;
  loadElementsUrl: string;
  showActions: boolean;
  isFolderHierarchy: boolean;
  loadFoldersUrl: string;
  identifierField: string;
  descriptionField: string;
  localStateRecovery: boolean;
  showStaticFoldersOnly: boolean;
  height?: string;
  dynamicHeight?: boolean;
  dynamicTreeHeightInDialog?: boolean;
  dynamicHeightAdditionalHeight?: string;
  selectors: {
    folderMenu?: Selectors;
    folderIcons?: Selectors;
    nodeMenu?: Selectors;
    nodeIcons?: Selectors;

    // fallback, used, when the more specific selectors are not defined
    menu: Selectors;
    icons: Selectors;
  };
  cellClass?: string;
  cellClassModifier?: string;
  infoWidth: string;
  infoHeight: string;
  focusSearchInput?: boolean;
}

const DEFAULT_ADD_ELEMENT_CONFIG = {
  showAddElementIcon: true,
  labelTooltip: "infotext.create.worklist.or.folder",
  availableOptions: [
    {
      actionsHref: "/api/core/worklists",
      description: "worklist",
      listFolderType: "WORKLIST",
    },
    {
      actionsHref: "/api/core/listfolders/WORKLIST",
      description: "folder",
      listFolderType: "WORKLIST",
    },
  ],
};

@WidgetComponent("nm-worklist-select")
@Component({
  selector: "nm-worklist-select",
  templateUrl: "./worklist-select.component.html",
  styleUrls: ["./worklist-select.component.scss"],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class WorklistSelectComponentWidget implements OnDestroy {
  private unsubscribe = NgUnsubscribe.create();

  @ViewChild(TreeSelectorComponent, { static: true })
  private treeSelector: TreeSelectorComponent;

  @WidgetInput()
  public disableElementAction = new Subject<{
    node: string;
    action: string;
    value: boolean;
  }>();

  @WidgetId()
  public widgetId: string;

  @WidgetInput()
  public removeNodeSelection = new Subject<boolean>();

  @WidgetInput()
  public updateElements = new Subject<any>();

  @WidgetInput()
  public updateFolders = new Subject<any>();

  @WidgetInput()
  public reloadElements = new Subject<boolean>();

  @WidgetInput("disableAddNewElementButton")
  public disableAddNewElementButtonChannel = new Subject<boolean>();

  @WidgetOutput()
  public listSelected = new Subject<number>();

  @WidgetOutput("elementSelected")
  public elementSelected = new ReplaySubject<any>(1);

  @WidgetOutput()
  public reset = new Subject();

  @WidgetOutput()
  public addElementAction = new Subject();

  @WidgetOutput()
  public changeElementStateAction = new Subject<any>();

  @WidgetInput()
  public selectItem = new Subject<any>();

  @WidgetOutput()
  public elementMoved = new Subject<any>();

  @WidgetOutput()
  public editClicked = new Subject<any>();

  /** Add new element configuration */
  public addElementConfig: any;

  /** Element selector configuration */
  public multi: boolean;
  public emitElementMoved: boolean;
  public selectInitialized = true;
  public localStorageState: string;
  public localStorageSearch: string;
  public listFolderType: string;
  public elementsDataType: string;
  public height: string;
  public staticFolders: any[];
  public refreshElementsActions: any[];
  public availableActions: any[] = [];
  public showFolderActions: boolean;
  public showElementIcon: boolean;
  public forcedElementSelection;
  public emitOnFolderSelect: boolean;
  public disableDeselect: boolean;
  public loadElements: boolean;
  public isFolderHierarchy: boolean;
  public identifierField: string;
  public descriptionField: string;
  public localStateRecovery: boolean;
  public showStaticFoldersOnly: boolean;
  public dynamicHeight: boolean = false;
  public dynamicTreeHeightInDialog: boolean = false;
  public dynamicHeightAdditionalHeight: string;
  public focusSearchInput: boolean;

  public folderMenuSelectors: Selectors;
  public folderIconsSelectors: Selectors;
  public nodeMenuSelectors: Selectors;
  public nodeIconsSelectors: Selectors;

  /** Footer configuration */
  public footer: any;
  public withHeader: boolean;
  public showActions: boolean;

  public actions: any[];
  public nodes: any[];
  public disableAddNewElementButton: boolean = false;

  private nodeLoader: NodeLoader;

  private originalName: String;

  constructor(
    private dialogService: DialogService,
    private halService: HalService,
    private notificationService: CustomNotificationService,
    private appstore: AppdataStore,
    private widgetFrameService: WidgetframeService,
    private cd: ChangeDetectorRef,
    private appContext: AppContext,
    private translateService: TranslateService,
    private currentLocaleService: CurrentLocaleService
  ) {
    this.nodeLoader = new NodeLoader(
      translateService,
      appstore,
      widgetFrameService
    );
    this.addActions();
  }

  createAction(name, icon, label, labelFromAction, isDisableable) {
    return {
      name: name,
      icon: icon,
      label: label,
      labelFromAction: labelFromAction,
      isDisableable: isDisableable,
    };
  }

  addActions() {
    this.actions = [];
    this.actions.push(
      this.createAction("refresh", "refresh", "label.refresh", false, false)
    );
    this.actions.push(
      this.createAction(
        "duplicate",
        "content-duplicate",
        "label.duplicate",
        false,
        false
      )
    );
    this.actions.push(
      this.createAction(
        "checkLogging",
        "read",
        "label.checkLogging",
        false,
        false
      )
    );
    this.actions.push(
      this.createAction("import", "file-import", null, true, false)
    );
    this.actions.push(
      this.createAction("export", "file-export", null, true, false)
    );
    this.actions.push(
      this.createAction("translate", "translate", null, true, false)
    );
    this.actions.push(
      this.createAction(
        "add-to-dashboard",
        "view-dashboard-outline",
        null,
        true,
        false
      )
    );
    this.actions.push(
      this.createAction(
        "remove-from-dashboard",
        "view-dashboard",
        null,
        true,
        false
      )
    );
  }

  @WidgetConfigure()
  widgetConfigure() {
    this.multi = getOrDefault(this.configuration.configuration.multi, false);
    this.emitElementMoved = getOrDefault(
      this.configuration.configuration.emitElementMoved,
      false
    );
    this.localStorageState = getOrDefault(
      this.configuration.configuration.localStorageState,
      "worklist-state-storage"
    );
    this.localStorageSearch = getOrDefault(
      this.configuration.configuration.localStorageSearch,
      "worklist-search-string"
    );
    this.listFolderType = getOrDefault(
      this.configuration.configuration.listFolderType,
      "WORKLIST"
    );
    this.elementsDataType = getOrDefault(
      this.configuration.configuration.elementsDataType,
      "worklists"
    );
    this.showFolderActions = getOrDefault(
      this.configuration.configuration.showFolderActions,
      true
    );
    this.showElementIcon = getOrDefault(
      this.configuration.configuration.showElementIcon,
      true
    );
    this.emitOnFolderSelect = getOrDefault(
      this.configuration.configuration.emitOnFolderSelect,
      true
    );
    this.disableDeselect = getOrDefault(
      this.configuration.configuration.disableDeselect,
      false
    );
    this.staticFolders = getOrDefault(
      this.configuration.configuration.staticFolders,
      NodeLoader.defaultStaticFolders()
    );
    this.refreshElementsActions = getOrDefault(
      this.configuration.configuration.refreshElementsActions,
      this.getDefaultRefreshActions()
    );
    this.addElementConfig = Object.assign(
      {},
      DEFAULT_ADD_ELEMENT_CONFIG,
      this.configuration.configuration.addElementConfig || {}
    );
    this.footer = getOrDefault(this.configuration.configuration.footer, null);
    this.showStaticFoldersOnly = getOrDefault(
      this.configuration.configuration.showStaticFoldersOnly,
      false
    );

    this.selectItem
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((data) => (this.forcedElementSelection = data));

    this.withHeader = getOrDefault(
      this.configuration.configuration.withHeader,
      true
    );
    this.showActions = getOrDefault(
      this.configuration.configuration.showActions,
      true
    );
    this.loadElements = getOrDefault(
      this.configuration.configuration.loadElements,
      false
    );
    this.isFolderHierarchy = getOrDefault(
      this.configuration.configuration.isFolderHierarchy,
      false
    );
    this.identifierField = getOrDefault(
      this.configuration.configuration.identifierField,
      "identifier"
    );
    this.descriptionField = getOrDefault(
      this.configuration.configuration.descriptionField,
      "description"
    );
    this.localStateRecovery = getOrDefault(
      this.configuration.configuration.localStateRecovery,
      true
    );

    this.dynamicHeight = getOrDefault(
      this.configuration.configuration.dynamicHeight,
      false
    );

    this.dynamicTreeHeightInDialog = getOrDefault(
      this.configuration.configuration.dynamicTreeHeightInDialog,
      false
    );

    this.dynamicHeightAdditionalHeight = getOrDefault(
      this.configuration.configuration.dynamicHeightAdditionalHeight,
      ""
    );

    this.height = getOrDefault(
      this.configuration.configuration.height,
      "650px"
    );

    const selectors: any = this.configuration.configuration.selectors || {};
    this.folderMenuSelectors = getOrDefault(
      selectors.folderMenu || selectors.menu,
      {
        target: ["nm-worklist-select", "nm-worklist-select-folder"],
        type: "menu",
      }
    );

    this.folderIconsSelectors = getOrDefault(
      selectors.folderIcons || selectors.icons,
      {
        target: ["nm-worklist-select", "nm-worklist-select-folder"],
        type: "icons",
      }
    );

    this.nodeMenuSelectors = getOrDefault(
      selectors.nodeMenu || selectors.menu,
      {
        target: ["nm-worklist-select", "nm-worklist-select-node"],
        type: "menu",
      }
    );

    this.nodeIconsSelectors = getOrDefault(
      selectors.nodeIcons || selectors.icons,
      {
        target: ["nm-worklist-select", "nm-worklist-select-node"],
        type: "icons",
      }
    );

    this.focusSearchInput = getOrDefault(
      this.configuration.configuration.focusSearchInput,
      false
    );

    this.removeNodeSelection
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((event) => this.treeSelector.clearSelection());

    this.updateElements.pipe(takeUntil(this.unsubscribe)).subscribe((node) => {
      const mapped = this.nodeLoader.mapEntry(
        node.value,
        this.identifierField,
        this.descriptionField
      );
      this.treeSelector.updateNode(mapped.id, mapped);
    });

    this.updateFolders.pipe(takeUntil(this.unsubscribe)).subscribe((node) => {
      const mapped = this.nodeLoader.mapFolder(node.value);
      this.treeSelector.updateNode(mapped.id, mapped);
    });

    let sources = [
      //
      of(),
      this.currentLocaleService.getCurrentLocale(),
      this.reloadElements,
      this.halService
        .getActionEvents()
        .pipe(filter((event) => event.name === "duplicate")),
    ];

    // trigger re-load, if the configured selectors
    // are triggered via the userContext
    // (used to reload after adding a folder or worklist)
    if (
      this.refreshElementsActions &&
      this.appContext &&
      this.appContext.userContext
    ) {
      this.refreshElementsActions.forEach((selector) => {
        sources.push(this.appContext.userContext.subscribe(selector));
      });
    }

    from(sources)
      .pipe(mergeAll(), takeUntil(this.unsubscribe), throttleTime(500))
      .subscribe(() => this.loadNodes());

    this.disableAddNewElementButtonChannel
      .asObservable()
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((disabled) => {
        this.disableAddNewElementButton = disabled;
        this.cd.markForCheck();
      });
  }

  private loadNodes() {
    if (this.showStaticFoldersOnly) {
      combineLatest([
        this.nodeLoader.staticFolders(this.staticFolders),
        this.nodeLoader.elements(
          this.configuration.configuration.loadElementsUrl,
          this.elementsDataType,
          this.identifierField,
          this.descriptionField
        ),
      ])
        .pipe(
          map(([folders, elements]) => [...folders.nodes, ...elements.nodes])
        )
        .subscribe((nodes) => {
          this.nodes = nodes;
          this.cd.markForCheck();
        });
      return;
    }

    this.nodeLoader
      .nodes(
        this.staticFolders,
        this.isFolderHierarchy
          ? this.configuration.configuration.loadFoldersUrl
          : null,
        this.listFolderType,
        this.configuration.configuration.loadElementsUrl,
        this.elementsDataType,
        this.identifierField,
        this.descriptionField
      )
      .subscribe((nodes) => {
        this.nodes = nodes;
        this.cd.markForCheck();
      });
  }

  public isNode(index, row: any): boolean {
    return row.isNode === true;
  }

  public isStatic(index, row: any): boolean {
    return row.isStatic === true;
  }

  public isStaticChild(index, row: any): boolean {
    return row.isNode === true && row.parent === "dynamic-worklists";
  }

  public isFolder(index, row: any): boolean {
    return row.isFolder === true;
  }

  public onEdit(data: any) {
    // save the original name to prevent executing actions, if nothing changed
    this.originalName = data.name;

    // use the inline edit mode unless explicitly set in the node
    if (data.editMode && data.editMode !== "inline") {
      this.editClicked.next(data.id);
    } else {
      this.treeSelector.edit(data.id);
    }
  }

  public onEdited(data: any) {
    let trimmedName = data.name.trim();

    if (!trimmedName) {
      let errorTitle = this.translateService.instant(
        "worklist-select.edit.error.title"
      );
      let errorMsg = this.translateService.instant(
        "worklist-select.edit.error.message"
      );
      this.notificationService.error(errorTitle, errorMsg);
      data.name = this.originalName;
      this.reload(data);
      return;
    }

    if (this.originalName === trimmedName) {
      return;
    }

    if (data.isFolder) {
      const action = data.actions.update;
      action.payload = {
        name: trimmedName,
      };
      this.halService.executeAndShowMessage("rename", action).subscribe();
      return;
    }

    const action = data.actions.update;
    action.payload.description = trimmedName;
    this.halService.executeAndShowMessage("rename", action).subscribe();
  }

  public onDrag(event: OnDragEvent) {
    event.cancel =
      this.isStatic(null, event.dragged) ||
      this.isStaticChild(null, event.dragged);
  }

  public onDrop(event: OnDropEvent) {
    if (
      this.isStatic(null, event.target) ||
      this.isStaticChild(null, event.target)
    ) {
      event.cancel = true;
    } else if (event.target.isNode) {
      event.dropAsChild = false;
    }
  }

  public onDropped(node, event: OnDroppedEvent) {
    if (this.emitElementMoved) {
      this.elementMoved.next(event);
      return;
    }
    const action = event.node.actions.update;
    if (event.node.isFolder) {
      const parent = event.to.parent?.folder ? event.to.parent.id : "-1";
      action.payload = {
        parent: parent,
      };
      this.halService.executeAndShowMessage("move", action).subscribe();
    } else {
      if (event.to.parent?.folder) {
        action.payload.listFolder = event.to.parent.id;
        event.node.root = false;
      } else {
        action.payload.listFolder = null;
        event.node.root = true;
      }
      this.halService.executeAndShowMessage("move", action).subscribe();
    }
  }

  onDeleteClick(node, action) {
    if (action.confirmed) {
      // trigger delete action, _without_ reloading...
      this.executeAction("delete", action, node, false).subscribe((event) => {
        this.notificationService.fromResponse(event.response);
        this.treeSelector.removeNode(node.id);
      });
    } else {
      action.confirmed = true;
      setTimeout(() => {
        action.confirmed = false;
        this.cd.markForCheck();
      }, 3000);
    }
  }

  public executeActionAndShowMessage(
    name: string,
    action: Action,
    node: any,
    reload: boolean = true
  ) {
    const observable = this.halService.executeAndShowMessage(name, action);

    if (reload) {
      observable.subscribe((event) => this.reload(node, event.response));
    }

    return observable;
  }

  private executeAction(
    actionName: string,
    action: Action,
    node: any,
    reload: boolean = true
  ): Observable<ActionEvent> {
    const observable = this.halService.execute(actionName, action);

    if (reload) {
      observable.subscribe((event) => this.reload(node, event.response));
    }

    return observable;
  }

  private reload(node: any, actionResponse?: HttpResponse<any>) {
    if (!node) {
      return;
    }

    // if the calling action already contained an updated node object
    // no need to reload
    const updatedNode = actionResponse?.body?.data;
    if (updatedNode && updatedNode[this.identifierField] === node.pimRef) {
      const mapped = this.nodeLoader.mapEntry(
        updatedNode,
        this.identifierField,
        this.descriptionField
      );
      this.treeSelector.updateNode(node.id, mapped);
      return;
    }

    if (node.actions.reload) {
      this.halService
        .execute("reload", node.actions.reload)
        .subscribe((event) => {
          if (event.response.body) {
            const mapped = this.nodeLoader.mapEntry(
              event.response.body,
              this.identifierField,
              this.descriptionField
            );
            this.treeSelector.updateNode(node.id, mapped);
          }
        });
    }
  }

  onClickAction(data, actionName) {
    switch (actionName) {
      case "add-to-dashboard":
      case "remove-from-dashboard":
      case "refresh":
      case "duplicate":
        this.executeActionAndShowMessage(
          actionName,
          data.actions[actionName],
          data
        );
        break;
      default:
        this.executeAction(actionName, data.actions[actionName], data);
        break;
    }
  }

  filteredActions(data): any[] {
    return this.actions.filter((action) => data.actions?.[action.name]);
  }

  @WidgetConfiguration()
  public configuration: WidgetConfig<WorklistSelectConfiguration>;

  ngOnDestroy(): void {
    this.unsubscribe.destroy();
  }

  onSelect(data) {
    if (data && Array.isArray(data)) {
      data = data.length > 0 ? data[0] : null;
    }

    if (data) {
      this.elementSelected.next(data);

      if (data.isNode || this.emitOnFolderSelect) {
        this.listSelected.next(data);
      }
    } else {
      this.elementSelected.next(null);
      this.reset.next();
    }
  }

  public onAddElement() {
    this.addElementAction.next();

    if (
      this.addElementConfig &&
      this.addElementConfig.availableOptions &&
      this.addElementConfig.availableOptions.length > 0
    ) {
      this.availableActions = this.getAvailableActions();

      const dialogRef = this.dialogService.open(
        CreateFolderEntryDialogComponent,
        {
          autoFocus: true,
          width: "706px",
          height: "889px",
        }
      );

      dialogRef.componentInstance.title = "infotext.create.element";
      dialogRef.componentInstance.availableActions = this.availableActions;
      dialogRef.componentInstance.listFolderType = this.listFolderType;
    }
  }

  private getAvailableActions(): any[] {
    var availableActions = [];

    if (this.addElementConfig && this.addElementConfig.availableOptions) {
      from(this.addElementConfig.availableOptions)
        .pipe(
          concatMap((availableOption) => {
            let option: any = availableOption as any;
            return this.widgetFrameService.getData(option.actionsHref).pipe(
              map((response) => {
                return {
                  description: option.description,
                  actions: response._actions,
                  listFolderType: option.listFolderType,
                };
              })
            );
          })
        )
        .subscribe((option) => availableActions.push(option));
    }

    return availableActions;
  }

  public onClickElementIcon(event, data) {
    event.stopPropagation();
    event.preventDefault();
    this.changeElementStateAction.next(data);
  }

  public onElementMove(event) {
    this.elementMoved.next(event);
  }

  public onEditButtonClick(event) {
    this.editClicked.next(event);
  }

  private getDefaultRefreshActions(): any[] {
    return [
      {
        target: "worklists",
      },
      {
        target: "listfolders",
        action: "create",
      },
      {
        target: "listfolder",
        action: "delete",
      },
    ];
  }

  public getWorkListRowContent(data: any): string {
    return data.appendEntriesCount && data.entries !== null
      ? "[" + data.entries + "] " + data.name
      : data.name;
  }
}
<nm-widgetframe
  [header]="'primary'"
  [configuration]="configuration"
  [infoTitle]="configuration.configuration.infoTitle"
  [infoText]="configuration.configuration.infoText"
  [infoWidth]="configuration.configuration.infoWidth"
  [infoHeight]="configuration.configuration.infoHeight"
  [infoPlacement]="'bottom'"
  [wikiLink]="configuration.configuration.wikiLink"
  [toolbarInvisible]="!withHeader"
  [withBorder]="configuration.configuration.withBorder"
  [widgetId]="widgetId"
>
  <ng-container slot="title" class="nm-widgetframe__title">
    {{ configuration.configuration.title | translate }}
  </ng-container>

  <ng-container slot="buttons" class="nm-widgetframe__buttons"> </ng-container>

  <ng-container slot="content" class="nm-widgetframe__content">
    <button
      mat-mini-fab
      color="primary"
      class="nm-add-element-button"
      [pTooltip]="addElementConfig.labelTooltip | translate"
      [disabled]="disableAddNewElementButton"
      [showDelay]="200"
      (click)="onAddElement()"
      *ngIf="selectInitialized && addElementConfig.showAddElementIcon"
    >
      <mat-icon>add</mat-icon>
    </button>

    <nm-tree-selector
      [nodes]="nodes"
      field="name"
      primary-key="pimRef"
      parent-key="parent"
      [drag-and-drop]="configuration.configuration.allowDrag"
      drop-as-root="{{ true }}"
      [initial-selection]="forcedElementSelection"
      [selection]="selectItem | async"
      [local-storage-grid]="localStorageState"
      [local-storage-filter]="localStorageSearch"
      [recover-from-local-storage]="localStateRecovery"
      (onSelection)="onSelect($event)"
      (onEdit)="onEdited($event)"
      [height]="dynamicHeight ? '100%' : height"
      [dynamicTreeHeightInDialog]="dynamicTreeHeightInDialog"
      [dynamicHeight]="dynamicHeight"
      [dynamicHeightAdditionalHeight]="dynamicHeightAdditionalHeight"
      [cellClass]="configuration.configuration.cellClass"
      [cellClassModifier]="configuration.configuration.cellClassModifier"
      [autofocus]="configuration.configuration.focusSearchInput"
      [isOverlayOpened]="false"
    >
      <!-- STATIC FOLDERS -->
      <nm-tree-node
        *nmTreeNodeDef="let data; let api = api; when: isStatic"
        (onDrag)="onDrag($event)"
        (onDrop)="onDrop($event)"
      >
        <mat-icon
          *ngIf="api.isExpanded"
          [svgIcon]="'folder-open-outline'"
          class="nm-folder-icon"
        ></mat-icon>
        <mat-icon
          *ngIf="!api.isExpanded"
          [svgIcon]="data.icon"
          class="nm-folder-icon"
        >
        </mat-icon>
        {{ data.name }}
      </nm-tree-node>

      <!-- FOLDERS -->
      <nm-tree-node
        *nmTreeNodeDef="let data; let api = api; when: isFolder"
        (onDrop)="onDrop($event)"
        (onDropped)="onDropped(data, $event)"
      >
        <mat-icon
          *ngIf="api.isExpanded"
          [svgIcon]="'folder-open-outline'"
          class="nm-folder-icon"
        ></mat-icon>
        <mat-icon
          *ngIf="!api.isExpanded"
          [svgIcon]="'folder-outline'"
          class="nm-folder-icon"
        >
        </mat-icon>
        {{ data.name }}

        <nm-tree-node-actions
          *ngIf="showActions"
          [data]="data"
          [menu-selectors]="folderMenuSelectors"
          [icon-selectors]="folderIconsSelectors"
        >
          <button
            mat-icon-button
            nm-tree-node-icon-action
            *ngIf="
              showFolderActions &&
              data.actions &&
              data.actions.update &&
              !data.hideUpdateAction
            "
            (click)="onEdit(data)"
          >
            <mat-icon color="primary" [svgIcon]="'form-textbox'"></mat-icon>
          </button>

          <button
            mat-icon-button
            nm-tree-node-icon-action
            *ngIf="
              showFolderActions &&
              data.actions &&
              data.actions.delete &&
              !api.hasChildren
            "
            (click)="onDeleteClick(data, data.actions.delete)"
          >
            <mat-icon color="warn" *ngIf="data.actions.delete.confirmed"
              >warning</mat-icon
            >
            <mat-icon color="primary" *ngIf="!data.actions.delete.confirmed"
              >delete</mat-icon
            >
          </button>
        </nm-tree-node-actions>
      </nm-tree-node>

      <!-- STATIC CHILD NODE - removes the "element action" -->
      <nm-tree-node
        *nmTreeNodeDef="let data; let api = api; when: isStaticChild"
        (onDrag)="onDrag($event)"
        (onDrop)="onDrop($event)"
        class="tree-node node"
      >
        <nm-ellipsis [content]="getWorkListRowContent(data)"></nm-ellipsis>

        <nm-tree-node-actions
          *ngIf="showActions"
          [data]="data"
          [menu-selectors]="nodeMenuSelectors"
          [icon-selectors]="nodeIconsSelectors"
        >
          <button
            mat-icon-button
            nm-tree-node-icon-action
            *ngIf="
              data.actions && data.actions.update && !data.hideUpdateAction
            "
            (click)="onEdit(data)"
          >
            <mat-icon color="primary" [svgIcon]="'form-textbox'"></mat-icon>
          </button>

          <button
            mat-icon-button
            nm-tree-node-icon-action
            *ngIf="data.actions && data.actions.delete"
            (click)="onDeleteClick(data, data.actions.delete)"
          >
            <mat-icon color="warn" *ngIf="data.actions.delete.confirmed"
              >warning</mat-icon
            >
            <mat-icon color="primary" *ngIf="!data.actions.delete.confirmed"
              >delete</mat-icon
            >
          </button>

          <button
            mat-menu-item
            nm-tree-node-menu-action
            *ngFor="let action of filteredActions(data)"
            (click)="onClickAction(data, action.name)"
            [disabled]="false"
          >
            <mat-icon [svgIcon]="action.icon"></mat-icon>
            <ng-container *ngIf="action.labelFromAction">
              {{ data.actions[action.name].title | translate }}
            </ng-container>
            <ng-container *ngIf="!action.labelFromAction">
              {{ action.label | translate }}
            </ng-container>
          </button>
        </nm-tree-node-actions>
      </nm-tree-node>

      <!-- ELEMENTS -->
      <nm-tree-node
        *nmTreeNodeDef="let data; let api = api; when: isNode"
        (onDrop)="onDrop($event)"
        (onDropped)="onDropped(data, $event)"
        class="tree-node node"
      >
        <mat-icon
          color="primary"
          *ngIf="showElementIcon"
          class="element-icon"
          [ngClass]="data.iconClass"
          [pTooltip]="data.iconTooltip | translate"
          (click)="onClickElementIcon($event, data)"
        >
          {{ data.icon }}
        </mat-icon>
        <nm-ellipsis [content]="getWorkListRowContent(data)"></nm-ellipsis>

        <nm-tree-node-actions
          *ngIf="showActions"
          [data]="data"
          [menu-selectors]="nodeMenuSelectors"
          [icon-selectors]="nodeIconsSelectors"
        >
          <button
            mat-icon-button
            nm-tree-node-icon-action
            *ngIf="
              data.actions && data.actions.update && !data.hideUpdateAction
            "
            (click)="onEdit(data)"
          >
            <mat-icon color="primary" [svgIcon]="'form-textbox'"></mat-icon>
          </button>

          <button
            mat-icon-button
            nm-tree-node-icon-action
            *ngIf="data.actions && data.actions.delete"
            (click)="onDeleteClick(data, data.actions.delete)"
          >
            <mat-icon color="warn" *ngIf="data.actions.delete.confirmed"
              >warning</mat-icon
            >
            <mat-icon color="primary" *ngIf="!data.actions.delete.confirmed"
              >delete</mat-icon
            >
          </button>

          <button
            mat-menu-item
            nm-tree-node-menu-action
            *ngFor="let action of filteredActions(data)"
            (click)="onClickAction(data, action.name)"
            [disabled]="false"
          >
            <mat-icon [svgIcon]="action.icon"></mat-icon>
            <ng-container *ngIf="action.labelFromAction">
              {{ data.actions[action.name].title | translate }}
            </ng-container>
            <ng-container *ngIf="!action.labelFromAction">
              {{ action.label | translate }}
            </ng-container>
          </button>
        </nm-tree-node-actions>
      </nm-tree-node>
    </nm-tree-selector>

    <nm-interaction-menu-bar
      *ngIf="footer && footer?.selectors"
      [menu]="footer?.selectors"
      [menuActionTemplate]="menuAction"
      [menuConfigurationTemplate]="menuComponentOrConfiguration"
    >
    </nm-interaction-menu-bar>
  </ng-container>
</nm-widgetframe>

<ng-template
  #menuAction
  let-onClick="onClick"
  let-configuration="configuration"
  let-param="param"
  let-disabled="disabled"
>
  <button mat-menu-item [disabled]="disabled | async" (click)="onClick($event)">
    <mat-icon
      *ngIf="configuration.icon"
      [svgIcon]="configuration.icon"
    ></mat-icon>
    <span>{{ configuration.description | translate }}</span>
  </button>
</ng-template>

<ng-template
  #menuComponentOrConfiguration
  let-configuration="configuration"
  let-param="param"
  let-disabled="disabled"
>
  <button mat-menu-item [disabled]="disabled | async">
    <mat-icon
      *ngIf="configuration.icon"
      [svgIcon]="configuration.icon"
    ></mat-icon>
    <span>{{ configuration.description | translate }}</span>
  </button>
</ng-template>
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""