nm-worklist-select
src/app/shared/widgets/worklist-select/worklist-select.component.ts
changeDetection | ChangeDetectionStrategy.OnPush |
selector | nm-worklist-select |
styleUrls | worklist-select.component.scss |
templateUrl | ./worklist-select.component.html |
constructor(dialogService: DialogService, halService: HalService, notificationService: CustomNotificationService, appstore: AppdataStore, widgetFrameService: WidgetframeService, cd: ChangeDetectorRef, appContext: AppContext, translateService: TranslateService, currentLocaleService: CurrentLocaleService)
|
||||||||||||||||||||||||||||||
Parameters :
|
addActions |
addActions()
|
Returns :
void
|
Private executeAction | ||||||||||||||||||||
executeAction(actionName: string, action: Action, node: any, reload: boolean)
|
||||||||||||||||||||
Parameters :
Returns :
Observable<ActionEvent>
|
Public executeActionAndShowMessage | ||||||||||||||||||||
executeActionAndShowMessage(name: string, action: Action, node: any, reload: boolean)
|
||||||||||||||||||||
Parameters :
Returns :
any
|
filteredActions | ||||
filteredActions(data: )
|
||||
Parameters :
Returns :
any[]
|
Private getAvailableActions |
getAvailableActions()
|
Returns :
any[]
|
Private getDefaultRefreshActions |
getDefaultRefreshActions()
|
Returns :
any[]
|
Public getWorkListRowContent | ||||||
getWorkListRowContent(data: any)
|
||||||
Parameters :
Returns :
string
|
Public isFolder | |||||||||
isFolder(index: , row: any)
|
|||||||||
Parameters :
Returns :
boolean
|
Public isNode | |||||||||
isNode(index: , row: any)
|
|||||||||
Parameters :
Returns :
boolean
|
Public isStatic | |||||||||
isStatic(index: , row: any)
|
|||||||||
Parameters :
Returns :
boolean
|
Public isStaticChild | |||||||||
isStaticChild(index: , row: any)
|
|||||||||
Parameters :
Returns :
boolean
|
Private loadNodes |
loadNodes()
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
Public onAddElement |
onAddElement()
|
Returns :
void
|
onClickAction | ||||||
onClickAction(data: , actionName: )
|
||||||
Parameters :
Returns :
void
|
Public onClickElementIcon | ||||||
onClickElementIcon(event: , data: )
|
||||||
Parameters :
Returns :
void
|
onDeleteClick | ||||||
onDeleteClick(node: , action: )
|
||||||
Parameters :
Returns :
void
|
Public onDrag | ||||||
onDrag(event: OnDragEvent)
|
||||||
Parameters :
Returns :
void
|
Public onDrop | ||||||
onDrop(event: OnDropEvent)
|
||||||
Parameters :
Returns :
void
|
Public onDropped | |||||||||
onDropped(node: , event: OnDroppedEvent)
|
|||||||||
Parameters :
Returns :
void
|
Public onEdit | ||||||
onEdit(data: any)
|
||||||
Parameters :
Returns :
void
|
Public onEditButtonClick | ||||
onEditButtonClick(event: )
|
||||
Parameters :
Returns :
void
|
Public onEdited | ||||||
onEdited(data: any)
|
||||||
Parameters :
Returns :
void
|
Public onElementMove | ||||
onElementMove(event: )
|
||||
Parameters :
Returns :
void
|
onSelect | ||||
onSelect(data: )
|
||||
Parameters :
Returns :
void
|
Private reload | |||||||||
reload(node: any, actionResponse?: HttpResponse
|
|||||||||
Parameters :
Returns :
void
|
widgetConfigure |
widgetConfigure()
|
Decorators : WidgetConfigure
|
Returns :
void
|
Public actions |
actions:
|
Type : any[]
|
Public addElementAction |
addElementAction:
|
Default value : new Subject()
|
Decorators : WidgetOutput
|
Public addElementConfig |
addElementConfig:
|
Type : any
|
Add new element configuration |
Public availableActions |
availableActions:
|
Type : any[]
|
Default value : []
|
Public changeElementStateAction |
changeElementStateAction:
|
Default value : new Subject<any>()
|
Decorators : WidgetOutput
|
Public configuration |
configuration:
|
Type : WidgetConfig<WorklistSelectConfiguration>
|
Decorators : WidgetConfiguration
|
Public descriptionField |
descriptionField:
|
Type : string
|
Public disableAddNewElementButton |
disableAddNewElementButton:
|
Type : boolean
|
Default value : false
|
Public disableAddNewElementButtonChannel |
disableAddNewElementButtonChannel:
|
Default value : new Subject<boolean>()
|
Decorators : WidgetInput
|
Public disableDeselect |
disableDeselect:
|
Type : boolean
|
Public disableElementAction |
disableElementAction:
|
Default value : new Subject<{
node: string;
action: string;
value: boolean;
}>()
|
Decorators : WidgetInput
|
Public dynamicHeight |
dynamicHeight:
|
Type : boolean
|
Default value : false
|
Public dynamicHeightAdditionalHeight |
dynamicHeightAdditionalHeight:
|
Type : string
|
Public dynamicTreeHeightInDialog |
dynamicTreeHeightInDialog:
|
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:
|
Type : string
|
Public elementSelected |
elementSelected:
|
Default value : new ReplaySubject<any>(1)
|
Decorators : WidgetOutput
|
Public emitElementMoved |
emitElementMoved:
|
Type : boolean
|
Public emitOnFolderSelect |
emitOnFolderSelect:
|
Type : boolean
|
Public focusSearchInput |
focusSearchInput:
|
Type : boolean
|
Public folderIconsSelectors |
folderIconsSelectors:
|
Type : Selectors
|
Public folderMenuSelectors |
folderMenuSelectors:
|
Type : Selectors
|
Public footer |
footer:
|
Type : any
|
Footer configuration |
Public forcedElementSelection |
forcedElementSelection:
|
Public height |
height:
|
Type : string
|
Public identifierField |
identifierField:
|
Type : string
|
Public isFolderHierarchy |
isFolderHierarchy:
|
Type : boolean
|
Public listFolderType |
listFolderType:
|
Type : string
|
Public listSelected |
listSelected:
|
Default value : new Subject<number>()
|
Decorators : WidgetOutput
|
Public loadElements |
loadElements:
|
Type : boolean
|
Public localStateRecovery |
localStateRecovery:
|
Type : boolean
|
Public localStorageSearch |
localStorageSearch:
|
Type : string
|
Public localStorageState |
localStorageState:
|
Type : string
|
Public multi |
multi:
|
Type : boolean
|
Element selector configuration |
Public nodeIconsSelectors |
nodeIconsSelectors:
|
Type : Selectors
|
Private nodeLoader |
nodeLoader:
|
Type : NodeLoader
|
Public nodeMenuSelectors |
nodeMenuSelectors:
|
Type : Selectors
|
Public nodes |
nodes:
|
Type : any[]
|
Private originalName |
originalName:
|
Type : String
|
Public refreshElementsActions |
refreshElementsActions:
|
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:
|
Type : boolean
|
Public showElementIcon |
showElementIcon:
|
Type : boolean
|
Public showFolderActions |
showFolderActions:
|
Type : boolean
|
Public showStaticFoldersOnly |
showStaticFoldersOnly:
|
Type : boolean
|
Public staticFolders |
staticFolders:
|
Type : any[]
|
Private treeSelector |
treeSelector:
|
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:
|
Type : string
|
Decorators : WidgetId
|
Public withHeader |
withHeader:
|
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>