]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/share/holdings/batch-item-attr.component.ts
LP1888723 Angular Holdings Maintenance / Item Attributes Editor
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / share / holdings / batch-item-attr.component.ts
1 import {Component, OnInit, Input, Output, ViewChild, TemplateRef,
2     EventEmitter} from '@angular/core';
3 import {StringComponent} from '@eg/share/string/string.component';
4
5 /**
6  * Displays attribute values and associated copy counts for managing
7  * updates to batches of items.
8  */
9
10
11 // Map of display value to boolean indicating whether a given item
12 // should be modified.
13 export interface BatchChangeSelection {
14     [value: string]: boolean;
15 }
16
17 @Component({
18   selector: 'eg-batch-item-attr',
19   templateUrl: 'batch-item-attr.component.html',
20   styles: [
21     `.header { background-color: #d9edf7; }`,
22     `.has-changes { background-color: #dff0d8; }`
23   ]
24 })
25
26 export class BatchItemAttrComponent {
27
28     // Main display label, e.g. "Circulation Modifier"
29     @Input() label: string;
30
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;
34
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} = {};
38
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>;
43
44     @Input() editInputDomId = '';
45
46     // In some cases, we can map display labels to something more
47     // human friendly.
48     @Input() displayAs: 'bool' | 'currency' = null;
49
50     // Display only
51     @Input() readOnly = false;
52
53     // Warn the user when a required field has an empty value
54     @Input() valueRequired = false;
55
56     // If true, a value of '' is considered unset for display and
57     // valueRequired purposes.
58     @Input() emptyStringIsUnset = true;
59
60     // Lists larger than this will be partially hidden behind
61     // and expandy.
62     @Input() defaultDisplayCount = 7;
63
64     @Output() changesSaved: EventEmitter<BatchChangeSelection> =
65         new EventEmitter<BatchChangeSelection>();
66
67     @Output() changesCanceled: EventEmitter<void> = new EventEmitter<void>();
68     @Output() valueCleared: EventEmitter<void> = new EventEmitter<void>();
69
70     // Is the editTtemplate visible?
71     editing = false;
72
73     hasChanged = false;
74
75     // Showing all entries?
76     expanded = false;
77
78     // Indicate which display values the user wants to modify.
79     editValues: BatchChangeSelection = {};
80
81     constructor() {}
82
83     save() {
84         this.hasChanged = true;
85         this.editing = false;
86         this.changesSaved.emit(this.editValues);
87     }
88
89     cancel() {
90         this.editing = false;
91         this.changesCanceled.emit();
92     }
93
94     clear() {
95         this.hasChanged = true;
96         this.editing = false;
97         this.valueCleared.emit();
98     }
99
100     bulky(): boolean {
101         return Object.keys(this.labelCounts).length > this.defaultDisplayCount;
102     }
103
104     multiValue(): boolean {
105         return Object.keys(this.labelCounts).length > 1;
106     }
107
108     // True if a value is required and any value exists that's unset.
109     warnOnRequired(): boolean {
110         if (!this.valueRequired) { return false; }
111
112         return Object.keys(this.labelCounts)
113             .filter(key => this.valueIsUnset(key)).length > 0;
114     }
115
116     valueIsUnset(value: any): boolean {
117         return (
118             value === null ||
119             value === undefined ||
120             (this.emptyStringIsUnset && value === '')
121         );
122     }
123
124     enterEditMode() {
125         if (this.readOnly || this.editing) { return; }
126         this.editing = true;
127
128         // Assume all values should be edited by default
129         Object.keys(this.labelCounts).forEach(
130             key => this.editValues[key] = true);
131
132         if (this.editInputDomId) {
133             setTimeout(() => {
134                 // Avoid using selectRootElement to focus.
135                 // https://stackoverflow.com/a/36059595
136                 const node = document.getElementById(this.editInputDomId);
137                 if (node) { node.focus(); }
138             });
139         }
140     }
141 }
142
143
144