@bennerinformatics/ember-fw-table 2.0.14 → 2.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,382 +1,390 @@
1
- import {isArray} from '@ember/array';
2
- import Component from '@ember/component';
3
- import {computed, observer} from '@ember/object';
4
- import {sort} from '@ember/object/computed';
5
- import {isEmpty, isNone} from '@ember/utils';
6
- import newTable from '../classes/Table';
7
- import exportTable from '../utils/export';
8
- import layout from '../templates/components/fw-table-sortable';
9
- import RSVP from 'rsvp';
10
-
11
- /**
12
- * This table contains logic to show a table as a panel in an app. This table features sorting rows can be expanded
13
- * to show hidden data
14
- * * ```handlebars
15
- * {{fw-table-sortable data columns title='Table'}}
16
- * ```
17
- *
18
- * If used in a block format instead, it is possible to fully define the title area, replacing the title parameter. The export action is passed as a yield parameter to redefine the export button
19
- * * ```handlebars
20
- * {{#fw-table-sortable data columns as |export|}}
21
- * <strong class='panel-title'>Table</strong>
22
- * {{/fw-table-sortable}}
23
- * ```
24
- *
25
- * In addition to the usual properties in columns, fw-table-sortable supports adding an additional `sortKey` property
26
- * If defined, clicking that column will sort by that property instead of valuePath
27
- *
28
- * It also supports a showExpanded property, which is set to a value which will cause this row to show in the expanded view if hidden.
29
- * Alternatively, setting this to `true` or `false` will force the state regardless of a property.
30
- * By default will show if the property from `valuePath` is not null
31
- *
32
- * @class TableSortable
33
- * @extends EmberLightTable
34
- * @module ember-fw-table
35
- * @public
36
- */
37
- const TableSortable = Component.extend({
38
- layout,
39
- classNames: ['panel', 'panel-default'],
40
-
41
- /**
42
- * Table rows
43
- *
44
- * @public
45
- * @property data
46
- * @type {Array}
47
- */
48
-
49
- /**
50
- * Table columns. See ember-light-table docs for more information
51
- *
52
- * @public
53
- * @property columns
54
- * @type {Array}
55
- */
56
- columns: null,
57
-
58
- /**
59
- * Additional class names for the table
60
- *
61
- * @public
62
- * @property tableClassNames
63
- * @type {Hash}
64
- */
65
- tableClassNames: '',
66
-
67
- /**
68
- * Hash of maximum columns based on responsive size
69
- *
70
- * @public
71
- * @property breakpoints
72
- * @type {Hash}
73
- */
74
-
75
- /**
76
- * Hash of actions to pass into the table for use in components
77
- *
78
- * @public
79
- * @property tableActions
80
- * @type {Hash}
81
- */
82
-
83
- /**
84
- * Title to display in the panel above the table
85
- *
86
- * @public
87
- * @property title
88
- * @type {String}
89
- */
90
-
91
- /**
92
- * Text to display when the table is empty
93
- *
94
- * @public
95
- * @property empty
96
- * @type {String}
97
- */
98
-
99
- /**
100
- * Action called when sorting the table
101
- *
102
- * @public
103
- * @property onSort
104
- * @type {Action}
105
- */
106
- onSort: () => RSVP.resolve(),
107
-
108
- /**
109
- * Set to false to make the table not show the header if empty
110
- *
111
- * @public
112
- * @property headerEmpty
113
- * @type {Boolean}
114
- */
115
- headerEmpty: true,
116
- /**
117
- * Sets the table to a fixed height, and adds a scroll bar to see the hidden rows
118
- *
119
- * @public
120
- * @property fixedHeight
121
- * @type {String}
122
- */
123
- fixedHeight: null,
124
- /**
125
- * Makes the table responsive to the screen width. The table is slightly more efficient if false when responsive is not used
126
- *
127
- * @public
128
- * @property responsive
129
- * @type {Boolean}
130
- */
131
- responsive: false,
132
-
133
- /**
134
- * Allows exporting the current contents of the table using an export button. Note that this sets a default title of "Table", so setting a separate one is recommended
135
- *
136
- * When enabled, columns have some additional values defined:
137
- * * `canExport`: if true (default), the column will be included in the export. Set to false to skip exporting the column.
138
- * * `exportOnly`: if true, the column will not show in the table, but will still be exported.
139
- *
140
- * Additionally, the value `export` is set to true in `format`'s context
141
- * @public
142
- * @property canExport
143
- * @type {Boolean}
144
- */
145
- canExport: false,
146
-
147
- /**
148
- * If true, uses the table block data for the header
149
- *
150
- * @public
151
- * @property showHeader
152
- * @type {Boolean}
153
- */
154
- showHeader: true,
155
-
156
- /**
157
- * If true, uses the table block data for the footer
158
- *
159
- * @public
160
- * @property showFooter
161
- * @type {Boolean}
162
- */
163
- showFooter: false,
164
-
165
- /**
166
- * Default sort order for the table. Can be a string for a single property, or an array to fallback
167
- *
168
- * @public
169
- * @property defaultSort
170
- * @type {String|Array}
171
- */
172
- defaultSort: null,
173
-
174
- /**
175
- * Private copy of defaultSort, made an array if its not
176
- *
177
- * @private
178
- * @property _defaultSort
179
- * @type {Array}
180
- */
181
- _defaultSort: computed('defaultSort', function() {
182
- let defaultSort = this.get('defaultSort');
183
- if (isNone(defaultSort)) {
184
- return [];
185
- }
186
- if (!isArray(defaultSort)) {
187
- return [defaultSort];
188
- }
189
- return defaultSort;
190
- }),
191
-
192
- /**
193
- * Sort column set by clicking a column to sort
194
- *
195
- * @private
196
- * @property sortColumn
197
- * @type {String}
198
- */
199
- sortColumn: null,
200
-
201
- /**
202
- * Internal sort order. Calculated from sortColumn and _defaultSort
203
- *
204
- * @private
205
- * @property sortOrder
206
- * @type {Array}
207
- */
208
- sortOrder: computed('_defaultSort.[]', 'sortColumn', function() {
209
- let sortColumn = this.get('sortColumn');
210
- if (sortColumn) {
211
- return [sortColumn].pushObjects(this.get('_defaultSort'));
212
- }
213
- return this.get('_defaultSort');
214
- }),
215
-
216
- /**
217
- * Passed in table data after sorting.
218
- *
219
- * @private
220
- * @property sortedData
221
- * @type {Array}
222
- */
223
- sortedData: sort('data', 'sortOrder'),
224
-
225
- /**
226
- * Internal table instance, in general changes to the table should be done to columns or data so they update propery
227
- *
228
- * @protected
229
- * @property table
230
- * @type {Table}
231
- */
232
- table: null,
233
-
234
- /**
235
- * Computed table title based on whether a boolean true is passed or a title
236
- *
237
- * @private
238
- * @property exportTitle
239
- * @type {String}
240
- */
241
- exportTitle: computed('canExport', function() {
242
- let canExport = this.get('canExport');
243
- if (canExport === true) {
244
- return 'Export';
245
- }
246
- return canExport || '';
247
- }),
248
-
249
- /**
250
- * Called to delete the full page of entries
251
- */
252
- deleteTable() {},
253
-
254
- /**
255
- * Determines title for delete function
256
- */
257
- deleteOverrideTitle: null,
258
-
259
- deleteTitle: computed('deleteTable', 'deleteOverrideTitle', function() {
260
- if (isEmpty(this.get('deleteTable'))) {
261
- return null;
262
- } else {
263
- return this.get('deleteOverrideTitle') ? this.get('deleteOverrideTitle') : 'Delete All';
264
- }
265
- }),
266
-
267
- /**
268
- * Callback to update the table rows whenever the data is changed, generally from sorting
269
- *
270
- * @private
271
- * @method updateTableRows
272
- */
273
- // eslint-disable-next-line ember/no-observers
274
- updateTableRows: observer('sortedData', function() {
275
- if (this.get('table')) {
276
- this.get('table').setRows(this.get('sortedData'));
277
- }
278
- }),
279
-
280
- /**
281
- * Callback to update the table columns whenever they change. Unlikely, but still a case worth covering
282
- *
283
- * @private
284
- * @method updateTableColumns
285
- */
286
- // eslint-disable-next-line ember/no-observers
287
- updateTableColumns: observer('columns', function() {
288
- if (this.get('table')) {
289
- this.get('table').setColumns(this.get('columns'));
290
- }
291
- }),
292
-
293
- /**
294
- * Initializes the component and loads the modal path.
295
- *
296
- * @private
297
- * @method didReceiveAttrs
298
- */
299
- didReceiveAttrs() {
300
- this._super(...arguments);
301
- // first, determine our sorting
302
- let defaultSort = this.get('_defaultSort');
303
- let columns = this.get('columns') || [];
304
- if (!isEmpty(defaultSort)) {
305
- // make a map of key to ascending
306
- let [mainKey] = defaultSort;
307
- let ascending = true;
308
- if (mainKey.endsWith(':desc')) {
309
- ascending = false;
310
- mainKey = mainKey.substring(0, mainKey.length - 5);
311
- }
312
- columns = columns.map((column) => {
313
- let key = column.sortKey || column.valuePath;
314
- return key === mainKey ? Object.assign({}, column, {sorted: true, ascending}) : column;
315
- });
316
- }
317
- this.set('table', newTable(columns, this.get('sortedData')));
318
-
319
- if (this.get('noPanel')) {
320
- this.set('classNames', null);
321
- }
322
- },
323
-
324
- actions: {
325
- /**
326
- * Called when clicking aa column header to sort the current row
327
- * @param {Column} column Column clicked
328
- */
329
- sort(column) {
330
- if (!column.sortable) {
331
- return;
332
- }
333
-
334
- // modify the sort state, this is ported from the default code to properly set ascending to true on sort
335
- if (column.sorted) {
336
- column.toggleProperty('ascending');
337
- } else {
338
- this.get('table.sortedColumns').setEach('sorted', false);
339
- column.set('sorted', true);
340
- column.set('ascending', true);
341
- }
342
-
343
- // determine how we are sorting
344
- let sortKey = column.sortKey || column.valuePath;
345
-
346
- // if the column is now decending, sort decending
347
- if (!column.ascending) {
348
- sortKey = `${sortKey}:desc`;
349
- }
350
-
351
- // call the sort logic
352
- this.onSort(column, sortKey).then(() => {
353
- this.set('sortColumn', sortKey);
354
- });
355
- },
356
-
357
- /**
358
- * Called when clicking a row to expand it and show more information
359
- * @param {Row} row Row clicked
360
- */
361
- expand(row) {
362
- let expand = row.get('canExpand') ? !row.get('expanded') : false;
363
- if (expand) {
364
- this.get('table.expandedRows').setEach('expanded', false);
365
- }
366
- row.set('expanded', expand);
367
- },
368
-
369
- /**
370
- * Called by the export button to export the current table data to CSV
371
- */
372
- export() {
373
- exportTable(this.get('table'), this.get('title'));
374
- }
375
- }
376
- });
377
-
378
- TableSortable.reopenClass({
379
- positionalParams: ['data', 'columns']
380
- });
381
-
382
- export default TableSortable;
1
+ import {isArray} from '@ember/array';
2
+ import Component from '@ember/component';
3
+ import {computed, observer} from '@ember/object';
4
+ import {sort} from '@ember/object/computed';
5
+ import {isEmpty, isNone} from '@ember/utils';
6
+ import newTable from '../classes/Table';
7
+ import exportTable from '../utils/export';
8
+ import layout from '../templates/components/fw-table-sortable';
9
+ import RSVP from 'rsvp';
10
+
11
+ /**
12
+ * This table contains logic to show a table as a panel in an app. This table features sorting rows can be expanded
13
+ * to show hidden data
14
+ * * ```handlebars
15
+ * {{fw-table-sortable data columns title='Table'}}
16
+ * ```
17
+ *
18
+ * If used in a block format instead, it is possible to fully define the title area, replacing the title parameter. The export action is passed as a yield parameter to redefine the export button
19
+ * * ```handlebars
20
+ * {{#fw-table-sortable data columns as |export|}}
21
+ * <strong class='panel-title'>Table</strong>
22
+ * {{/fw-table-sortable}}
23
+ * ```
24
+ *
25
+ * In addition to the usual properties in columns, fw-table-sortable supports adding an additional `sortKey` property
26
+ * If defined, clicking that column will sort by that property instead of valuePath
27
+ *
28
+ * It also supports a showExpanded property, which is set to a value which will cause this row to show in the expanded view if hidden.
29
+ * Alternatively, setting this to `true` or `false` will force the state regardless of a property.
30
+ * By default will show if the property from `valuePath` is not null
31
+ *
32
+ * @class TableSortable
33
+ * @extends EmberLightTable
34
+ * @module ember-fw-table
35
+ * @public
36
+ */
37
+ const TableSortable = Component.extend({
38
+ layout,
39
+ classNames: ['panel', 'panel-default'],
40
+
41
+ /**
42
+ * Table rows
43
+ *
44
+ * @public
45
+ * @property data
46
+ * @type {Array}
47
+ */
48
+
49
+ /**
50
+ * Table columns. See ember-light-table docs for more information
51
+ *
52
+ * @public
53
+ * @property columns
54
+ * @type {Array}
55
+ */
56
+ columns: null,
57
+
58
+ /**
59
+ * Additional class names for the table
60
+ *
61
+ * @public
62
+ * @property tableClassNames
63
+ * @type {Hash}
64
+ */
65
+ tableClassNames: '',
66
+
67
+ /**
68
+ * Hash of maximum columns based on responsive size
69
+ *
70
+ * @public
71
+ * @property breakpoints
72
+ * @type {Hash}
73
+ */
74
+
75
+ /**
76
+ * Hash of actions to pass into the table for use in components
77
+ *
78
+ * @public
79
+ * @property tableActions
80
+ * @type {Hash}
81
+ */
82
+
83
+ /**
84
+ * Title to display in the panel above the table
85
+ *
86
+ * @public
87
+ * @property title
88
+ * @type {String}
89
+ */
90
+
91
+ /**
92
+ * Text to display when the table is empty
93
+ *
94
+ * @public
95
+ * @property empty
96
+ * @type {String}
97
+ */
98
+
99
+ /**
100
+ * Action called when sorting the table
101
+ *
102
+ * @public
103
+ * @property onSort
104
+ * @type {Action}
105
+ */
106
+ onSort: () => RSVP.resolve(),
107
+
108
+ /**
109
+ * Set to false to make the table not show the header if empty
110
+ *
111
+ * @public
112
+ * @property headerEmpty
113
+ * @type {Boolean}
114
+ */
115
+ headerEmpty: true,
116
+ /**
117
+ * Sets the table to a fixed height, and adds a scroll bar to see the hidden rows
118
+ *
119
+ * @public
120
+ * @property fixedHeight
121
+ * @type {String}
122
+ */
123
+ fixedHeight: null,
124
+ /**
125
+ * Makes the table responsive to the screen width. The table is slightly more efficient if false when responsive is not used
126
+ *
127
+ * @public
128
+ * @property responsive
129
+ * @type {Boolean}
130
+ */
131
+ responsive: false,
132
+
133
+ /**
134
+ * Allows exporting the current contents of the table using an export button. Note that this sets a default title of "Table", so setting a separate one is recommended
135
+ *
136
+ * When enabled, columns have some additional values defined:
137
+ * * `canExport`: if true (default), the column will be included in the export. Set to false to skip exporting the column.
138
+ * * `exportOnly`: if true, the column will not show in the table, but will still be exported.
139
+ *
140
+ * Additionally, the value `export` is set to true in `format`'s context
141
+ * @public
142
+ * @property canExport
143
+ * @type {Boolean}
144
+ */
145
+ canExport: false,
146
+
147
+ /**
148
+ * If true, uses the table block data for the header
149
+ *
150
+ * @public
151
+ * @property showHeader
152
+ * @type {Boolean}
153
+ */
154
+ showHeader: true,
155
+
156
+ /**
157
+ * If true, uses the table block data for the footer
158
+ *
159
+ * @public
160
+ * @property showFooter
161
+ * @type {Boolean}
162
+ */
163
+ showFooter: false,
164
+
165
+ /**
166
+ * Default sort order for the table. Can be a string for a single property, or an array to fallback
167
+ *
168
+ * @public
169
+ * @property defaultSort
170
+ * @type {String|Array}
171
+ */
172
+ defaultSort: null,
173
+
174
+ /**
175
+ * Private copy of defaultSort, made an array if its not
176
+ *
177
+ * @private
178
+ * @property _defaultSort
179
+ * @type {Array}
180
+ */
181
+ _defaultSort: computed('defaultSort', function() {
182
+ let defaultSort = this.get('defaultSort');
183
+ if (isNone(defaultSort)) {
184
+ return [];
185
+ }
186
+ if (!isArray(defaultSort)) {
187
+ return [defaultSort];
188
+ }
189
+ return defaultSort;
190
+ }),
191
+
192
+ /**
193
+ * Sort column set by clicking a column to sort
194
+ *
195
+ * @private
196
+ * @property sortColumn
197
+ * @type {String}
198
+ */
199
+ sortColumn: null,
200
+
201
+ /**
202
+ * Internal sort order. Calculated from sortColumn and _defaultSort
203
+ *
204
+ * @private
205
+ * @property sortOrder
206
+ * @type {Array}
207
+ */
208
+ sortOrder: computed('_defaultSort.[]', 'sortColumn', function() {
209
+ let sortColumn = this.get('sortColumn');
210
+ if (sortColumn) {
211
+ return [sortColumn].pushObjects(this.get('_defaultSort'));
212
+ }
213
+ return this.get('_defaultSort');
214
+ }),
215
+
216
+ /**
217
+ * Passed in table data after sorting.
218
+ *
219
+ * @private
220
+ * @property sortedData
221
+ * @type {Array}
222
+ */
223
+ sortedData: sort('data', 'sortOrder'),
224
+
225
+ /**
226
+ * Internal table instance, in general changes to the table should be done to columns or data so they update propery
227
+ *
228
+ * @protected
229
+ * @property table
230
+ * @type {Table}
231
+ */
232
+ table: null,
233
+
234
+ /**
235
+ * Computed table title based on whether a boolean true is passed or a title
236
+ *
237
+ * @private
238
+ * @property exportTitle
239
+ * @type {String}
240
+ */
241
+ exportTitle: computed('canExport', function() {
242
+ let canExport = this.get('canExport');
243
+ if (canExport === true) {
244
+ return 'Export';
245
+ }
246
+ return canExport || '';
247
+ }),
248
+
249
+ /**
250
+ * Called to delete the full page of entries
251
+ */
252
+ deleteTable: undefined,
253
+
254
+ /**
255
+ * Determines title for delete function
256
+ */
257
+ deleteOverrideTitle: null,
258
+
259
+ deleteTitle: computed('deleteTable', 'deleteOverrideTitle', function() {
260
+ if (!this.get('deleteTable')) {
261
+ return null;
262
+ } else {
263
+ return this.get('deleteOverrideTitle') ? this.get('deleteOverrideTitle') : 'Delete All';
264
+ }
265
+ }),
266
+
267
+ /**
268
+ * Determines if the current user has permission to delete the table (usually has-role helper will be used to determine this)
269
+ * example: deleteTablePermission=(has-role 'admin')
270
+ * @type {Boolean}
271
+ * @default true
272
+ */
273
+ deleteTablePermission: true,
274
+
275
+ /**
276
+ * Callback to update the table rows whenever the data is changed, generally from sorting
277
+ *
278
+ * @private
279
+ * @method updateTableRows
280
+ */
281
+ // eslint-disable-next-line ember/no-observers
282
+ updateTableRows: observer('sortedData', function() {
283
+ if (this.get('table')) {
284
+ this.get('table').setRows(this.get('sortedData'));
285
+ }
286
+ }),
287
+
288
+ /**
289
+ * Callback to update the table columns whenever they change. Unlikely, but still a case worth covering
290
+ *
291
+ * @private
292
+ * @method updateTableColumns
293
+ */
294
+ // eslint-disable-next-line ember/no-observers
295
+ updateTableColumns: observer('columns', function() {
296
+ if (this.get('table')) {
297
+ this.get('table').setColumns(this.get('columns'));
298
+ }
299
+ }),
300
+
301
+ /**
302
+ * Initializes the component and loads the modal path.
303
+ *
304
+ * @private
305
+ * @method didReceiveAttrs
306
+ */
307
+ didReceiveAttrs() {
308
+ this._super(...arguments);
309
+ // first, determine our sorting
310
+ let defaultSort = this.get('_defaultSort');
311
+ let columns = this.get('columns') || [];
312
+ if (!isEmpty(defaultSort)) {
313
+ // make a map of key to ascending
314
+ let [mainKey] = defaultSort;
315
+ let ascending = true;
316
+ if (mainKey.endsWith(':desc')) {
317
+ ascending = false;
318
+ mainKey = mainKey.substring(0, mainKey.length - 5);
319
+ }
320
+ columns = columns.map((column) => {
321
+ let key = column.sortKey || column.valuePath;
322
+ return key === mainKey ? Object.assign({}, column, {sorted: true, ascending}) : column;
323
+ });
324
+ }
325
+ this.set('table', newTable(columns, this.get('sortedData')));
326
+
327
+ if (this.get('noPanel')) {
328
+ this.set('classNames', null);
329
+ }
330
+ },
331
+
332
+ actions: {
333
+ /**
334
+ * Called when clicking aa column header to sort the current row
335
+ * @param {Column} column Column clicked
336
+ */
337
+ sort(column) {
338
+ if (!column.sortable) {
339
+ return;
340
+ }
341
+
342
+ // modify the sort state, this is ported from the default code to properly set ascending to true on sort
343
+ if (column.sorted) {
344
+ column.toggleProperty('ascending');
345
+ } else {
346
+ this.get('table.sortedColumns').setEach('sorted', false);
347
+ column.set('sorted', true);
348
+ column.set('ascending', true);
349
+ }
350
+
351
+ // determine how we are sorting
352
+ let sortKey = column.sortKey || column.valuePath;
353
+
354
+ // if the column is now decending, sort decending
355
+ if (!column.ascending) {
356
+ sortKey = `${sortKey}:desc`;
357
+ }
358
+
359
+ // call the sort logic
360
+ this.onSort(column, sortKey).then(() => {
361
+ this.set('sortColumn', sortKey);
362
+ });
363
+ },
364
+
365
+ /**
366
+ * Called when clicking a row to expand it and show more information
367
+ * @param {Row} row Row clicked
368
+ */
369
+ expand(row) {
370
+ let expand = row.get('canExpand') ? !row.get('expanded') : false;
371
+ if (expand) {
372
+ this.get('table.expandedRows').setEach('expanded', false);
373
+ }
374
+ row.set('expanded', expand);
375
+ },
376
+
377
+ /**
378
+ * Called by the export button to export the current table data to CSV
379
+ */
380
+ export() {
381
+ exportTable(this.get('table'), this.get('title'));
382
+ }
383
+ }
384
+ });
385
+
386
+ TableSortable.reopenClass({
387
+ positionalParams: ['data', 'columns']
388
+ });
389
+
390
+ export default TableSortable;