e1f988ff9f6072a34e73c617c7c34451b9e2eb43
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / share / grid / grid.component.ts
1 import {Component, Input, Output, OnInit, AfterViewInit, EventEmitter,
2     OnDestroy, HostListener, ViewEncapsulation} from '@angular/core';
3 import {Subscription} from 'rxjs';
4 import {IdlService} from '@eg/core/idl.service';
5 import {OrgService} from '@eg/core/org.service';
6 import {ServerStoreService} from '@eg/core/server-store.service';
7 import {FormatService} from '@eg/core/format.service';
8 import {GridContext, GridColumn, GridDataSource, GridRowFlairEntry} from './grid';
9
10 /**
11  * Main grid entry point.
12  */
13
14 @Component({
15   selector: 'eg-grid',
16   templateUrl: './grid.component.html',
17   styleUrls: ['grid.component.css'],
18   // share grid css globally once imported so all grid component CSS
19   // can live in grid.component.css and to avoid multiple copies of
20   // the CSS when multiple grids are displayed.
21   encapsulation: ViewEncapsulation.None
22 })
23
24 export class GridComponent implements OnInit, AfterViewInit, OnDestroy {
25
26     // Source of row data.
27     @Input() dataSource: GridDataSource;
28
29     // IDL class for auto-generation of columns
30     @Input() idlClass: string;
31
32     // True if any columns are sortable
33     @Input() sortable: boolean;
34
35     // True if the grid supports sorting of multiple columns at once
36     @Input() multiSortable: boolean;
37
38     // If true, grid sort requests only operate on data that
39     // already exists in the grid data source -- no row fetching.
40     // The assumption is all data is already available.
41     @Input() useLocalSort: boolean;
42
43     // Storage persist key / per-grid-type unique identifier
44     // The value is prefixed with 'eg.grid.'
45     @Input() persistKey: string;
46
47     @Input() disableSelect: boolean;
48
49     // Prevent selection of multiple rows
50     @Input() disableMultiSelect: boolean;
51
52     // Show an extra column in the grid where the caller can apply
53     // row-specific flair (material icons).
54     @Input() rowFlairIsEnabled: boolean;
55
56     // Returns a material icon name to display in the flar column
57     // (if enabled) for the given row.
58     @Input() rowFlairCallback: (row: any) => GridRowFlairEntry;
59
60     // Returns a space-separated list of CSS class names to apply to
61     // a given row
62     @Input() rowClassCallback: (row: any) => string;
63
64     // Returns a space-separated list of CSS class names to apply to
65     // a given cell or all cells in a column.
66     @Input() cellClassCallback: (row: any, col: GridColumn) => string;
67
68     // comma-separated list of fields to show by default.
69     // This field takes precedence over hideFields.
70     // When a value is applied, any field not in this list will
71     // be hidden.
72     @Input() showFields: string;
73
74     // comma-separated list of fields to hide.
75     // This does not imply all other fields should be visible, only that
76     // the selected fields will be hidden.
77     @Input() hideFields: string;
78
79     // Allow the caller to jump directly to a specific page of
80     // grid data.
81     @Input() pageOffset: number;
82     // Pass in a default page size.  May be overridden by settings.
83     @Input() pageSize: number;
84
85     // If true and an idlClass is specificed, the grid assumes
86     // datatype=link fields that link to classes which define a selector
87     // are fleshed with the linked object.  And, instead of displaying
88     // the raw field value, displays the selector value from the linked
89     // object.  The caller is responsible for fleshing the appropriate
90     // fields in the GridDataSource getRows handler.
91     //
92     // This only applies to auto-generated columns.
93     //
94     // For example, idlClass="aou" and field="ou_type", the display
95     // value will be ou_type().name() since "name" is the selector
96     // field on the "aout" class.
97     @Input() showLinkSelectors: boolean;
98
99     @Input() disablePaging: boolean;
100
101     context: GridContext;
102
103     // These events are emitted from our grid-body component.
104     // They are defined here for ease of access to the caller.
105     @Output() onRowActivate: EventEmitter<any>;
106     @Output() onRowClick: EventEmitter<any>;
107
108     constructor(
109         private idl: IdlService,
110         private org: OrgService,
111         private store: ServerStoreService,
112         private format: FormatService
113     ) {
114         this.context =
115             new GridContext(this.idl, this.org, this.store, this.format);
116         this.onRowActivate = new EventEmitter<any>();
117         this.onRowClick = new EventEmitter<any>();
118     }
119
120     ngOnInit() {
121
122         if (!this.dataSource) {
123             throw new Error('<eg-grid/> requires a [dataSource]');
124         }
125
126         this.context.idlClass = this.idlClass;
127         this.context.dataSource = this.dataSource;
128         this.context.persistKey = this.persistKey;
129         this.context.isSortable = this.sortable === true;
130         this.context.isMultiSortable = this.multiSortable === true;
131         this.context.useLocalSort = this.useLocalSort === true;
132         this.context.disableSelect = this.disableSelect === true;
133         this.context.showLinkSelectors = this.showLinkSelectors === true;
134         this.context.disableMultiSelect = this.disableMultiSelect === true;
135         this.context.rowFlairIsEnabled = this.rowFlairIsEnabled  === true;
136         this.context.rowFlairCallback = this.rowFlairCallback;
137         this.context.disablePaging = this.disablePaging === true;
138         if (this.showFields) {
139             this.context.defaultVisibleFields = this.showFields.split(',');
140         }
141         if (this.hideFields) {
142             this.context.defaultHiddenFields = this.hideFields.split(',');
143         }
144
145         if (this.pageOffset) {
146             this.context.pager.offset = this.pageOffset;
147         }
148
149         if (this.pageSize) {
150             this.context.pager.limit = this.pageSize;
151         }
152
153         // TS doesn't seem to like: let foo = bar || () => '';
154         this.context.rowClassCallback =
155             this.rowClassCallback || function () { return ''; };
156         this.context.cellClassCallback =
157             this.cellClassCallback || function() { return ''; };
158
159         this.context.init();
160     }
161
162     ngAfterViewInit() {
163         this.context.initData();
164     }
165
166     ngOnDestroy() {
167         this.context.destroy();
168     }
169
170     reload() {
171         this.context.reload();
172     }
173 }
174
175
176