1 import {Component, OnInit, Input, Output, ViewChild, TemplateRef,
2 EventEmitter} from '@angular/core';
3 import {StringComponent} from '@eg/share/string/string.component';
6 * Displays attribute values and associated copy counts for managing
7 * updates to batches of items.
11 // Map of display value to boolean indicating whether a given item
12 // should be modified.
13 export interface BatchChangeSelection {
14 [value: string]: boolean;
18 selector: 'eg-batch-item-attr',
19 templateUrl: 'batch-item-attr.component.html',
21 `.header { background-color: #d9edf7; }`,
22 `.has-changes { background-color: #dff0d8; }`
26 export class BatchItemAttrComponent {
28 // Main display label, e.g. "Circulation Modifier"
29 @Input() label: string;
31 // Optional. Useful for exracting information (i.e. hasChanges)
32 // on a specific field from a set of batch attr components.
33 @Input() name: string;
35 // Maps display labels to the number of items that have the label.
36 // e.g. {"Stacks": 4, "Display": 12}
37 @Input() labelCounts: {[label: string]: number} = {};
39 // Ref to some type of edit widget for modifying the value.
40 // Note this component simply displays the template, it does not
41 // interact with the template in any way.
42 @Input() editTemplate: TemplateRef<any>;
44 @Input() editInputDomId = '';
46 // In some cases, we can map display labels to something more
48 @Input() displayAs: 'bool' | 'currency' = null;
51 @Input() readOnly = false;
53 // Warn the user when a required field has an empty value
54 @Input() valueRequired = false;
56 // If true, a value of '' is considered unset for display and
57 // valueRequired purposes.
58 @Input() emptyStringIsUnset = true;
60 // Lists larger than this will be partially hidden behind
62 @Input() defaultDisplayCount = 7;
64 @Output() changesSaved: EventEmitter<BatchChangeSelection> =
65 new EventEmitter<BatchChangeSelection>();
67 @Output() changesCanceled: EventEmitter<void> = new EventEmitter<void>();
68 @Output() valueCleared: EventEmitter<void> = new EventEmitter<void>();
70 // Is the editTtemplate visible?
75 // Showing all entries?
78 // Indicate which display values the user wants to modify.
79 editValues: BatchChangeSelection = {};
84 this.hasChanged = true;
86 this.changesSaved.emit(this.editValues);
91 this.changesCanceled.emit();
95 this.hasChanged = true;
97 this.valueCleared.emit();
101 return Object.keys(this.labelCounts).length > this.defaultDisplayCount;
104 multiValue(): boolean {
105 return Object.keys(this.labelCounts).length > 1;
108 // True if a value is required and any value exists that's unset.
109 warnOnRequired(): boolean {
110 if (!this.valueRequired) { return false; }
112 return Object.keys(this.labelCounts)
113 .filter(key => this.valueIsUnset(key)).length > 0;
116 valueIsUnset(value: any): boolean {
119 value === undefined ||
120 (this.emptyStringIsUnset && value === '')
125 if (this.readOnly || this.editing) { return; }
128 // Assume all values should be edited by default
129 Object.keys(this.labelCounts).forEach(
130 key => this.editValues[key] = true);
132 if (this.editInputDomId) {
134 // Avoid using selectRootElement to focus.
135 // https://stackoverflow.com/a/36059595
136 const node = document.getElementById(this.editInputDomId);
137 if (node) { node.focus(); }