blob: 5db05b69e9298c363249a3ab6d873b224fc059bb [file] [log] [blame]
Yoshie Muranakafa562732019-07-17 11:23:15 -05001window.angular && (function(angular) {
2 'use strict';
3
4 /**
5 *
Yoshie Muranakabb688792019-08-12 09:31:52 -05006 * bmcTable Component
Yoshie Muranakafa562732019-07-17 11:23:15 -05007 *
8 * To use:
Yoshie Muranakafa562732019-07-17 11:23:15 -05009 *
Yoshie Muranakab1f64242019-09-04 11:40:51 -070010 * The 'data' attribute should be an array of all row objects in the table.
11 * It will render each item as a <tr> in the table.
12 * Each row object in the data array should also have a 'uiData'
Yoshie Muranakafa562732019-07-17 11:23:15 -050013 * property that should be an array of the properties that will render
14 * as each table cell <td>.
Yoshie Muranakab1f64242019-09-04 11:40:51 -070015 * Each row object in the data array can optionally have an
Yoshie Muranakabb688792019-08-12 09:31:52 -050016 * 'actions' property that should be an array of actions to provide the
17 * <bmc-table-actions> component.
Yoshie Muranaka1d83af02019-09-06 08:52:35 -070018 * Each row object can optionally have an 'expandContent' property
19 * that should be a string value and can contain valid HTML. To render
20 * the expanded content, set 'expandable' attribute to true.
Yoshie Muranakafa562732019-07-17 11:23:15 -050021 *
Yoshie Muranakab1f64242019-09-04 11:40:51 -070022 * data = [
23 * { uiData: ['root', 'Admin', 'enabled' ] },
24 * { uiData: ['user1', 'User', 'disabled' ] }
25 * ]
26 *
27 * The 'header' attribute should be an array of all header objects in the
28 * table. Each object in the header array should have a 'label' property
29 * that will render as a <th> in the table.
30 * If the table is sortable, can optionally add 'sortable' property to header
31 * row object. If a particular column is not sortable, set to false.
32 *
33 * header = [
34 * { label: 'Username' },
35 * { label: 'Privilege' }
36 * { label: 'Account Status', sortable: false }
37 * ]
38 *
39 * The 'sortable' attribute should be a boolean value. Defaults to false.
40 * The 'default-sort' attribute should be the index value of the header
41 * obejct that should be sorted on inital load.
42 *
43 * The 'row-actions-enabled' attribute, should be a boolean value
44 * Can be set to true to render table row actions. Defaults to false.
Yoshie Muranaka1d83af02019-09-06 08:52:35 -070045 * Row actions are defined in data.actions.
46 *
47 * The 'expandable' attribute should be a boolean value. If true each
48 * row object in data array should contain a 'expandContent' property
Yoshie Muranakab1f64242019-09-04 11:40:51 -070049 *
50 * The 'size' attribute which can be set to 'small' which will
51 * render a smaller font size in the table.
Yoshie Muranakafa562732019-07-17 11:23:15 -050052 *
53 */
Yoshie Muranakabb688792019-08-12 09:31:52 -050054
Yoshie Muranakafa562732019-07-17 11:23:15 -050055 const TableController = function() {
Yoshie Muranakab1f64242019-09-04 11:40:51 -070056 this.sortAscending = true;
57 this.activeSort;
Yoshie Muranaka1d83af02019-09-06 08:52:35 -070058 this.expandedRows = new Set();
Yoshie Muranakab1f64242019-09-04 11:40:51 -070059
Yoshie Muranakafa562732019-07-17 11:23:15 -050060 /**
Yoshie Muranakab1f64242019-09-04 11:40:51 -070061 * Sorts table data
Yoshie Muranakafa562732019-07-17 11:23:15 -050062 */
Yoshie Muranakab1f64242019-09-04 11:40:51 -070063 const sortData = () => {
64 this.data.sort((a, b) => {
65 const aProp = a.uiData[this.activeSort];
66 const bProp = b.uiData[this.activeSort];
67 if (aProp === bProp) {
68 return 0;
69 } else {
70 if (this.sortAscending) {
71 return aProp < bProp ? -1 : 1;
72 }
73 return aProp > bProp ? -1 : 1;
Yoshie Muranakafa562732019-07-17 11:23:15 -050074 }
Yoshie Muranakafa562732019-07-17 11:23:15 -050075 })
Yoshie Muranakafa562732019-07-17 11:23:15 -050076 };
77
78 /**
Yoshie Muranaka1d83af02019-09-06 08:52:35 -070079 * Prep table
80 * Make adjustments to account for optional configurations
81 */
82 const prepTable = () => {
83 if (this.sortable) {
84 // If sort is enabled, check for undefined 'sortable'
85 // property for each item in header array
86 this.header = this.header.map((column) => {
87 column.sortable =
88 column.sortable === undefined ? true : column.sortable;
89 return column;
90 })
91 }
92 if (this.rowActionsEnabled) {
93 // If table actions are enabled push an empty
94 // string to the header array to account for additional
95 // table actions cell
96 this.header.push({label: '', sortable: false});
97 }
98 if (this.expandable) {
99 // If table is expandable, push an empty string to the
100 // header array to account for additional expansion cell
101 this.header.unshift({label: '', sortable: false});
102 }
103 };
104
105 /**
Yoshie Muranakafa562732019-07-17 11:23:15 -0500106 * Callback when table row action clicked
107 * Emits user desired action and associated row data to
108 * parent controller
109 * @param {string} action : action type
110 * @param {any} row : user object
111 */
Yoshie Muranakabb688792019-08-12 09:31:52 -0500112 this.onEmitTableAction = (action, row) => {
Yoshie Muranakafa562732019-07-17 11:23:15 -0500113 if (action !== undefined && row !== undefined) {
114 const value = {action, row};
115 this.emitAction({value});
116 }
117 };
118
119 /**
Yoshie Muranakab1f64242019-09-04 11:40:51 -0700120 * Callback when sortable table header clicked
121 * @param {number} index : index of header item
122 */
123 this.onClickSort = (index) => {
124 if (index === this.activeSort) {
125 // If clicked header is already sorted, reverse
126 // the sort direction
127 this.sortAscending = !this.sortAscending;
128 this.data.reverse();
129 } else {
130 this.sortAscending = true;
131 this.activeSort = index;
132 sortData();
133 }
134 };
135
136 /**
Yoshie Muranaka1d83af02019-09-06 08:52:35 -0700137 * Callback when expand trigger clicked
138 * @param {number} row : index of expanded row
139 */
140 this.onClickExpand = (row) => {
141 if (this.expandedRows.has(row)) {
142 this.expandedRows.delete(row)
143 } else {
144 this.expandedRows.add(row);
145 }
146 };
147
148 /**
Yoshie Muranakab1f64242019-09-04 11:40:51 -0700149 * onInit Component lifecycle hook
150 * Checking for undefined values
Yoshie Muranakafa562732019-07-17 11:23:15 -0500151 */
152 this.$onInit = () => {
Yoshie Muranakab1f64242019-09-04 11:40:51 -0700153 this.header = this.header === undefined ? [] : this.header;
154 this.data = this.data == undefined ? [] : this.data;
155 this.sortable = this.sortable === undefined ? false : this.sortable;
Yoshie Muranakabb688792019-08-12 09:31:52 -0500156 this.rowActionsEnabled =
Yoshie Muranakab1f64242019-09-04 11:40:51 -0700157 this.rowActionsEnabled === undefined ? false : this.rowActionsEnabled;
158 this.size = this.size === undefined ? '' : this.size;
Yoshie Muranaka1d83af02019-09-06 08:52:35 -0700159 this.expandable = this.expandable === undefined ? false : this.expandable;
Yoshie Muranakab1f64242019-09-04 11:40:51 -0700160
161 // Check for undefined 'uiData' property for each item in data array
162 this.data = this.data.map((row) => {
163 if (row.uiData === undefined) {
164 row.uiData = [];
165 }
166 return row;
167 })
Yoshie Muranaka1d83af02019-09-06 08:52:35 -0700168 prepTable();
Yoshie Muranakafa562732019-07-17 11:23:15 -0500169 };
Yoshie Muranakab1f64242019-09-04 11:40:51 -0700170
171 /**
172 * onChanges Component lifecycle hook
173 * Check for changes in the data array and apply
174 * default or active sort if one is defined
175 */
176 this.$onChanges = (onChangesObj) => {
177 const dataChange = onChangesObj.data;
178 if (dataChange) {
179 if (this.activeSort !== undefined || this.defaultSort !== undefined) {
180 this.activeSort = this.defaultSort !== undefined ? this.defaultSort :
181 this.activeSort;
182 sortData();
183 }
184 }
185 }
Yoshie Muranakafa562732019-07-17 11:23:15 -0500186 };
187
188 /**
189 * Register bmcTable component
190 */
191 angular.module('app.common.components').component('bmcTable', {
192 template: require('./table.html'),
193 controller: TableController,
Yoshie Muranakab1f64242019-09-04 11:40:51 -0700194 bindings: {
195 data: '<', // Array
196 header: '<', // Array
197 rowActionsEnabled: '<', // boolean
198 size: '<', // string
199 sortable: '<', // boolean
200 defaultSort: '<', // number (index of sort)
Yoshie Muranaka1d83af02019-09-06 08:52:35 -0700201 expandable: '<', // boolean
Yoshie Muranakab1f64242019-09-04 11:40:51 -0700202 emitAction: '&'
203 }
Yoshie Muranakafa562732019-07-17 11:23:15 -0500204 })
205})(window.angular);