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