1window.angular && (function(angular) { 2 'use strict'; 3 4 /** 5 * 6 * bmcTable Component 7 * 8 * To use: 9 * 10 * 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' 13 * property that should be an array of the properties that will render 14 * as each table cell <td>. 15 * Each row object in the data array can optionally have an 16 * 'actions' property that should be an array of actions to provide the 17 * <bmc-table-actions> component. 18 * 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. 21 * 22 * 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. 45 * 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 49 * 50 * The 'size' attribute which can be set to 'small' which will 51 * render a smaller font size in the table. 52 * 53 */ 54 55 const TableController = function() { 56 this.sortAscending = true; 57 this.activeSort; 58 this.expandedRows = new Set(); 59 60 /** 61 * Sorts table data 62 */ 63 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; 74 } 75 }) 76 }; 77 78 /** 79 * 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 /** 106 * 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 */ 112 this.onEmitTableAction = (action, row) => { 113 if (action !== undefined && row !== undefined) { 114 const value = {action, row}; 115 this.emitAction({value}); 116 } 117 }; 118 119 /** 120 * 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 /** 137 * 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 /** 149 * onInit Component lifecycle hook 150 * Checking for undefined values 151 */ 152 this.$onInit = () => { 153 this.header = this.header === undefined ? [] : this.header; 154 this.data = this.data == undefined ? [] : this.data; 155 this.sortable = this.sortable === undefined ? false : this.sortable; 156 this.rowActionsEnabled = 157 this.rowActionsEnabled === undefined ? false : this.rowActionsEnabled; 158 this.size = this.size === undefined ? '' : this.size; 159 this.expandable = this.expandable === undefined ? false : this.expandable; 160 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 }) 168 prepTable(); 169 }; 170 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 } 186 }; 187 188 /** 189 * Register bmcTable component 190 */ 191 angular.module('app.common.components').component('bmcTable', { 192 template: require('./table.html'), 193 controller: TableController, 194 bindings: { 195 data: '<', // Array 196 header: '<', // Array 197 rowActionsEnabled: '<', // boolean 198 size: '<', // string 199 sortable: '<', // boolean 200 defaultSort: '<', // number (index of sort) 201 expandable: '<', // boolean 202 emitAction: '&' 203 } 204 }) 205})(window.angular); 206