UNPKG

@nakedobjects/gemini

Version:

Single Page Application client for a Naked Objects application.

914 lines (902 loc) 382 kB
import * as i0 from '@angular/core'; import { EventEmitter, Directive, Output, HostListener, Component, Input, ViewChildren, ElementRef, ViewChild, Injectable, ViewContainerRef, NgModule } from '@angular/core'; import forEach from 'lodash-es/forEach'; import map from 'lodash-es/map'; import mapValues from 'lodash-es/mapValues'; import zipObject from 'lodash-es/zipObject'; import * as i1 from '@angular/common'; import difference from 'lodash-es/difference'; import findIndex from 'lodash-es/findIndex'; import first from 'lodash-es/first'; import some from 'lodash-es/some'; import flatten from 'lodash-es/flatten'; import * as i3 from '@nakedobjects/view-models'; import { MenuViewModel, DomainObjectViewModel, ListViewModel, CollectionViewModel, ChoiceViewModel, validateDate, PropertyViewModel, RecentItemViewModel, copy, ParameterViewModel, SortType } from '@nakedobjects/view-models'; import * as Ro from '@nakedobjects/restful-objects'; import * as i1$1 from '@angular/router'; import { RouterModule } from '@angular/router'; import * as i2 from '@nakedobjects/services'; import { fixedDateFormat, supportedDateFormats, defaultTimeFormat, defaultShortTimeFormat, shortTimeFormat, CollectionViewState, InteractionMode, ErrorCategory, ClientErrorCode, HttpStatusCode, TypeResultCache, ViewType, Pane } from '@nakedobjects/services'; import * as i4$1 from '@angular/cdk/drag-drop'; import { DragDropModule } from '@angular/cdk/drag-drop'; import find from 'lodash-es/find'; import * as i4 from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import every from 'lodash-es/every'; import keys from 'lodash-es/keys'; import omit from 'lodash-es/omit'; import { BehaviorSubject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import concat from 'lodash-es/concat'; import { DateTime } from 'luxon'; import filter from 'lodash-es/filter'; import fromPairs from 'lodash-es/fromPairs'; import each from 'lodash-es/each'; import * as i2$1 from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http'; import { BrowserModule } from '@angular/platform-browser'; function safeUnsubscribe(sub) { if (sub) { sub.unsubscribe(); } } function isFocusable(nativeElement) { return !!(nativeElement && nativeElement instanceof Object && 'focus' in nativeElement); } function safeFocus(nativeElement) { if (isFocusable(nativeElement)) { nativeElement.focus(); } } function focus(element) { setTimeout(() => safeFocus(element?.nativeElement)); return true; } function createForm(dialog, formBuilder) { const pps = dialog.parameters; const parms = zipObject(map(pps, p => p.id), map(pps, p => p)); const controls = mapValues(parms, p => [p.getValueForControl(), (a) => p.validator(a)]); const form = formBuilder.group(controls); // eslint-disable-next-line @typescript-eslint/no-explicit-any const sub = form.valueChanges.subscribe((data) => { // cache parm values forEach(data, (v, k) => parms[k].setValueFromControl(v)); dialog.setParms(); }); return { form: form, dialog: dialog, parms: parms, sub: sub }; } function accept(droppableVm, component, draggableVm) { if (draggableVm) { draggableVm.canDropOn(droppableVm.returnType). then(canDrop => component.canDrop = canDrop). catch(() => component.canDrop = false); return true; } return false; } function dropOn(draggableVm, droppable, component) { if (component.canDrop) { droppable.drop(draggableVm) .then(() => { component.control.setValue(droppable.selectedChoice); }); } } function paste(event, droppable, component, get, clear) { const vKeyCode = 86; const deleteKeyCode = 46; if (event && (event.keyCode === vKeyCode && event.ctrlKey)) { const cvm = get(); if (cvm) { droppable.drop(cvm) .then(() => { component.control.setValue(droppable.selectedChoice); }); event.preventDefault(); } } if (event && event.keyCode === deleteKeyCode) { clear(); } } class ClickDirective { el; constructor(el) { this.el = el.nativeElement; } leftClick = new EventEmitter(); rightClick = new EventEmitter(); onClick() { this.leftClick.emit('event'); return false; } handleKey(event) { const enterKeyCode = 13; if (event.which === enterKeyCode) { const trigger = event.shiftKey ? this.rightClick : this.leftClick; trigger.emit('event'); return false; } return true; } onEnter(event) { return this.handleKey(event); } onEnter1(event) { return this.handleKey(event); } onContextMenu() { this.rightClick.emit('event'); return false; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ClickDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.9", type: ClickDirective, selector: "[nofClick]", outputs: { leftClick: "leftClick", rightClick: "rightClick" }, host: { listeners: { "click": "onClick()", "keydown": "onEnter($event)", "keypress": "onEnter1($event)", "contextmenu": "onContextMenu()" } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ClickDirective, decorators: [{ type: Directive, args: [{ selector: '[nofClick]' }] }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { leftClick: [{ type: Output }], rightClick: [{ type: Output }], onClick: [{ type: HostListener, args: ['click'] }], onEnter: [{ type: HostListener, args: ['keydown', ['$event']] }], onEnter1: [{ type: HostListener, args: ['keypress', ['$event']] }], onContextMenu: [{ type: HostListener, args: ['contextmenu'] }] } }); function wrapAction(a) { return { value: a.title, doClick: () => a.doInvoke(), doRightClick: () => a.doInvoke(true), show: () => true, disabled: () => a.disabled() ? true : null, tempDisabled: () => a.tempDisabled(), title: () => a.description, accesskey: null, presentationHint: a.presentationHint, showDialog: () => a.showDialog() }; } class ActionComponent { action; focusList; canClick() { return !(this.disabled() || this.tempDisabled()); } doClick() { if (this.canClick()) { this.action.doClick(); } } doRightClick() { if (this.canClick() && this.action.doRightClick) { this.action.doRightClick(); } } class() { return ({ tempdisabled: this.tempDisabled(), [this.dialogClass()]: true, }); } show() { return this.action.show(); } disabled() { return this.action.disabled(); } tempDisabled() { return this.action.tempDisabled(); } dialogClass() { return this.showDialog() ? 'has-params' : 'no-params'; } showDialog() { return this.action.showDialog(); } get value() { return this.action.value; } get title() { return this.action.title(); } focus() { if (this.disabled()) { return false; } return !!(this.focusList && this.focusList.first) && focus(this.focusList.first); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ActionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: ActionComponent, selector: "nof-action", inputs: { action: "action" }, viewQueries: [{ propertyName: "focusList", predicate: ["focus"], descendants: true }], ngImport: i0, template: "<input #focus tabindex=\"0\" type=\"button\" nofClick (leftClick)=\"doClick()\" (rightClick)=\"doRightClick()\" [value]=\"value\" [disabled]=\"disabled()\" *ngIf=\"show()\" [title]=\"title\" [ngClass]=\"class()\">\n", styles: ["input{white-space:normal;text-align:left;cursor:pointer;background-color:transparent;outline:none;border:none;font-size:var(--font-size-2);color:var(--menu-text-color);padding:var(--space-3);font-weight:var(--font-weight-1)}input:focus,input:hover{outline-color:var(--contrast-outline-color);outline-style:solid;outline-width:1px}input:disabled{color:var(--disabled-color);outline:none}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: ClickDirective, selector: "[nofClick]", outputs: ["leftClick", "rightClick"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ActionComponent, decorators: [{ type: Component, args: [{ selector: 'nof-action', template: "<input #focus tabindex=\"0\" type=\"button\" nofClick (leftClick)=\"doClick()\" (rightClick)=\"doRightClick()\" [value]=\"value\" [disabled]=\"disabled()\" *ngIf=\"show()\" [title]=\"title\" [ngClass]=\"class()\">\n", styles: ["input{white-space:normal;text-align:left;cursor:pointer;background-color:transparent;outline:none;border:none;font-size:var(--font-size-2);color:var(--menu-text-color);padding:var(--space-3);font-weight:var(--font-weight-1)}input:focus,input:hover{outline-color:var(--contrast-outline-color);outline-style:solid;outline-width:1px}input:disabled{color:var(--disabled-color);outline:none}\n"] }] }], propDecorators: { action: [{ type: Input, args: [{ required: true }] }], focusList: [{ type: ViewChildren, args: ['focus'] }] } }); class ActionListComponent { previousActionChildrenNames = []; holder; sub; actionChildren; set menuHolder(mh) { this.holder = mh; this.actionHolders = []; // clear cache; } get menuHolder() { return this.holder; } get items() { return this.menuHolder.menuItems; } actionHolders = []; getActionHolders(menuItem) { return map(menuItem.actions, a => wrapAction(a)); } hasActions = (menuItem) => { const actions = menuItem.actions; return actions && actions.length > 0; }; hasItems = (menuItem) => { const items = menuItem.menuItems; return items && items.length > 0; }; menuName = (menuItem) => menuItem.name; menuItems = (menuItem) => menuItem.menuItems; menuActions = (menuItem, index) => { if (!this.actionHolders[index]) { this.actionHolders[index] = this.getActionHolders(menuItem); } return this.actionHolders[index]; }; toggleCollapsed = (menuItem) => menuItem.toggleCollapsed(); navCollapsed = (menuItem) => menuItem.navCollapsed; displayClass = (menuItem) => ({ collapsed: menuItem.navCollapsed, open: !menuItem.navCollapsed, rootMenu: !menuItem.name }); classes(action) { const hint = action.presentationHint ?? ''; return hint.trim(); } focusFromIndex(actions, index = 0) { const toFocus = actions.toArray().slice(index); if (toFocus && toFocus.length > 0) { // until first element returns true some(toFocus, i => i.focus()); } } focus(actions) { if (actions && actions.length > 0) { const actionChildrenNames = map(actions.toArray(), a => a.action.value); const newActions = difference(actionChildrenNames, this.previousActionChildrenNames); let index = 0; if (newActions && newActions.length > 0) { const firstAction = first(newActions); index = findIndex(actions.toArray(), a => a.action.value === firstAction); index = index < 0 ? 0 : index; } this.previousActionChildrenNames = actionChildrenNames; this.focusFromIndex(actions, index); } } ngAfterViewInit() { this.focus(this.actionChildren); this.sub = this.actionChildren?.changes.subscribe((ql) => this.focus(ql)); } ngOnDestroy() { safeUnsubscribe(this.sub); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ActionListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: ActionListComponent, selector: "nof-action-list", inputs: { menuHolder: "menuHolder" }, viewQueries: [{ propertyName: "actionChildren", predicate: ActionComponent, descendants: true }], ngImport: i0, template: "<ng-container *ngFor=\"let menu of items; let i = index\">\n\n <div *ngIf=\"menuName(menu)\" (click)=\"toggleCollapsed(menu)\" (keydown.enter)=\"toggleCollapsed(menu)\" class=\"submenu\" [ngSwitch]=\"navCollapsed(menu)\" tabindex=\"0\">\n {{menuName(menu)}}\n <div *ngSwitchCase=\"true\" (keydown.enter)=\"toggleCollapsed(menu)\" class=\"icon-expand\" tabindex=\"0\"></div>\n <div *ngSwitchCase=\"false\" (keydown.enter)=\"toggleCollapsed(menu)\" class=\"icon-collapse\" tabindex=\"0\"></div>\n </div>\n <div *ngIf=\"!navCollapsed(menu)\" class=\"menuitem\" [ngClass]=\"displayClass(menu)\">\n <ng-container *ngIf=\"hasActions(menu)\">\n <ng-container *ngFor=\"let action of menuActions(menu, i)\">\n <nof-action [ngClass]=\"classes(action)\" [action]=\"action\"></nof-action>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"hasItems(menu)\">\n <nof-action-list [menuHolder]=\"menu\"></nof-action-list>\n </ng-container>\n </div>\n</ng-container>\n", styles: [":host{float:left;margin-bottom:var(--space-5);display:block;margin-right:var(--space-4);background-color:var(--menu-background-color)}nof-action,.submenu{display:block;cursor:pointer;outline:none;margin-right:var(--space-5);width:var(--action-width)}.submenu{padding:var(--space-3);color:var(--menu-text-color)}.submenu:hover{outline-style:solid;outline-width:1px;outline-color:var(--default-contrast-color)}.collapsed{display:none}.open{margin-left:var(--space-4)}.open.rootMenu{margin-left:0}.icon-expand:before{content:var(--submenu-expand-icon)}.icon-collapse:before{content:var(--submenu-collapse-icon)}.icon-expand,.icon-collapse{font-size:var(--font-size-1)}.icon-expand:hover,.icon-collapse:hover{outline-color:var(--contrast-outline-color);outline-width:1px}[class^=icon-],[class*=\" icon-\"]{font-family:iconFont;font-weight:var(--font-weight-1);font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;display:inline-block;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0% 0%;background-repeat:repeat;margin-top:0;position:relative}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: ActionListComponent, selector: "nof-action-list", inputs: ["menuHolder"] }, { kind: "component", type: ActionComponent, selector: "nof-action", inputs: ["action"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ActionListComponent, decorators: [{ type: Component, args: [{ selector: 'nof-action-list', template: "<ng-container *ngFor=\"let menu of items; let i = index\">\n\n <div *ngIf=\"menuName(menu)\" (click)=\"toggleCollapsed(menu)\" (keydown.enter)=\"toggleCollapsed(menu)\" class=\"submenu\" [ngSwitch]=\"navCollapsed(menu)\" tabindex=\"0\">\n {{menuName(menu)}}\n <div *ngSwitchCase=\"true\" (keydown.enter)=\"toggleCollapsed(menu)\" class=\"icon-expand\" tabindex=\"0\"></div>\n <div *ngSwitchCase=\"false\" (keydown.enter)=\"toggleCollapsed(menu)\" class=\"icon-collapse\" tabindex=\"0\"></div>\n </div>\n <div *ngIf=\"!navCollapsed(menu)\" class=\"menuitem\" [ngClass]=\"displayClass(menu)\">\n <ng-container *ngIf=\"hasActions(menu)\">\n <ng-container *ngFor=\"let action of menuActions(menu, i)\">\n <nof-action [ngClass]=\"classes(action)\" [action]=\"action\"></nof-action>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"hasItems(menu)\">\n <nof-action-list [menuHolder]=\"menu\"></nof-action-list>\n </ng-container>\n </div>\n</ng-container>\n", styles: [":host{float:left;margin-bottom:var(--space-5);display:block;margin-right:var(--space-4);background-color:var(--menu-background-color)}nof-action,.submenu{display:block;cursor:pointer;outline:none;margin-right:var(--space-5);width:var(--action-width)}.submenu{padding:var(--space-3);color:var(--menu-text-color)}.submenu:hover{outline-style:solid;outline-width:1px;outline-color:var(--default-contrast-color)}.collapsed{display:none}.open{margin-left:var(--space-4)}.open.rootMenu{margin-left:0}.icon-expand:before{content:var(--submenu-expand-icon)}.icon-collapse:before{content:var(--submenu-collapse-icon)}.icon-expand,.icon-collapse{font-size:var(--font-size-1)}.icon-expand:hover,.icon-collapse:hover{outline-color:var(--contrast-outline-color);outline-width:1px}[class^=icon-],[class*=\" icon-\"]{font-family:iconFont;font-weight:var(--font-weight-1);font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;display:inline-block;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0% 0%;background-repeat:repeat;margin-top:0;position:relative}\n"] }] }], propDecorators: { actionChildren: [{ type: ViewChildren, args: [ActionComponent] }], menuHolder: [{ type: Input }] } }); class ActionBarComponent { actions; set menuHolder(mhvm) { const menuItems = mhvm.menuItems; const avms = flatten(map(menuItems || [], (mi) => mi.actions)); this.actions = map(avms, a => wrapAction(a)); } actionChildren; sub; classes(action) { const hint = action.presentationHint ?? ''; return hint.trim(); } focusOnFirstAction(actions) { if (actions) { // until first element returns true some(actions.toArray(), i => i.focus()); } } ngAfterViewInit() { this.focusOnFirstAction(this.actionChildren); this.sub = this.actionChildren?.changes.subscribe((ql) => this.focusOnFirstAction(ql)); } ngOnDestroy() { safeUnsubscribe(this.sub); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ActionBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: ActionBarComponent, selector: "nof-action-bar", inputs: { actions: "actions", menuHolder: "menuHolder" }, viewQueries: [{ propertyName: "actionChildren", predicate: ActionComponent, descendants: true }], ngImport: i0, template: "<nof-action [ngClass]=\"classes(action)\" *ngFor=\"let action of actions\" [action]=\"action\"></nof-action>\n\n", styles: [":host{display:block}nof-action{outline:none;display:block;float:left;padding:var(--space-3);margin-left:var(--space-1);margin-right:var(--space-4);margin-bottom:var(--space-3);font-weight:var(--font-weight-2)er;font-size:var(--font-size-2)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: ActionComponent, selector: "nof-action", inputs: ["action"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ActionBarComponent, decorators: [{ type: Component, args: [{ selector: 'nof-action-bar', template: "<nof-action [ngClass]=\"classes(action)\" *ngFor=\"let action of actions\" [action]=\"action\"></nof-action>\n\n", styles: [":host{display:block}nof-action{outline:none;display:block;float:left;padding:var(--space-3);margin-left:var(--space-1);margin-right:var(--space-4);margin-bottom:var(--space-3);font-weight:var(--font-weight-2)er;font-size:var(--font-size-2)}\n"] }] }], propDecorators: { actions: [{ type: Input }], menuHolder: [{ type: Input }], actionChildren: [{ type: ViewChildren, args: [ActionComponent] }] } }); // updated by build do not update manually or change name or regex may not match const clientVersion = '16.1.0'; class ApplicationPropertiesComponent { viewModelFactory; constructor(viewModelFactory) { this.viewModelFactory = viewModelFactory; } get applicationName() { return this.applicationProperties?.applicationName ?? ''; } get userName() { return this.applicationProperties?.userName ?? ''; } get serverUrl() { return this.applicationProperties?.serverUrl ?? ''; } get implVersion() { return this.applicationProperties?.serverVersion?.implVersion ?? ''; } get apiVersion() { return this.applicationProperties?.serverVersion?.specVersion ?? ''; } get appVersion() { return this.applicationProperties?.serverVersion?.appVersion ?? ''; } get clientVersion() { return clientVersion; } applicationProperties; ngOnInit() { this.applicationProperties = this.viewModelFactory.applicationPropertiesViewModel(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ApplicationPropertiesComponent, deps: [{ token: i3.ViewModelFactoryService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: ApplicationPropertiesComponent, selector: "nof-application-properties", ngImport: i0, template: "<div id=\"pane1\" class=\"single\">\n <div class=\"applicationproperties\">\n <div class=\"header\">\n <div class=\"title\">Application Properties</div>\n </div>\n <div class=\"main-column\">\n <div class=\"properties\">\n <div class=\"property\">\n Application Name: {{applicationName}}\n </div>\n <div class=\"property\">\n User Name: {{userName}}\n </div>\n <div class=\"property\">\n Server Url: {{serverUrl}}\n </div>\n <div class=\"property\">\n Server API version: {{apiVersion}}\n </div>\n <div class=\"property\">\n Server Framework version: {{implVersion}}\n </div>\n <div class=\"property\">\n Server Application version: {{appVersion}}\n </div>\n <div class=\"property\">\n Client version: {{clientVersion}}\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;margin-left:var(--space-5);padding-left:var(--space-5);height:100%;overflow-y:auto}.header{display:block;margin-bottom:var(--space-5);overflow:hidden;color:var(--contrast-text-color);font-size:var(--font-size-4);font-weight:var(--font-weight-2);background-color:var(--header-background-color)}.title{position:relative;margin-left:var(--space-1);padding-left:var(--space-3);padding-right:var(--space-3);margin-right:var(--space-5);margin-top:var(--space-1);display:inline-block}.properties{color:var(--default-text-color);font-size:var(--font-size-2);font-weight:var(--font-weight-1);width:var(--field-width);padding:var(--space-3);margin-bottom:var(--space-5)}\n"] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ApplicationPropertiesComponent, decorators: [{ type: Component, args: [{ selector: 'nof-application-properties', template: "<div id=\"pane1\" class=\"single\">\n <div class=\"applicationproperties\">\n <div class=\"header\">\n <div class=\"title\">Application Properties</div>\n </div>\n <div class=\"main-column\">\n <div class=\"properties\">\n <div class=\"property\">\n Application Name: {{applicationName}}\n </div>\n <div class=\"property\">\n User Name: {{userName}}\n </div>\n <div class=\"property\">\n Server Url: {{serverUrl}}\n </div>\n <div class=\"property\">\n Server API version: {{apiVersion}}\n </div>\n <div class=\"property\">\n Server Framework version: {{implVersion}}\n </div>\n <div class=\"property\">\n Server Application version: {{appVersion}}\n </div>\n <div class=\"property\">\n Client version: {{clientVersion}}\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [":host{display:block;margin-left:var(--space-5);padding-left:var(--space-5);height:100%;overflow-y:auto}.header{display:block;margin-bottom:var(--space-5);overflow:hidden;color:var(--contrast-text-color);font-size:var(--font-size-4);font-weight:var(--font-weight-2);background-color:var(--header-background-color)}.title{position:relative;margin-left:var(--space-1);padding-left:var(--space-3);padding-right:var(--space-3);margin-right:var(--space-5);margin-top:var(--space-1);display:inline-block}.properties{color:var(--default-text-color);font-size:var(--font-size-2);font-weight:var(--font-weight-1);width:var(--field-width);padding:var(--space-3);margin-bottom:var(--space-5)}\n"] }] }], ctorParameters: () => [{ type: i3.ViewModelFactoryService }] }); class PaneComponent { activatedRoute; urlManager; context; constructor(activatedRoute, urlManager, context) { this.activatedRoute = activatedRoute; this.urlManager = urlManager; this.context = context; } activatedRouteDataSub; paneRouteDataSub; lastPaneRouteData; // pane API paneId; paneType; paneIdName; arData; onChild() { setTimeout(() => this.paneType = 'split'); } onChildless() { setTimeout(() => this.paneType = 'single'); } doSetup(routeData) { return !routeData.isEqual(this.lastPaneRouteData); } ngOnInit() { this.activatedRouteDataSub = this.activatedRoute.data.subscribe(d => { const data = d; this.arData = data; this.paneId = data.pane; this.paneType = data.paneType; this.paneIdName = this.paneId === 1 ? 'pane1' : 'pane2'; if (!this.paneRouteDataSub) { this.paneRouteDataSub = this.urlManager.getPaneRouteDataObservable(this.paneId) .subscribe((paneRouteData) => { if (!paneRouteData.isEqualIgnoringReload(this.lastPaneRouteData)) { // only remove messages if something more than reload flag has changed this.context.clearMessages(); this.context.clearWarnings(); } if (this.doSetup(paneRouteData)) { this.lastPaneRouteData = paneRouteData; this.setup(paneRouteData); } }); } }); } ngOnDestroy() { safeUnsubscribe(this.activatedRouteDataSub); safeUnsubscribe(this.paneRouteDataSub); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: PaneComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: i2.UrlManagerService }, { token: i2.ContextService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: PaneComponent, selector: "ng-component", ngImport: i0, template: '<div></div>', isInline: true }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: PaneComponent, decorators: [{ type: Component, args: [{ template: '<div></div>' }] }], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i2.UrlManagerService }, { type: i2.ContextService }] }); class AttachmentComponent extends PaneComponent { viewModelFactory; error; configService; constructor(activatedRoute, urlManager, viewModelFactory, context, error, configService) { super(activatedRoute, urlManager, context); this.viewModelFactory = viewModelFactory; this.error = error; this.configService = configService; } // template API image; title = ''; setup(routeData) { const oid = Ro.ObjectIdWrapper.fromObjectId(routeData.objectId, this.configService.config.keySeparator); this.context.getObject(routeData.paneId, oid, routeData.interactionMode) .then((object) => { const attachmentId = routeData.attachmentId; const attachment = attachmentId ? object.propertyMember(attachmentId) : undefined; if (attachment) { const avm = this.viewModelFactory.attachmentViewModel(attachment, routeData.paneId); if (avm) { avm.setImage(this); } } }) .catch((reject) => this.error.handleError(reject)); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: AttachmentComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: i2.UrlManagerService }, { token: i3.ViewModelFactoryService }, { token: i2.ContextService }, { token: i2.ErrorService }, { token: i2.ConfigService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: AttachmentComponent, selector: "nof-attachment", usesInheritance: true, ngImport: i0, template: "<div [attr.id]=\"paneIdName\" [ngClass]=\"paneType\">\n <div class=\"attachment view\">\n <div class=\"reference\">\n <img *ngIf=\"image\" src=\"{{image}}\" alt=\"{{title}}\" />\n </div>\n </div>\n</div>\n<router-outlet (activate)=\"onChild()\" (deactivate)=\"onChildless()\"></router-outlet>\n", styles: [".attachment{justify-content:center;align-items:center;display:flex;height:100%}img{max-width:100%;height:auto}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: AttachmentComponent, decorators: [{ type: Component, args: [{ selector: 'nof-attachment', template: "<div [attr.id]=\"paneIdName\" [ngClass]=\"paneType\">\n <div class=\"attachment view\">\n <div class=\"reference\">\n <img *ngIf=\"image\" src=\"{{image}}\" alt=\"{{title}}\" />\n </div>\n </div>\n</div>\n<router-outlet (activate)=\"onChild()\" (deactivate)=\"onChildless()\"></router-outlet>\n", styles: [".attachment{justify-content:center;align-items:center;display:flex;height:100%}img{max-width:100%;height:auto}\n"] }] }], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: i2.UrlManagerService }, { type: i3.ViewModelFactoryService }, { type: i2.ContextService }, { type: i2.ErrorService }, { type: i2.ConfigService }] }); class AttachmentPropertyComponent { error; urlManager; clickHandlerService; constructor(error, urlManager, clickHandlerService) { this.error = error; this.urlManager = urlManager; this.clickHandlerService = clickHandlerService; } attach = null; set attachment(avm) { this.attach = avm; this.setup(); } get attachment() { return this.attach; } title = 'Empty'; image; doAttachmentClick = (right) => { if (this.attachment.empty && !this.image) { return; } if (this.attachment.displayInline()) { this.urlManager.setAttachment(this.attachment.link, this.clickHandlerService.pane(this.attachment.onPaneId, right)); } else { this.attachment.downloadFile() .then(blob => { const burl = URL.createObjectURL(blob); window.open(burl); }) .catch((reject) => this.error.handleError(reject)); } }; setup() { if (this.attachment) { if (this.attachment.displayInline()) { this.attachment.setImage(this); } else { this.attachment.setTitle(this); } } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: AttachmentPropertyComponent, deps: [{ token: i2.ErrorService }, { token: i2.UrlManagerService }, { token: i2.ClickHandlerService }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: AttachmentPropertyComponent, selector: "nof-attachment-property", inputs: { attachment: "attachment" }, ngImport: i0, template: "<div *ngIf=\"attachment\" class=\"reference file-attachment\" nofClick (leftClick)=\"doAttachmentClick()\" (rightClick)=\"doAttachmentClick(true)\" tabindex=\"0\">\n <div *ngIf=\"!attachment.empty && !image\">{{title}}</div>\n <img *ngIf=\"!attachment.empty && image\" src=\"{{image}}\" alt=\"{{title}}\" />\n <div *ngIf=\"attachment.empty\">{{title}}</div>\n</div>", styles: [".reference{cursor:pointer}img{max-width:var(--field-value-width);max-height:100px}.value{display:block;float:left;width:var(--field-value-width);padding-left:var(--space-3);padding-right:var(--space-3);margin:var(--space-1)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: ClickDirective, selector: "[nofClick]", outputs: ["leftClick", "rightClick"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: AttachmentPropertyComponent, decorators: [{ type: Component, args: [{ selector: 'nof-attachment-property', template: "<div *ngIf=\"attachment\" class=\"reference file-attachment\" nofClick (leftClick)=\"doAttachmentClick()\" (rightClick)=\"doAttachmentClick(true)\" tabindex=\"0\">\n <div *ngIf=\"!attachment.empty && !image\">{{title}}</div>\n <img *ngIf=\"!attachment.empty && image\" src=\"{{image}}\" alt=\"{{title}}\" />\n <div *ngIf=\"attachment.empty\">{{title}}</div>\n</div>", styles: [".reference{cursor:pointer}img{max-width:var(--field-value-width);max-height:100px}.value{display:block;float:left;width:var(--field-value-width);padding-left:var(--space-3);padding-right:var(--space-3);margin:var(--space-1)}\n"] }] }], ctorParameters: () => [{ type: i2.ErrorService }, { type: i2.UrlManagerService }, { type: i2.ClickHandlerService }], propDecorators: { attachment: [{ type: Input }] } }); class BaseDialogComponent { viewModelFactory; error; context; formBuilder; constructor(viewModelFactory, error, context, formBuilder) { this.viewModelFactory = viewModelFactory; this.error = error; this.context = context; this.formBuilder = formBuilder; } parentViewModel; parms; formSub; sub; createFormSub; set parent(parent) { this.parentChanged = this.parentViewModel !== parent; this.parentViewModel = parent; } get parent() { return this.parentViewModel; } currentDialogId; parentChanged = false; set selectedDialogId(id) { this.currentDialogId = id; } get selectedDialogId() { return this.currentDialogId; } dialog = null; form; get title() { const dialog = this.dialog; return dialog ? dialog.title : ''; } get message() { const dialog = this.dialog; return dialog ? dialog.getMessage() : ''; } get parameters() { const dialog = this.dialog; return dialog ? dialog.parameters : []; } get tooltip() { const dialog = this.dialog; return dialog ? dialog.tooltip() : ''; } onSubmit(right) { if (this.dialog) { forEach(this.parms, (p, _) => { if (p.isEditable) { const newValue = this.form.value[p.id]; p.setValueFromControl(newValue); } }); this.dialog.doInvoke(right); } } close = () => { if (this.dialog) { this.dialog.doCloseReplaceHistory(); this.dialog = null; } }; createForm(dialog) { safeUnsubscribe(this.formSub); safeUnsubscribe(this.createFormSub); ({ form: this.form, dialog: this.dialog, parms: this.parms, sub: this.createFormSub } = createForm(dialog, this.formBuilder)); this.formSub = this.form.valueChanges.subscribe((_) => this.onValueChanged()); } onValueChanged() { if (this.dialog) { // clear messages if dialog changes this.dialog.resetMessage(); this.context.clearMessages(); this.context.clearWarnings(); } } closeExistingDialog() { if (this.dialog) { if (this.dialog.id !== this.currentDialogId) { this.dialog.doCloseKeepHistory(); } else { this.dialog.doCloseKeepUrl(); } this.dialog = null; } } getDialog() { // if it's the same dialog just return if (this.parent && this.currentDialogId) { if (!this.parentChanged && this.dialog && this.dialog.id === this.currentDialogId) { return; } this.parentChanged = false; const p = this.parent; let action = null; let actionViewModel = null; if (p instanceof MenuViewModel) { action = p.menuRep.actionMember(this.currentDialogId); } if (p instanceof DomainObjectViewModel && p.domainObject.hasActionMember(this.currentDialogId)) { action = p.domainObject.actionMember(this.currentDialogId); } if (p instanceof ListViewModel) { action = p.actionMember(this.currentDialogId); actionViewModel = find(p.actions, a => a.actionRep.actionId() === this.currentDialogId) || null; } if (p instanceof CollectionViewModel && p.hasMatchingLocallyContributedAction(this.currentDialogId)) { action = p.actionMember(this.currentDialogId); actionViewModel = find(p.actions, a => a.actionRep.actionId() === this.currentDialogId) || null; } if (action) { this.context.getInvokableAction(action) .then(details => { // only if we still have a dialog (may have beenn removed while getting invokable action) if (this.currentDialogId) { // must be a change this.closeExistingDialog(); const dialogViewModel = this.viewModelFactory.dialogViewModel(this.parent.routeData, details, actionViewModel, false); this.createForm(dialogViewModel); } }) .catch((reject) => { this.error.handleError(reject); }); } else { this.closeExistingDialog(); } } else { this.closeExistingDialog(); } } ngOnDestroy() { safeUnsubscribe(this.createFormSub); safeUnsubscribe(this.formSub); safeUnsubscribe(this.sub); this.closeExistingDialog(); } ngOnChanges() { this.getDialog(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: BaseDialogComponent, deps: [{ token: i3.ViewModelFactoryService }, { token: i2.ErrorService }, { token: i2.ContextService }, { token: i4.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: BaseDialogComponent, selector: "ng-component", inputs: { selectedDialogId: "selectedDialogId" }, usesOnChanges: true, ngImport: i0, template: '<div></div>', isInline: true }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: BaseDialogComponent, decorators: [{ type: Component, args: [{ template: '<div></div>' }] }], ctorParameters: () => [{ type: i3.ViewModelFactoryService }, { type: i2.ErrorService }, { type: i2.ContextService }, { type: i4.FormBuilder }], propDecorators: { selectedDialogId: [{ type: Input }] } }); class FieldComponent { loggerService; renderer; dragAndDrop; constructor(loggerService, renderer, dragAndDrop) { this.loggerService = loggerService; this.renderer = renderer; this.dragAndDrop = dragAndDrop; } set formGroup(fm) { this.formGrp = fm; this.formGrp.valueChanges.pipe(debounceTime(200)).subscribe(_ => this.onValueChanged()); this.onValueChanged(); // (re)set validation messages now } get formGroup() { return this.formGrp; } get message() { return this.model.getMessage(); } get isBoolean() { return this.model.returnType === 'boolean'; } get subject() { if (!this.bSubject) { const initialValue = this.control.value; this.bSubject = new BehaviorSubject(initialValue); this.sub = this.control.valueChanges.subscribe((data) => { this.bSubject.next(data); }); } return this.bSubject; } formGrp; vmParent; model; isConditionalChoices; isAutoComplete; bSubject; sub; lastArgs; control; currentOptions = []; pArgs; paneId; canDrop = false; dragOver = false; init(vmParent, vm, control) { this.vmParent = vmParent; this.model = vm; this.control = control; this.paneId = this.model.onPaneId; this.isConditionalChoices = (this.model.entryType === Ro.EntryType.ConditionalChoices || this.model.entryType === Ro.EntryType.MultipleConditionalChoices); this.isAutoComplete = this.model.entryType === Ro.EntryType.AutoComplete; if (this.isConditionalChoices) { this.pArgs = omit(this.model.promptArguments, 'x-ro-nof-members'); this.populateDropdown(); } } get accept() { // eslint-disable-next-line @typescript-eslint/no-this-alias const _this = this; return (cdkDrag, _cdkDropList) => { return accept(_this.model, _this, cdkDrag.data); }; } drop(event) { const cdkDrag = event.item; if (event.isPointerOverContainer) { dropOn(cdkDrag.data, this.model, this); } this.canDrop = false; this.dragOver = false; } exit() { this.canDrop = false; this.dragOver = false; } enter() { this.dragOver = true; } isDomainObjectViewModel(object) { return !!(object && object instanceof Object && 'properties' in object); } mapValues(args, parmsOrProps) { return mapValues(args, (v, n) => { const pop = find(parmsOrProps, p => p.argId === n); return pop.getValue(); }); } populateArguments() { const dialog = this.vmParent; const object = this.vmParent; if (!dialog && !object) { this.loggerService.throw('FieldComponent:populateArguments Expect dialog or object'); } let parmsOrProps; if (this.isDomainObjectViewModel(object)) { parmsOrProps = object.properties; } else { parmsOrProps = dialog.parameters; } return this.mapValues(this.pArgs, parmsOrProps); } argsChanged(newArgs) { const same = this.lastArgs && keys(this.lastArgs).length === keys(newArgs).length && every(this.lastArgs, (v, k) => newArgs[k].toValueString() === v.toValueString()); this.lastArgs = newArgs; return !same; } populateDropdown() { const nArgs = this.populateArguments(); if (this.argsChanged(nArgs)) { const prompts = this.model.conditionalChoices; if (prompts) { prompts(nArgs). then((cvms) => { // if unchanged return if (cvms.length === this.currentOptions.length && every(cvms, (c, i) => c.equals(this.currentOptions[i]))) { return; } this.model.choices = cvms; this.currentOptions = cvms; if (this.isConditionalChoices) { // need to reset control to find the selected options if (this.model.entryType === Ro.EntryType.MultipleConditionalChoices) { this.control.reset(this.model.selectedMultiChoices); } else { this.control.reset(this.model.selectedChoice); } } }). catch(() => { // error clear everything this.model.selectedChoice = null; this.currentOptions = []; }); } } } onChange() { if (this.isConditionalChoices) { this.populateDropdown(); } else if (this.isAutoComplete) { this.populateAutoComplete(); } else if (this.isBoolean) { this.populateBoolean(); } } onValueChanged() { if (this.model) { this.onChange(); } } populateAutoComplete() { const input = this.control.value; if (input instanceof ChoiceViewModel) { return; } const prompt = this.model.prompt; if (prompt && input && input.length > 0 && input.length >= (this.model.minLength ?? 0)) { prompt(input) .then((cvms) => { if (cvms.length === this.currentOptions.length && every(cvms, (c, i) => c.equals(this.currentOptions[i]))) { return; } this.model.choices = cvms; this.currentOptions = cvms; this.model.selectedChoice = null; }) .catch(() => { this.model.choices = []; this.currentOptions = []; this.model.selectedChoice = null; }); } else { this.model.choices = []; this.currentOptions = []; this.model.selectedChoice = null; } } populateBoolean() { // editable booleans only if (this.isBoolean && this.control) { const input = this.control.value; const element = this.checkboxList?.first.nativeElement; if (input == null) { this.renderer.setProperty(element, 'indeterminate', true); this.renderer.setProperty(element, 'checked', null); } else { this.renderer.setProperty(element, 'indeterminate', false); this.renderer.setProperty(element, 'checked', !!input); } } } select(item) { this