mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-11 03:53:02 +00:00
Manage different level of confidence in the submission
This commit is contained in:
@@ -49,33 +49,60 @@ module.exports = {
|
|||||||
// NOTE: every how many minutes submission is saved automatically
|
// NOTE: every how many minutes submission is saved automatically
|
||||||
timer: 5
|
timer: 5
|
||||||
},
|
},
|
||||||
metadata: {
|
icons: {
|
||||||
// NOTE: allow to set icons used to represent metadata belonging to a relation group
|
metadata: [
|
||||||
icons: [
|
|
||||||
/**
|
/**
|
||||||
* NOTE: example of configuration
|
* NOTE: example of configuration
|
||||||
* {
|
* {
|
||||||
* // NOTE: metadata name
|
* // NOTE: metadata name
|
||||||
* name: 'dc.author',
|
* name: 'dc.author',
|
||||||
* config: {
|
* // NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used
|
||||||
* // NOTE: used when metadata value has an authority
|
* style: 'fa-user'
|
||||||
* withAuthority: {
|
|
||||||
* // NOTE: fontawesome (v4.x) icon classes and bootstrap color utility classes can be used
|
|
||||||
* style: 'fa-user'
|
|
||||||
* },
|
|
||||||
* // NOTE: used when metadata value has not an authority
|
|
||||||
* withoutAuthority: {
|
|
||||||
* style: 'fa-user text-muted'
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
{
|
||||||
|
// NOTE: metadata name
|
||||||
|
name: 'dc.author',
|
||||||
|
// NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used
|
||||||
|
style: 'fa-user'
|
||||||
|
},
|
||||||
// default configuration
|
// default configuration
|
||||||
{
|
{
|
||||||
name: 'default',
|
name: 'default',
|
||||||
config: {}
|
style: ''
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
authority: {
|
||||||
|
confidence: [
|
||||||
|
/**
|
||||||
|
* NOTE: example of configuration
|
||||||
|
* {
|
||||||
|
* // NOTE: confidence value
|
||||||
|
* value: 'dc.author',
|
||||||
|
* // NOTE: fontawesome (v4.x) icon classes and bootstrap utility classes can be used
|
||||||
|
* style: 'fa-user'
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
value: 600,
|
||||||
|
style: 'text-success'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 500,
|
||||||
|
style: 'text-info'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 400,
|
||||||
|
style: 'text-warning'
|
||||||
|
},
|
||||||
|
// default configuration
|
||||||
|
{
|
||||||
|
value: 'default',
|
||||||
|
style: 'text-muted'
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Angular Universal settings
|
// Angular Universal settings
|
||||||
|
44
src/app/core/integration/models/confidence-type.ts
Normal file
44
src/app/core/integration/models/confidence-type.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
export enum ConfidenceType {
|
||||||
|
/**
|
||||||
|
* This authority value has been confirmed as accurate by an
|
||||||
|
* interactive user or authoritative policy
|
||||||
|
*/
|
||||||
|
CF_ACCEPTED = 600,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value is singular and valid but has not been seen and accepted
|
||||||
|
* by a human, so its provenance is uncertain.
|
||||||
|
*/
|
||||||
|
CF_UNCERTAIN = 500,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are multiple matching authority values of equal validity.
|
||||||
|
*/
|
||||||
|
CF_AMBIGUOUS = 400,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are no matching answers from the authority.
|
||||||
|
*/
|
||||||
|
CF_NOTFOUND = 300,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The authority encountered an internal failure - this preserves a
|
||||||
|
* record in the metadata of why there is no value.
|
||||||
|
*/
|
||||||
|
CF_FAILED = 200,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The authority recommends this submission be rejected.
|
||||||
|
*/
|
||||||
|
CF_REJECTED = 100,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No reasonable confidence value is available
|
||||||
|
*/
|
||||||
|
CF_NOVALUE = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value has not been set (DB default).
|
||||||
|
*/
|
||||||
|
CF_UNSET = -1
|
||||||
|
}
|
@@ -0,0 +1,95 @@
|
|||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
ElementRef, EventEmitter,
|
||||||
|
HostListener,
|
||||||
|
Inject,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
Output,
|
||||||
|
Renderer2,
|
||||||
|
SimpleChanges
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { findIndex } from 'lodash';
|
||||||
|
|
||||||
|
import { AuthorityValue } from '../../core/integration/models/authority.value';
|
||||||
|
import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model';
|
||||||
|
import { ConfidenceType } from '../../core/integration/models/confidence-type';
|
||||||
|
import { isNotEmpty, isNull } from '../empty.util';
|
||||||
|
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||||
|
import { ConfidenceIconConfig } from '../../../config/submission-config.interface';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[dsAuthorityConfidenceState]'
|
||||||
|
})
|
||||||
|
export class AuthorityConfidenceStateDirective implements OnChanges {
|
||||||
|
|
||||||
|
@Input() authorityValue: AuthorityValue | FormFieldMetadataValueObject | string;
|
||||||
|
|
||||||
|
private previousClass: string = null;
|
||||||
|
private newClass: string;
|
||||||
|
|
||||||
|
@Output() whenClickOnConfidenceNotAccepted: EventEmitter<ConfidenceType> = new EventEmitter<ConfidenceType>();
|
||||||
|
|
||||||
|
@HostListener('click') onClick() {
|
||||||
|
if (isNotEmpty(this.authorityValue) && this.getConfidenceByValue(this.authorityValue) !== ConfidenceType.CF_ACCEPTED) {
|
||||||
|
this.whenClickOnConfidenceNotAccepted.emit(this.getConfidenceByValue(this.authorityValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||||
|
private elem: ElementRef,
|
||||||
|
private renderer: Renderer2
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (!changes.authorityValue.firstChange) {
|
||||||
|
this.previousClass = this.getClassByConfidence(this.getConfidenceByValue(changes.authorityValue.previousValue))
|
||||||
|
}
|
||||||
|
this.newClass = this.getClassByConfidence(this.getConfidenceByValue(changes.authorityValue.currentValue));
|
||||||
|
|
||||||
|
if (isNull(this.previousClass)) {
|
||||||
|
this.renderer.addClass(this.elem.nativeElement, this.newClass);
|
||||||
|
} else if (this.previousClass !== this.newClass) {
|
||||||
|
this.renderer.removeClass(this.elem.nativeElement, this.previousClass);
|
||||||
|
this.renderer.addClass(this.elem.nativeElement, this.newClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
if (isNull(this.previousClass)) {
|
||||||
|
this.renderer.addClass(this.elem.nativeElement, this.newClass);
|
||||||
|
} else if (this.previousClass !== this.newClass) {
|
||||||
|
this.renderer.removeClass(this.elem.nativeElement, this.previousClass);
|
||||||
|
this.renderer.addClass(this.elem.nativeElement, this.newClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getConfidenceByValue(value: any): ConfidenceType {
|
||||||
|
let confidence: ConfidenceType = ConfidenceType.CF_UNSET;
|
||||||
|
|
||||||
|
if (isNotEmpty(value) && value instanceof AuthorityValue && value.hasAuthority()) {
|
||||||
|
confidence = ConfidenceType.CF_ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotEmpty(value) && value instanceof FormFieldMetadataValueObject) {
|
||||||
|
confidence = value.confidence;
|
||||||
|
}
|
||||||
|
|
||||||
|
return confidence;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getClassByConfidence(confidence: any): string {
|
||||||
|
const confidenceIcons: ConfidenceIconConfig[] = this.EnvConfig.submission.icons.authority.confidence;
|
||||||
|
|
||||||
|
const confidenceIndex: number = findIndex(confidenceIcons, {value: confidence});
|
||||||
|
|
||||||
|
const defaultconfidenceIndex: number = findIndex(confidenceIcons, {value: 'default'});
|
||||||
|
const defaultClass: string = (defaultconfidenceIndex !== -1) ? confidenceIcons[defaultconfidenceIndex].style : '';
|
||||||
|
|
||||||
|
return (confidenceIndex !== -1) ? confidenceIcons[confidenceIndex].style : defaultClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,7 +20,8 @@
|
|||||||
[ngClass]="{'chip-selected disabled': (editable && c.editMode) || dragged == i}"
|
[ngClass]="{'chip-selected disabled': (editable && c.editMode) || dragged == i}"
|
||||||
(click)="chipsSelected($event, i);">
|
(click)="chipsSelected($event, i);">
|
||||||
<span>
|
<span>
|
||||||
<ng-container *ngIf="c.hasIcons()">
|
<i *ngIf="showIcons && !c.isNestedItem()" dsAuthorityConfidenceState [authorityValue]="c.item" class="fa fa-circle-o" aria-hidden="true"></i>
|
||||||
|
<ng-container *ngIf="showIcons && c.hasIcons()">
|
||||||
<i *ngFor="let icon of c.icons; let l = last"
|
<i *ngFor="let icon of c.icons; let l = last"
|
||||||
[ngbTooltip]="tipContent"
|
[ngbTooltip]="tipContent"
|
||||||
triggers="manual"
|
triggers="manual"
|
||||||
@@ -28,6 +29,8 @@
|
|||||||
class="fa {{icon.style}}"
|
class="fa {{icon.style}}"
|
||||||
[class.mr-1]="!l"
|
[class.mr-1]="!l"
|
||||||
[class.mr-2]="l"
|
[class.mr-2]="l"
|
||||||
|
dsAuthorityConfidenceState
|
||||||
|
[authorityValue]="c.item[icon.metadata] || c.item"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
(dragstart)="tooltip.close();"
|
(dragstart)="tooltip.close();"
|
||||||
(mouseover)="showTooltip(t, i, icon.metadata)"
|
(mouseover)="showTooltip(t, i, icon.metadata)"
|
||||||
|
@@ -19,6 +19,7 @@ export class ChipsComponent implements OnChanges {
|
|||||||
@Input() chips: Chips;
|
@Input() chips: Chips;
|
||||||
@Input() wrapperClass: string;
|
@Input() wrapperClass: string;
|
||||||
@Input() editable = true;
|
@Input() editable = true;
|
||||||
|
@Input() showIcons = false;
|
||||||
|
|
||||||
@Output() selected: EventEmitter<number> = new EventEmitter<number>();
|
@Output() selected: EventEmitter<number> = new EventEmitter<number>();
|
||||||
@Output() remove: EventEmitter<number> = new EventEmitter<number>();
|
@Output() remove: EventEmitter<number> = new EventEmitter<number>();
|
||||||
|
@@ -3,7 +3,6 @@ import { isNotEmpty } from '../../empty.util';
|
|||||||
|
|
||||||
export interface ChipsItemIcon {
|
export interface ChipsItemIcon {
|
||||||
metadata: string;
|
metadata: string;
|
||||||
hasAuthority: boolean;
|
|
||||||
style: string;
|
style: string;
|
||||||
tooltip?: any;
|
tooltip?: any;
|
||||||
}
|
}
|
||||||
@@ -11,7 +10,7 @@ export interface ChipsItemIcon {
|
|||||||
export class ChipsItem {
|
export class ChipsItem {
|
||||||
public id: string;
|
public id: string;
|
||||||
public display: string;
|
public display: string;
|
||||||
public item: any;
|
private _item: any;
|
||||||
public editMode?: boolean;
|
public editMode?: boolean;
|
||||||
public icons?: ChipsItemIcon[];
|
public icons?: ChipsItemIcon[];
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ export class ChipsItem {
|
|||||||
editMode?: boolean) {
|
editMode?: boolean) {
|
||||||
|
|
||||||
this.id = uniqueId();
|
this.id = uniqueId();
|
||||||
this.item = item;
|
this._item = item;
|
||||||
this.fieldToDisplay = fieldToDisplay;
|
this.fieldToDisplay = fieldToDisplay;
|
||||||
this.objToDisplay = objToDisplay;
|
this.objToDisplay = objToDisplay;
|
||||||
this.setDisplayText();
|
this.setDisplayText();
|
||||||
@@ -33,6 +32,21 @@ export class ChipsItem {
|
|||||||
this.icons = icons || [];
|
this.icons = icons || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set item(item) {
|
||||||
|
this._item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get item() {
|
||||||
|
return this._item;
|
||||||
|
}
|
||||||
|
|
||||||
|
isNestedItem(): boolean {
|
||||||
|
return (isNotEmpty(this.item)
|
||||||
|
&& isObject(this.item)
|
||||||
|
&& isNotEmpty(this.objToDisplay)
|
||||||
|
&& this.item[this.objToDisplay]);
|
||||||
|
}
|
||||||
|
|
||||||
hasIcons(): boolean {
|
hasIcons(): boolean {
|
||||||
return isNotEmpty(this.icons);
|
return isNotEmpty(this.icons);
|
||||||
}
|
}
|
||||||
@@ -46,7 +60,7 @@ export class ChipsItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateItem(item: any): void {
|
updateItem(item: any): void {
|
||||||
this.item = item;
|
this._item = item;
|
||||||
this.setDisplayText();
|
this.setDisplayText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,10 +69,10 @@ export class ChipsItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setDisplayText(): void {
|
private setDisplayText(): void {
|
||||||
let value = this.item;
|
let value = this._item;
|
||||||
if (isObject(this.item)) {
|
if (isObject(this._item)) {
|
||||||
// Check If displayField is in an internal object
|
// Check If displayField is in an internal object
|
||||||
const obj = this.objToDisplay ? this.item[this.objToDisplay] : this.item;
|
const obj = this.objToDisplay ? this._item[this.objToDisplay] : this._item;
|
||||||
|
|
||||||
if (isObject(obj) && obj) {
|
if (isObject(obj) && obj) {
|
||||||
value = obj[this.fieldToDisplay] || obj.value;
|
value = obj[this.fieldToDisplay] || obj.value;
|
||||||
|
@@ -3,34 +3,20 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
|||||||
import { ChipsItem, ChipsItemIcon } from './chips-item.model';
|
import { ChipsItem, ChipsItemIcon } from './chips-item.model';
|
||||||
import { hasValue, isNotEmpty } from '../../empty.util';
|
import { hasValue, isNotEmpty } from '../../empty.util';
|
||||||
import { PLACEHOLDER_PARENT_METADATA } from '../../form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model';
|
import { PLACEHOLDER_PARENT_METADATA } from '../../form/builder/ds-dynamic-form-ui/models/dynamic-group/dynamic-group.model';
|
||||||
|
import { MetadataIconConfig } from '../../../../config/submission-config.interface';
|
||||||
export interface IconsConfig {
|
|
||||||
withAuthority?: {
|
|
||||||
style: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
withoutAuthority?: {
|
|
||||||
style: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataIconsConfig {
|
|
||||||
name: string;
|
|
||||||
config: IconsConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Chips {
|
export class Chips {
|
||||||
chipsItems: BehaviorSubject<ChipsItem[]>;
|
chipsItems: BehaviorSubject<ChipsItem[]>;
|
||||||
displayField: string;
|
displayField: string;
|
||||||
displayObj: string;
|
displayObj: string;
|
||||||
iconsConfig: MetadataIconsConfig[];
|
iconsConfig: MetadataIconConfig[];
|
||||||
|
|
||||||
private _items: ChipsItem[];
|
private _items: ChipsItem[];
|
||||||
|
|
||||||
constructor(items: any[] = [],
|
constructor(items: any[] = [],
|
||||||
displayField: string = 'display',
|
displayField: string = 'display',
|
||||||
displayObj?: string,
|
displayObj?: string,
|
||||||
iconsConfig?: MetadataIconsConfig[]) {
|
iconsConfig?: MetadataIconConfig[]) {
|
||||||
|
|
||||||
this.displayField = displayField;
|
this.displayField = displayField;
|
||||||
this.displayObj = displayObj;
|
this.displayObj = displayObj;
|
||||||
@@ -115,8 +101,8 @@ export class Chips {
|
|||||||
private getChipsIcons(item) {
|
private getChipsIcons(item) {
|
||||||
const icons = [];
|
const icons = [];
|
||||||
const defaultConfigIndex: number = findIndex(this.iconsConfig, {name: 'default'});
|
const defaultConfigIndex: number = findIndex(this.iconsConfig, {name: 'default'});
|
||||||
const defaultConfig: IconsConfig = (defaultConfigIndex !== -1) ? this.iconsConfig[defaultConfigIndex].config : undefined;
|
const defaultConfig: MetadataIconConfig = (defaultConfigIndex !== -1) ? this.iconsConfig[defaultConfigIndex] : undefined;
|
||||||
let config: IconsConfig;
|
let config: MetadataIconConfig;
|
||||||
let configIndex: number;
|
let configIndex: number;
|
||||||
let value: any;
|
let value: any;
|
||||||
|
|
||||||
@@ -126,26 +112,31 @@ export class Chips {
|
|||||||
value = item[metadata];
|
value = item[metadata];
|
||||||
configIndex = findIndex(this.iconsConfig, {name: metadata});
|
configIndex = findIndex(this.iconsConfig, {name: metadata});
|
||||||
|
|
||||||
config = (configIndex !== -1) ? this.iconsConfig[configIndex].config : defaultConfig;
|
config = (configIndex !== -1) ? this.iconsConfig[configIndex] : defaultConfig;
|
||||||
|
|
||||||
if (hasValue(value) && isNotEmpty(config) && !this.hasPlaceholder(value)) {
|
if (hasValue(value) && isNotEmpty(config) && !this.hasPlaceholder(value)) {
|
||||||
|
|
||||||
let icon: ChipsItemIcon;
|
let icon: ChipsItemIcon;
|
||||||
const hasAuthority: boolean = !!(isObject(value) && ((value.hasOwnProperty('authority') && value.authority) || (value.hasOwnProperty('id') && value.id)));
|
|
||||||
|
|
||||||
// Set icons
|
// Set icon
|
||||||
if ((this.displayObj && this.displayObj === metadata && hasAuthority)
|
icon = {
|
||||||
|
metadata,
|
||||||
|
style: config.style
|
||||||
|
};
|
||||||
|
|
||||||
|
icons.push(icon);
|
||||||
|
/* if ((this.displayObj && this.displayObj === metadata && hasAuthority)
|
||||||
|| (this.displayObj && this.displayObj !== metadata)) {
|
|| (this.displayObj && this.displayObj !== metadata)) {
|
||||||
|
|
||||||
icon = {
|
icon = {
|
||||||
metadata,
|
metadata,
|
||||||
hasAuthority: hasAuthority,
|
hasAuthority: hasAuthority,
|
||||||
style: (hasAuthority) ? config.withAuthority.style : config.withoutAuthority.style
|
style: config.style
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (icon) {
|
if (icon) {
|
||||||
icons.push(icon);
|
icons.push(icon);
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -68,6 +68,7 @@
|
|||||||
*ngIf="chips && chips.hasItems()"
|
*ngIf="chips && chips.hasItems()"
|
||||||
[chips]="chips"
|
[chips]="chips"
|
||||||
[editable]="true"
|
[editable]="true"
|
||||||
|
[showIcons]="true"
|
||||||
(selected)="onChipSelected($event)"></ds-chips>
|
(selected)="onChipSelected($event)"></ds-chips>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -15,7 +15,7 @@ import { Subscription } from 'rxjs/Subscription';
|
|||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { filter, flatMap, map, mergeMap, scan } from 'rxjs/operators';
|
import { filter, flatMap, map, mergeMap, scan } from 'rxjs/operators';
|
||||||
import { DynamicFormControlModel, DynamicFormGroupModel, DynamicInputModel } from '@ng-dynamic-forms/core';
|
import { DynamicFormControlModel, DynamicFormGroupModel, DynamicInputModel } from '@ng-dynamic-forms/core';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual, isObject } from 'lodash';
|
||||||
|
|
||||||
import { DynamicGroupModel, PLACEHOLDER_PARENT_METADATA } from './dynamic-group.model';
|
import { DynamicGroupModel, PLACEHOLDER_PARENT_METADATA } from './dynamic-group.model';
|
||||||
import { FormBuilderService } from '../../../form-builder.service';
|
import { FormBuilderService } from '../../../form-builder.service';
|
||||||
@@ -32,6 +32,8 @@ import { hasOnlyEmptyProperties } from '../../../../../object.util';
|
|||||||
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
||||||
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
||||||
import { IntegrationData } from '../../../../../../core/integration/integration-data';
|
import { IntegrationData } from '../../../../../../core/integration/integration-data';
|
||||||
|
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
|
||||||
|
import { AuthorityValue } from '../../../../../../core/integration/models/authority.value';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-dynamic-group',
|
selector: 'ds-dynamic-group',
|
||||||
@@ -219,7 +221,7 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
|
|||||||
let returnObj = Object.create({});
|
let returnObj = Object.create({});
|
||||||
returnObj = Object.keys(valueObj).map((fieldName) => {
|
returnObj = Object.keys(valueObj).map((fieldName) => {
|
||||||
let return$: Observable<any>;
|
let return$: Observable<any>;
|
||||||
if (valueObj[fieldName].hasAuthority() && isNotEmpty(valueObj[fieldName].authority)) {
|
if (isObject(valueObj[fieldName]) && valueObj[fieldName].hasAuthority() && isNotEmpty(valueObj[fieldName].authority)) {
|
||||||
const fieldId = fieldName.replace(/\./g, '_');
|
const fieldId = fieldName.replace(/\./g, '_');
|
||||||
const model = this.formBuilderService.findById(fieldId, this.formModel);
|
const model = this.formBuilderService.findById(fieldId, this.formModel);
|
||||||
const searchOptions: IntegrationSearchOptions = new IntegrationSearchOptions(
|
const searchOptions: IntegrationSearchOptions = new IntegrationSearchOptions(
|
||||||
@@ -231,7 +233,13 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
|
|||||||
1);
|
1);
|
||||||
|
|
||||||
return$ = this.authorityService.getEntryByValue(searchOptions)
|
return$ = this.authorityService.getEntryByValue(searchOptions)
|
||||||
.map((result: IntegrationData) => result.payload[0]);
|
.map((result: IntegrationData) => Object.assign(
|
||||||
|
new FormFieldMetadataValueObject(),
|
||||||
|
valueObj[fieldName],
|
||||||
|
{
|
||||||
|
otherInformation: (result.payload[0] as AuthorityValue).otherInformation
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return$ = Observable.of(valueObj[fieldName]);
|
return$ = Observable.of(valueObj[fieldName]);
|
||||||
}
|
}
|
||||||
@@ -262,6 +270,7 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
|
|||||||
}, []),
|
}, []),
|
||||||
filter((modelValues: any[]) => this.model.value.length === modelValues.length)
|
filter((modelValues: any[]) => this.model.value.length === modelValues.length)
|
||||||
).subscribe((modelValue) => {
|
).subscribe((modelValue) => {
|
||||||
|
this.model.valueUpdates.next(modelValue);
|
||||||
this.initChips(modelValue);
|
this.initChips(modelValue);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -272,7 +281,7 @@ export class DsDynamicGroupComponent implements OnDestroy, OnInit {
|
|||||||
initChipsValue,
|
initChipsValue,
|
||||||
'value',
|
'value',
|
||||||
this.model.mandatoryField,
|
this.model.mandatoryField,
|
||||||
this.EnvConfig.submission.metadata.icons);
|
this.EnvConfig.submission.icons.metadata);
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
this.chips.chipsItems
|
this.chips.chipsItems
|
||||||
.subscribe((subItems: any[]) => {
|
.subscribe((subItems: any[]) => {
|
||||||
|
@@ -4,7 +4,12 @@
|
|||||||
|
|
||||||
<div class="form-row align-items-center">
|
<div class="form-row align-items-center">
|
||||||
<!--Simple lookup, first field -->
|
<!--Simple lookup, first field -->
|
||||||
<div class="col">
|
<div class="col right-addon">
|
||||||
|
<i dsAuthorityConfidenceState
|
||||||
|
class="fa fa-circle-o fa-2x fa-fw position-absolute mt-1 p-0"
|
||||||
|
aria-hidden="true"
|
||||||
|
[authorityValue]="model.value"
|
||||||
|
(whenClickOnConfidenceNotAccepted)="whenClickOnConfidenceNotAccepted(sdRef, $event)"></i>
|
||||||
<input class="form-control"
|
<input class="form-control"
|
||||||
[attr.autoComplete]="model.autoComplete"
|
[attr.autoComplete]="model.autoComplete"
|
||||||
[class.is-invalid]="showErrorMessages"
|
[class.is-invalid]="showErrorMessages"
|
||||||
@@ -41,19 +46,29 @@
|
|||||||
(click)="$event.stopPropagation(); sdRef.close();"
|
(click)="$event.stopPropagation(); sdRef.close();"
|
||||||
(input)="onInput($event)">
|
(input)="onInput($event)">
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!isInputDisabled()" class="col-auto text-center">
|
<div class="col-auto text-center">
|
||||||
<button ngbDropdownAnchor
|
<button ngbDropdownAnchor
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
type="button"
|
type="button"
|
||||||
[disabled]="model.readOnly || isSearchDisabled()"
|
[disabled]="model.readOnly || isSearchDisabled()"
|
||||||
|
[hidden]="isInputDisabled()"
|
||||||
(click)="sdRef.open(); search(); $event.stopPropagation();">{{'form.search' | translate}}
|
(click)="sdRef.open(); search(); $event.stopPropagation();">{{'form.search' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="isInputDisabled()" class="col-auto text-center">
|
<div *ngIf="isInputDisabled()" class="col-auto text-center">
|
||||||
<button class="btn btn-secondary"
|
<button class="btn btn-secondary"
|
||||||
type="button"
|
type="button"
|
||||||
[disabled]="model.readOnly"
|
ngbTooltip="{{'form.edit-help' | translate}}"
|
||||||
(click)="remove($event)">{{'form.remove' | translate}}
|
placement="top"
|
||||||
|
[disabled]="isEditDisabled()"
|
||||||
|
(click)="switchEditMode()">{{'form.edit' | translate}}
|
||||||
|
</button>
|
||||||
|
<button *ngIf="editMode" class="btn btn-secondary"
|
||||||
|
type="button"
|
||||||
|
ngbTooltip="{{'form.save-help' | translate}}"
|
||||||
|
placement="top"
|
||||||
|
[disabled]="!hasEmptyValue()"
|
||||||
|
(click)="saveChanges()">{{'form.save' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -19,11 +19,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* align fa-spin */
|
/* align fa-spin */
|
||||||
.left-addon .fa-spin {
|
.left-addon .fa {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-addon .fa-spin {
|
.right-addon .fa {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,8 @@ import { Subscription } from 'rxjs/Subscription';
|
|||||||
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
|
||||||
import { AuthorityValue } from '../../../../../../core/integration/models/authority.value';
|
import { AuthorityValue } from '../../../../../../core/integration/models/authority.value';
|
||||||
import { DynamicLookupNameModel } from './dynamic-lookup-name.model';
|
import { DynamicLookupNameModel } from './dynamic-lookup-name.model';
|
||||||
|
import { ConfidenceType } from '../../../../../../core/integration/models/confidence-type';
|
||||||
|
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-dynamic-lookup',
|
selector: 'ds-dynamic-lookup',
|
||||||
@@ -27,6 +29,7 @@ export class DsDynamicLookupComponent implements OnDestroy, OnInit {
|
|||||||
@Output() change: EventEmitter<any> = new EventEmitter<any>();
|
@Output() change: EventEmitter<any> = new EventEmitter<any>();
|
||||||
@Output() focus: EventEmitter<any> = new EventEmitter<any>();
|
@Output() focus: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
public editMode = false;
|
||||||
public firstInputValue = '';
|
public firstInputValue = '';
|
||||||
public secondInputValue = '';
|
public secondInputValue = '';
|
||||||
public loading = false;
|
public loading = false;
|
||||||
@@ -40,6 +43,10 @@ export class DsDynamicLookupComponent implements OnDestroy, OnInit {
|
|||||||
private cdr: ChangeDetectorRef) {
|
private cdr: ChangeDetectorRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputFormatter = (x: { display: string }, y: number) => {
|
||||||
|
return y === 1 ? this.firstInputValue : this.secondInputValue;
|
||||||
|
};
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.searchOptions = new IntegrationSearchOptions(
|
this.searchOptions = new IntegrationSearchOptions(
|
||||||
this.model.authorityOptions.scope,
|
this.model.authorityOptions.scope,
|
||||||
@@ -55,38 +62,33 @@ export class DsDynamicLookupComponent implements OnDestroy, OnInit {
|
|||||||
.subscribe((value) => {
|
.subscribe((value) => {
|
||||||
if (isEmpty(value)) {
|
if (isEmpty(value)) {
|
||||||
this.resetFields();
|
this.resetFields();
|
||||||
} else {
|
} else if (!this.editMode) {
|
||||||
this.setInputsValue(this.model.value);
|
this.setInputsValue(this.model.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public formatItemForInput(item: any, field: number): string {
|
protected getCurrentValue(): string {
|
||||||
if (isUndefined(item) || isNull(item)) {
|
let result = '';
|
||||||
return '';
|
if (!this.isLookupName()) {
|
||||||
}
|
result = this.firstInputValue;
|
||||||
return (typeof item === 'string') ? item : this.inputFormatter(item, field);
|
} else {
|
||||||
}
|
if (isNotEmpty(this.firstInputValue)) {
|
||||||
|
result = this.firstInputValue;
|
||||||
inputFormatter = (x: { display: string }, y: number) => {
|
}
|
||||||
return y === 1 ? this.firstInputValue : this.secondInputValue;
|
if (isNotEmpty(this.secondInputValue)) {
|
||||||
};
|
result = isEmpty(result)
|
||||||
|
? this.secondInputValue
|
||||||
onInput(event) {
|
: this.firstInputValue + (this.model as DynamicLookupNameModel).separator + ' ' + this.secondInputValue;
|
||||||
if (!this.model.authorityOptions.closed) {
|
|
||||||
if (isNotEmpty(this.getCurrentValue())) {
|
|
||||||
const currentValue = new FormFieldMetadataValueObject(this.getCurrentValue());
|
|
||||||
this.onSelect(currentValue);
|
|
||||||
} else {
|
|
||||||
this.remove();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
onScroll() {
|
protected resetFields() {
|
||||||
if (!this.loading && this.pageInfo.currentPage <= this.pageInfo.totalPages) {
|
this.firstInputValue = '';
|
||||||
this.searchOptions.currentPage++;
|
if (this.isLookupName()) {
|
||||||
this.search();
|
this.secondInputValue = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,24 +112,110 @@ export class DsDynamicLookupComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getCurrentValue(): string {
|
public formatItemForInput(item: any, field: number): string {
|
||||||
let result = '';
|
if (isUndefined(item) || isNull(item)) {
|
||||||
if (!this.isLookupName()) {
|
return '';
|
||||||
result = this.firstInputValue;
|
|
||||||
} else {
|
|
||||||
if (isNotEmpty(this.firstInputValue)) {
|
|
||||||
result = this.firstInputValue;
|
|
||||||
}
|
|
||||||
if (isNotEmpty(this.secondInputValue)) {
|
|
||||||
result = isEmpty(result)
|
|
||||||
? this.secondInputValue
|
|
||||||
: this.firstInputValue + (this.model as DynamicLookupNameModel).separator + ' ' + this.secondInputValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return (typeof item === 'string') ? item : this.inputFormatter(item, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
search() {
|
public hasAuthorityValue() {
|
||||||
|
return hasValue(this.model.value)
|
||||||
|
&& this.model.value.hasAuthority();
|
||||||
|
}
|
||||||
|
|
||||||
|
public hasEmptyValue() {
|
||||||
|
return isNotEmpty(this.getCurrentValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearFields() {
|
||||||
|
// Clear inputs whether there is no results and authority is closed
|
||||||
|
if (this.model.authorityOptions.closed) {
|
||||||
|
this.resetFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEditDisabled() {
|
||||||
|
return !this.hasAuthorityValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public isInputDisabled() {
|
||||||
|
return (this.model.authorityOptions.closed && this.hasAuthorityValue() && !this.editMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isLookupName() {
|
||||||
|
return (this.model instanceof DynamicLookupNameModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSearchDisabled() {
|
||||||
|
return isEmpty(this.firstInputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onBlurEvent(event: Event) {
|
||||||
|
this.blur.emit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onFocusEvent(event) {
|
||||||
|
this.focus.emit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onInput(event) {
|
||||||
|
if (!this.model.authorityOptions.closed) {
|
||||||
|
if (isNotEmpty(this.getCurrentValue())) {
|
||||||
|
const currentValue = new FormFieldMetadataValueObject(this.getCurrentValue());
|
||||||
|
if (!this.editMode) {
|
||||||
|
this.onSelect(currentValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onScroll() {
|
||||||
|
if (!this.loading && this.pageInfo.currentPage <= this.pageInfo.totalPages) {
|
||||||
|
this.searchOptions.currentPage++;
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSelect(event) {
|
||||||
|
this.group.markAsDirty();
|
||||||
|
this.model.valueUpdates.next(event);
|
||||||
|
this.setInputsValue(event);
|
||||||
|
this.change.emit(event);
|
||||||
|
this.optionsList = null;
|
||||||
|
this.pageInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public openChange(isOpened: boolean) {
|
||||||
|
if (!isOpened) {
|
||||||
|
if (this.model.authorityOptions.closed && !this.hasAuthorityValue()) {
|
||||||
|
this.setInputsValue('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove() {
|
||||||
|
this.group.markAsPristine();
|
||||||
|
this.model.valueUpdates.next(null);
|
||||||
|
this.change.emit(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveChanges() {
|
||||||
|
if (isNotEmpty(this.getCurrentValue())) {
|
||||||
|
const newValue = Object.assign(new AuthorityValue(), this.model.value, {
|
||||||
|
display: this.getCurrentValue(),
|
||||||
|
value: this.getCurrentValue()
|
||||||
|
});
|
||||||
|
this.onSelect(newValue);
|
||||||
|
} else {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
this.switchEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public search() {
|
||||||
this.optionsList = null;
|
this.optionsList = null;
|
||||||
this.pageInfo = null;
|
this.pageInfo = null;
|
||||||
|
|
||||||
@@ -145,68 +233,17 @@ export class DsDynamicLookupComponent implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clearFields() {
|
public switchEditMode() {
|
||||||
// Clear inputs whether there is no results and authority is closed
|
this.editMode = !this.editMode;
|
||||||
if (this.model.authorityOptions.closed) {
|
}
|
||||||
this.resetFields();
|
|
||||||
|
public whenClickOnConfidenceNotAccepted(sdRef: NgbDropdown, confidence: ConfidenceType) {
|
||||||
|
if (!this.model.readOnly) {
|
||||||
|
sdRef.open();
|
||||||
|
this.search();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected resetFields() {
|
|
||||||
this.firstInputValue = '';
|
|
||||||
if (this.isLookupName()) {
|
|
||||||
this.secondInputValue = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelect(event) {
|
|
||||||
this.group.markAsDirty();
|
|
||||||
this.model.valueUpdates.next(event);
|
|
||||||
this.setInputsValue(event);
|
|
||||||
this.change.emit(event);
|
|
||||||
this.optionsList = null;
|
|
||||||
this.pageInfo = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
isInputDisabled() {
|
|
||||||
return this.model.authorityOptions.closed && hasValue(this.model.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
isLookupName() {
|
|
||||||
return (this.model instanceof DynamicLookupNameModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
isSearchDisabled() {
|
|
||||||
// if (this.firstInputValue === ''
|
|
||||||
// && (this.isLookupName ? this.secondInputValue === '' : true)) {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
return isEmpty(this.firstInputValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove() {
|
|
||||||
this.group.markAsPristine();
|
|
||||||
this.model.valueUpdates.next(null);
|
|
||||||
this.change.emit(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
openChange(isOpened: boolean) {
|
|
||||||
if (!isOpened) {
|
|
||||||
if (this.model.authorityOptions.closed) {
|
|
||||||
this.setInputsValue('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onBlurEvent(event: Event) {
|
|
||||||
this.blur.emit(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onFocusEvent(event) {
|
|
||||||
this.focus.emit(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
if (hasValue(this.sub)) {
|
if (hasValue(this.sub)) {
|
||||||
this.sub.unsubscribe();
|
this.sub.unsubscribe();
|
||||||
|
@@ -5,9 +5,10 @@
|
|||||||
|
|
||||||
<ds-chips [chips]="chips"
|
<ds-chips [chips]="chips"
|
||||||
[editable]="false"
|
[editable]="false"
|
||||||
|
[showIcons]="model.hasAuthority"
|
||||||
[wrapperClass]="'border-bottom border-light'">
|
[wrapperClass]="'border-bottom border-light'">
|
||||||
|
|
||||||
<input *ngIf="!searchOptions"
|
<input *ngIf="!model.hasAuthority"
|
||||||
class="border-0 form-control-plaintext tag-input flex-grow-1 mt-1 mb-1 chips-sort-ignore"
|
class="border-0 form-control-plaintext tag-input flex-grow-1 mt-1 mb-1 chips-sort-ignore"
|
||||||
type="text"
|
type="text"
|
||||||
[class.pl-3]="chips.hasItems()"
|
[class.pl-3]="chips.hasItems()"
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
(keyup)="onKeyUp($event)" />
|
(keyup)="onKeyUp($event)" />
|
||||||
|
|
||||||
|
|
||||||
<input *ngIf="searchOptions"
|
<input *ngIf="model.hasAuthority"
|
||||||
class="border-0 form-control-plaintext tag-input flex-grow-1 mt-1 mb-1 chips-sort-ignore"
|
class="border-0 form-control-plaintext tag-input flex-grow-1 mt-1 mb-1 chips-sort-ignore"
|
||||||
type="text"
|
type="text"
|
||||||
[(ngModel)]="currentValue"
|
[(ngModel)]="currentValue"
|
||||||
@@ -42,7 +43,8 @@
|
|||||||
(selectItem)="onSelectItem($event)"
|
(selectItem)="onSelectItem($event)"
|
||||||
(keypress)="preventEventsPropagation($event)"
|
(keypress)="preventEventsPropagation($event)"
|
||||||
(keydown)="preventEventsPropagation($event)"
|
(keydown)="preventEventsPropagation($event)"
|
||||||
(keyup)="onKeyUp($event)"/>
|
(keyup)="onKeyUp($event)"
|
||||||
|
#instance="ngbTypeahead"/>
|
||||||
<i *ngIf="searching" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw text-primary position-absolute mt-1 p-0" aria-hidden="true"></i>
|
<i *ngIf="searching" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw text-primary position-absolute mt-1 p-0" aria-hidden="true"></i>
|
||||||
</ds-chips>
|
</ds-chips>
|
||||||
|
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
import { ChangeDetectorRef, Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
|
import { ChangeDetectorRef, Component, EventEmitter, Inject, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbTypeahead, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
||||||
import { DynamicTagModel } from './dynamic-tag.model';
|
import { DynamicTagModel } from './dynamic-tag.model';
|
||||||
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
||||||
import { Chips } from '../../../../../chips/models/chips.model';
|
import { Chips } from '../../../../../chips/models/chips.model';
|
||||||
import { hasValue, isNotEmpty } from '../../../../../empty.util';
|
import { hasValue, isNotEmpty } from '../../../../../empty.util';
|
||||||
import { isEqual } from 'lodash';
|
|
||||||
import { GlobalConfig } from '../../../../../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../../../../../config/global-config.interface';
|
||||||
import { GLOBAL_CONFIG } from '../../../../../../../config';
|
import { GLOBAL_CONFIG } from '../../../../../../../config';
|
||||||
|
|
||||||
@@ -28,6 +28,8 @@ export class DsDynamicTagComponent implements OnInit {
|
|||||||
@Output() change: EventEmitter<any> = new EventEmitter<any>();
|
@Output() change: EventEmitter<any> = new EventEmitter<any>();
|
||||||
@Output() focus: EventEmitter<any> = new EventEmitter<any>();
|
@Output() focus: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
@ViewChild('instance') instance: NgbTypeahead;
|
||||||
|
|
||||||
chips: Chips;
|
chips: Chips;
|
||||||
hasAuthority: boolean;
|
hasAuthority: boolean;
|
||||||
|
|
||||||
@@ -82,7 +84,11 @@ export class DsDynamicTagComponent implements OnInit {
|
|||||||
this.model.authorityOptions.metadata);
|
this.model.authorityOptions.metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chips = new Chips(this.model.value, 'display');
|
this.chips = new Chips(
|
||||||
|
this.model.value,
|
||||||
|
'display',
|
||||||
|
null,
|
||||||
|
this.EnvConfig.submission.icons.metadata);
|
||||||
|
|
||||||
this.chips.chipsItems
|
this.chips.chipsItems
|
||||||
.subscribe((subItems: any[]) => {
|
.subscribe((subItems: any[]) => {
|
||||||
@@ -108,7 +114,7 @@ export class DsDynamicTagComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onBlur(event: Event) {
|
onBlur(event: Event) {
|
||||||
if (isNotEmpty(this.currentValue)) {
|
if (isNotEmpty(this.currentValue) && !this.instance.isPopupOpen()) {
|
||||||
this.addTagsToChips();
|
this.addTagsToChips();
|
||||||
}
|
}
|
||||||
this.blur.emit(event);
|
this.blur.emit(event);
|
||||||
|
@@ -22,6 +22,12 @@
|
|||||||
|
|
||||||
<div class="position-relative right-addon">
|
<div class="position-relative right-addon">
|
||||||
<i *ngIf="searching" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw text-primary position-absolute mt-1 p-0" aria-hidden="true"></i>
|
<i *ngIf="searching" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw text-primary position-absolute mt-1 p-0" aria-hidden="true"></i>
|
||||||
|
<i *ngIf="!searching"
|
||||||
|
dsAuthorityConfidenceState
|
||||||
|
class="fa fa-circle-o fa-2x fa-fw position-absolute mt-1 p-0"
|
||||||
|
aria-hidden="true"
|
||||||
|
[authorityValue]="currentValue"
|
||||||
|
(whenClickOnConfidenceNotAccepted)="whenClickOnConfidenceNotAccepted($event)"></i>
|
||||||
<input class="form-control"
|
<input class="form-control"
|
||||||
[attr.autoComplete]="model.autoComplete"
|
[attr.autoComplete]="model.autoComplete"
|
||||||
[class.is-invalid]="showErrorMessages"
|
[class.is-invalid]="showErrorMessages"
|
||||||
|
@@ -6,8 +6,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* align fa-spin */
|
/* align fa-spin */
|
||||||
.left-addon .fa-spin { left: 0;}
|
.left-addon .fa {
|
||||||
.right-addon .fa-spin { right: 0;}
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-addon .fa {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
:host /deep/ .dropdown-menu {
|
:host /deep/ .dropdown-menu {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
|
@@ -2,6 +2,8 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } fro
|
|||||||
import { FormGroup } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { debounceTime, distinctUntilChanged, map, merge, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { Subject } from 'rxjs/Subject';
|
||||||
import { NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
import { AuthorityService } from '../../../../../../core/integration/authority.service';
|
||||||
@@ -9,6 +11,7 @@ import { DynamicTypeaheadModel } from './dynamic-typeahead.model';
|
|||||||
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
import { IntegrationSearchOptions } from '../../../../../../core/integration/models/integration-options.model';
|
||||||
import { isEmpty, isNotEmpty } from '../../../../../empty.util';
|
import { isEmpty, isNotEmpty } from '../../../../../empty.util';
|
||||||
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
|
import { FormFieldMetadataValueObject } from '../../../models/form-field-metadata-value.model';
|
||||||
|
import { ConfidenceType } from '../../../../../../core/integration/models/confidence-type';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-dynamic-typeahead',
|
selector: 'ds-dynamic-typeahead',
|
||||||
@@ -28,7 +31,8 @@ export class DsDynamicTypeaheadComponent implements OnInit {
|
|||||||
searching = false;
|
searching = false;
|
||||||
searchOptions: IntegrationSearchOptions;
|
searchOptions: IntegrationSearchOptions;
|
||||||
searchFailed = false;
|
searchFailed = false;
|
||||||
hideSearchingWhenUnsubscribed = new Observable(() => () => this.changeSearchingStatus(false));
|
hideSearchingWhenUnsubscribed$ = new Observable(() => () => this.changeSearchingStatus(false));
|
||||||
|
click$ = new Subject<string>();
|
||||||
currentValue: any;
|
currentValue: any;
|
||||||
inputValue: any;
|
inputValue: any;
|
||||||
|
|
||||||
@@ -36,12 +40,13 @@ export class DsDynamicTypeaheadComponent implements OnInit {
|
|||||||
return (typeof x === 'object') ? x.display : x
|
return (typeof x === 'object') ? x.display : x
|
||||||
};
|
};
|
||||||
|
|
||||||
search = (text$: Observable<string>) =>
|
search = (text$: Observable<string>) => {
|
||||||
text$
|
return text$.pipe(
|
||||||
.debounceTime(300)
|
merge(this.click$),
|
||||||
.distinctUntilChanged()
|
debounceTime(200),
|
||||||
.do(() => this.changeSearchingStatus(true))
|
distinctUntilChanged(),
|
||||||
.switchMap((term) => {
|
tap(() => this.changeSearchingStatus(true)),
|
||||||
|
switchMap((term) => {
|
||||||
if (term === '' || term.length < this.model.minChars) {
|
if (term === '' || term.length < this.model.minChars) {
|
||||||
return Observable.of({list: []});
|
return Observable.of({list: []});
|
||||||
} else {
|
} else {
|
||||||
@@ -60,10 +65,12 @@ export class DsDynamicTypeaheadComponent implements OnInit {
|
|||||||
return Observable.of({list: []});
|
return Observable.of({list: []});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
.map((results) => results.list)
|
map((results) => results.list),
|
||||||
.do(() => this.changeSearchingStatus(false))
|
tap(() => this.changeSearchingStatus(false)),
|
||||||
.merge(this.hideSearchingWhenUnsubscribed);
|
merge(this.hideSearchingWhenUnsubscribed$)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
constructor(private authorityService: AuthorityService, private cdr: ChangeDetectorRef) {
|
constructor(private authorityService: AuthorityService, private cdr: ChangeDetectorRef) {
|
||||||
}
|
}
|
||||||
@@ -88,8 +95,7 @@ export class DsDynamicTypeaheadComponent implements OnInit {
|
|||||||
|
|
||||||
onInput(event) {
|
onInput(event) {
|
||||||
if (!this.model.authorityOptions.closed && isNotEmpty(event.target.value)) {
|
if (!this.model.authorityOptions.closed && isNotEmpty(event.target.value)) {
|
||||||
const valueObj = new FormFieldMetadataValueObject(event.target.value);
|
this.inputValue = new FormFieldMetadataValueObject(event.target.value);
|
||||||
this.inputValue = valueObj;
|
|
||||||
this.model.valueUpdates.next(this.inputValue);
|
this.model.valueUpdates.next(this.inputValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,4 +126,10 @@ export class DsDynamicTypeaheadComponent implements OnInit {
|
|||||||
this.model.valueUpdates.next(event.item);
|
this.model.valueUpdates.next(event.item);
|
||||||
this.change.emit(event.item);
|
this.change.emit(event.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public whenClickOnConfidenceNotAccepted(confidence: ConfidenceType) {
|
||||||
|
if (!this.model.readOnly) {
|
||||||
|
this.click$.next(this.formatter(this.currentValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { isNotEmpty, isNotNull } from '../../../empty.util';
|
import { isEmpty, isNotEmpty, isNotNull } from '../../../empty.util';
|
||||||
|
import { ConfidenceType } from '../../../../core/integration/models/confidence-type';
|
||||||
|
|
||||||
export class FormFieldMetadataValueObject {
|
export class FormFieldMetadataValueObject {
|
||||||
metadata?: string;
|
metadata?: string;
|
||||||
@@ -6,7 +7,7 @@ export class FormFieldMetadataValueObject {
|
|||||||
display: string;
|
display: string;
|
||||||
language: any;
|
language: any;
|
||||||
authority: string;
|
authority: string;
|
||||||
confidence: number;
|
confidence: ConfidenceType;
|
||||||
place: number;
|
place: number;
|
||||||
closed: boolean;
|
closed: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -17,7 +18,7 @@ export class FormFieldMetadataValueObject {
|
|||||||
authority: string = null,
|
authority: string = null,
|
||||||
display: string = null,
|
display: string = null,
|
||||||
place: number = 0,
|
place: number = 0,
|
||||||
confidence: number = -1,
|
confidence: number = null,
|
||||||
otherInformation: any = null,
|
otherInformation: any = null,
|
||||||
metadata: string = null) {
|
metadata: string = null) {
|
||||||
this.value = isNotNull(value) ? ((typeof value === 'string') ? value.trim() : value) : null;
|
this.value = isNotNull(value) ? ((typeof value === 'string') ? value.trim() : value) : null;
|
||||||
@@ -26,10 +27,12 @@ export class FormFieldMetadataValueObject {
|
|||||||
this.display = display || value;
|
this.display = display || value;
|
||||||
|
|
||||||
this.confidence = confidence;
|
this.confidence = confidence;
|
||||||
if (authority != null) {
|
if (authority != null && isEmpty(confidence)) {
|
||||||
this.confidence = 600;
|
this.confidence = ConfidenceType.CF_ACCEPTED;
|
||||||
} else if (isNotEmpty(confidence)) {
|
} else if (isNotEmpty(confidence)) {
|
||||||
this.confidence = confidence;
|
this.confidence = confidence;
|
||||||
|
} else {
|
||||||
|
this.confidence = ConfidenceType.CF_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.place = place;
|
this.place = place;
|
||||||
|
@@ -83,6 +83,7 @@ import { InputSuggestionsComponent } from './input-suggestions/input-suggestions
|
|||||||
import { CapitalizePipe } from './utils/capitalize.pipe';
|
import { CapitalizePipe } from './utils/capitalize.pipe';
|
||||||
import { MomentModule } from 'angular2-moment';
|
import { MomentModule } from 'angular2-moment';
|
||||||
import { ObjectKeysPipe } from './utils/object-keys-pipe';
|
import { ObjectKeysPipe } from './utils/object-keys-pipe';
|
||||||
|
import { AuthorityConfidenceStateDirective } from './authority-confidence/authority-confidence-state.directive';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||||
@@ -183,7 +184,8 @@ const DIRECTIVES = [
|
|||||||
VarDirective,
|
VarDirective,
|
||||||
DragClickDirective,
|
DragClickDirective,
|
||||||
DebounceDirective,
|
DebounceDirective,
|
||||||
ClickOutsideDirective
|
ClickOutsideDirective,
|
||||||
|
AuthorityConfidenceStateDirective
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -1,16 +1,28 @@
|
|||||||
import { Config } from './config.interface';
|
import { Config } from './config.interface';
|
||||||
import { MetadataIconsConfig } from '../app/shared/chips/models/chips.model';
|
|
||||||
|
|
||||||
interface AutosaveConfig extends Config {
|
interface AutosaveConfig extends Config {
|
||||||
metadata: string[],
|
metadata: string[],
|
||||||
timer: number
|
timer: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MetadataConfig extends Config {
|
interface IconsConfig extends Config {
|
||||||
icons: MetadataIconsConfig[]
|
metadata: MetadataIconConfig[],
|
||||||
|
authority: {
|
||||||
|
confidence: ConfidenceIconConfig[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MetadataIconConfig extends Config {
|
||||||
|
name: string,
|
||||||
|
style: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfidenceIconConfig extends Config {
|
||||||
|
value: any,
|
||||||
|
style: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SubmissionConfig extends Config {
|
export interface SubmissionConfig extends Config {
|
||||||
autosave: AutosaveConfig,
|
autosave: AutosaveConfig,
|
||||||
metadata: MetadataConfig
|
icons: IconsConfig
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user