@neovici/cosmoz-omnitable 7.2.1 → 8.0.0-beta.1

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.
Files changed (50) hide show
  1. package/README.md +23 -0
  2. package/cosmoz-omnitable-column-amount.js +89 -320
  3. package/cosmoz-omnitable-column-autocomplete.js +36 -47
  4. package/cosmoz-omnitable-column-boolean.js +107 -209
  5. package/cosmoz-omnitable-column-date.js +89 -102
  6. package/cosmoz-omnitable-column-datetime.js +86 -119
  7. package/cosmoz-omnitable-column-list-data.js +4 -1
  8. package/cosmoz-omnitable-column-list-horizontal.js +20 -38
  9. package/cosmoz-omnitable-column-list-mixin.js +133 -140
  10. package/cosmoz-omnitable-column-list.js +19 -28
  11. package/cosmoz-omnitable-column-mixin.js +69 -447
  12. package/cosmoz-omnitable-column-number.js +91 -183
  13. package/cosmoz-omnitable-column-time.js +77 -162
  14. package/cosmoz-omnitable-column.js +49 -93
  15. package/cosmoz-omnitable-group-row.js +1 -5
  16. package/cosmoz-omnitable-header-row.js +9 -6
  17. package/cosmoz-omnitable-item-expand.js +0 -3
  18. package/cosmoz-omnitable-item-row.js +5 -8
  19. package/cosmoz-omnitable-styles.js +1 -5
  20. package/cosmoz-omnitable.js +72 -763
  21. package/lib/cosmoz-omnitable-amount-range-input.js +295 -0
  22. package/{cosmoz-omnitable-column-date-mixin.js → lib/cosmoz-omnitable-date-input-mixin.js} +4 -26
  23. package/lib/cosmoz-omnitable-date-range-input.js +81 -0
  24. package/lib/cosmoz-omnitable-datetime-range-input.js +75 -0
  25. package/lib/cosmoz-omnitable-number-range-input.js +159 -0
  26. package/{cosmoz-omnitable-column-range-mixin.js → lib/cosmoz-omnitable-range-input-mixin.js} +45 -123
  27. package/lib/cosmoz-omnitable-settings.js +7 -4
  28. package/lib/cosmoz-omnitable-time-range-input.js +130 -0
  29. package/lib/generic-sorter.js +2 -2
  30. package/lib/invoke.js +1 -0
  31. package/lib/memoize.js +54 -0
  32. package/lib/polymer-haunted-render-mixin.js +19 -0
  33. package/lib/save-as-csv-action.js +32 -0
  34. package/lib/save-as-xlsx-action.js +25 -0
  35. package/lib/use-canvas-width.js +1 -1
  36. package/lib/use-dom-columns.js +133 -0
  37. package/lib/use-hash-state.js +59 -0
  38. package/lib/use-layout.js +1 -1
  39. package/lib/use-omnitable.js +26 -14
  40. package/lib/use-processed-items.js +132 -0
  41. package/lib/use-sort-and-group-options.js +30 -0
  42. package/lib/utils-amount.js +147 -0
  43. package/lib/utils-data.js +36 -0
  44. package/lib/utils-date.js +204 -0
  45. package/lib/utils-datetime.js +71 -0
  46. package/lib/utils-number.js +112 -0
  47. package/lib/utils-time.js +115 -0
  48. package/package.json +1 -1
  49. package/lib/use-force-render.js +0 -8
  50. package/lib/use-render-on-column-updates.js +0 -18
@@ -5,9 +5,8 @@ import {
5
5
  html, nothing
6
6
  } from 'lit-html';
7
7
 
8
- import { translatable } from '@neovici/cosmoz-i18next';
9
8
  import { columnMixin } from './cosmoz-omnitable-column-mixin';
10
- import { listColumnMixin } from './cosmoz-omnitable-column-list-mixin';
9
+ import { getString, getTexts, listColumnMixin, onChange, onFocus, onText } from './cosmoz-omnitable-column-list-mixin';
11
10
  import '@neovici/cosmoz-autocomplete';
12
11
 
13
12
  /**
@@ -16,50 +15,42 @@ import '@neovici/cosmoz-autocomplete';
16
15
  * @appliesMixin listColumnMixin
17
16
  * @appliesMixin columnMixin
18
17
  */
19
- class OmnitableColumnList extends listColumnMixin(
20
- columnMixin(translatable(PolymerElement))
21
- ) {
22
- static get is() {
23
- return 'cosmoz-omnitable-column-list';
24
- }
25
-
26
- renderCell(column, { item }) {
18
+ class OmnitableColumnList extends listColumnMixin(columnMixin(PolymerElement)) {
19
+ renderCell({ valuePath, textProperty }, { item }) {
27
20
  return html`<cosmoz-omnitable-column-list-data
28
- .items=${ column.getTexts(item, column.valuePath, column.textProperty) }
21
+ .items=${ getTexts(item, valuePath, textProperty) }
29
22
  ></cosmoz-omnitable-column-list-data>`;
30
23
  }
31
24
 
32
- renderEditCell(column, { item }) {
33
- const onChange = event => {
34
- event.model = { item };
35
- return column._valueChanged(event);
36
- };
25
+ renderEditCell(column, { item }, onItemChange) {
26
+ const onChange = event => onItemChange(event.target.value.split(/,\s*/gu));
37
27
 
38
28
  return html`<paper-input
39
29
  no-label-float
40
30
  type="text"
41
31
  @change=${ onChange }
42
- .value=${ column.getString(item, column.valuePath) }
32
+ .value=${ getString(column, item) }
43
33
  ></paper-input>`;
44
34
  }
45
35
 
46
- renderHeader(column) {
47
- const spinner = column.loading
48
- ? html`<paper-spinner-lite style="width: 20px; height: 20px;" suffix slot="suffix" active></paper-spinner-lite>`
49
- : nothing;
36
+ renderHeader(column, { filter, query }, setState, source) {
37
+ const
38
+ spinner = column.loading
39
+ ? html`<paper-spinner-lite style="width: 20px; height: 20px;" suffix slot="suffix" active></paper-spinner-lite>`
40
+ : nothing;
50
41
 
51
42
  return html`<cosmoz-autocomplete-ui
52
43
  class="external-values-${ column.externalValues }"
53
44
  .label=${ column.title }
54
- .source=${ column._source }
45
+ .source=${ source }
55
46
  .textProperty=${ column.textProperty }
56
- .value=${ column._computeValue(column.filter, column._source) }
57
- .text=${ column.query }
58
- .onChange=${ column._onChange }
59
- .onFocus=${ column._onFocus }
60
- .onText=${ column._onText }
47
+ .value=${ filter }
48
+ .text=${ query }
49
+ .onChange=${ onChange(setState) }
50
+ .onFocus=${ onFocus(setState) }
51
+ .onText=${ onText(setState) }
61
52
  >${ spinner }</cosmoz-autocomplete-ui>`;
62
53
  }
63
54
  }
64
55
 
65
- customElements.define(OmnitableColumnList.is, OmnitableColumnList);
56
+ customElements.define('cosmoz-omnitable-column-list', OmnitableColumnList);
@@ -1,480 +1,102 @@
1
- import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin';
1
+ import { get } from '@polymer/polymer/lib/utils/path';
2
2
 
3
- /**
4
- * @polymer
5
- * @mixinFunction
6
- * @appliesMixin templatizeMixin
7
- */
8
- export const columnMixin = dedupingMixin(base => class extends base { // eslint-disable-line max-lines-per-function
9
- static get properties() { // eslint-disable-line max-lines-per-function
10
- return {
11
- title: {
12
- type: String,
13
- observer: '_titleChanged'
14
- },
3
+ export const
4
+ getString = ({ valuePath }, item) => get(item, valuePath),
5
+ toXlsxValue = getString,
15
6
 
16
- valuePath: {
17
- type: String
18
- },
19
-
20
- /**
21
- * If the column should be disabled until enabled with enabledColumns
22
- */
23
- disabled: {
24
- type: Boolean,
25
- value: false,
26
- observer: '_disabledChanged'
27
- },
28
-
29
- /**
30
- * If true, the column will be editable by using an input element for rendering.
31
- */
32
- editable: {
33
- type: Boolean,
34
- observer: '_editableChanged'
35
- },
36
-
37
- /**
38
- * Values to filter this column will be managed outside of omnitable
39
- */
40
- externalValues: {
41
- type: Boolean,
42
- value: false,
43
- observer: '_externalValuesChanged'
44
- },
45
-
46
- filter: {
47
- type: Object,
48
- notify: true,
49
- value() {
50
- return this._getDefaultFilter();
51
- }
52
- },
53
-
54
- /**
55
- * Indicates whether the user has engaged with the
56
- * header/filter component of the column
57
- */
58
- headerFocused: {
59
- type: Boolean,
60
- value: false,
61
- notify: true
62
- },
63
-
64
- /**
65
- * Indicate that the column is loading/performing work
66
- */
67
- loading: {
68
- type: Boolean,
69
- value: false
70
- },
71
-
72
- /**
73
- * Whether the column wants a list of all distinct values for the column in `values`
74
- */
75
- bindValues: {
76
- type: Boolean,
77
- readOnly: true,
78
- value: false
79
- },
80
-
81
- /**
82
- * Column name for use with enabledColumns
83
- */
84
- name: {
85
- type: String
86
- },
87
-
88
- /**
89
- * All values for this column.
90
- */
91
- values: {
92
- type: Array
93
- },
94
-
95
- sortOn: {
96
- type: String
97
- },
7
+ applySingleFilter = ({ valuePath }, filter) => item => {
8
+ const value = get(item, valuePath);
9
+ if (value == null) {
10
+ return false;
11
+ }
12
+ return value.toString().toLowerCase().includes(filter.toLowerCase());
13
+ },
98
14
 
99
- groupOn: {
100
- type: String
101
- },
15
+ serializeFilter = (column, filter) => filter === '' || filter == null ? null : filter,
102
16
 
17
+ // eslint-disable-next-line max-lines-per-function
18
+ columnMixin = base => class extends base {
19
+ static get properties() {
20
+ return {
103
21
  /**
104
22
  * Used to indicate that an element using this behavior is a column definition that can be used
105
23
  * in cosmoz-omnitable
106
24
  */
107
- isOmnitableColumn: {
108
- type: Boolean,
109
- value: true,
110
- readOnly: true
111
- },
112
-
113
- /**
114
- * Base width of this column.
115
- */
116
- width: {
117
- type: String,
118
- value: '75px'
119
- },
120
-
121
- /**
122
- * Base width of this column when in edit mode.
123
- */
124
- editWidth: {
125
- type: String,
126
- value: '75px'
127
- },
128
-
129
- /**
130
- * The minimum width of this column.
131
- */
132
- minWidth: {
133
- type: String,
134
- value: '40px'
135
- },
136
-
137
- /**
138
- * The minimum width of this column in edit mode.
25
+ isOmnitableColumn: { type: Boolean, value: true },
26
+ title: { type: String },
27
+ valuePath: { type: String, notify: true },
28
+ values: { type: Array, notify: true },
29
+ /**
30
+ * If the column should be disabled until enabled with enabledColumns
139
31
  */
140
- editMinWidth: {
141
- type: String,
142
- value: '40px'
143
- },
144
-
145
- /**
146
- * Width growing factor for this column.
32
+ disabled: { type: Boolean, value: false, notify: true },
33
+ /**
34
+ * If true, the column will be editable by using an input element for rendering.
147
35
  */
148
- flex: {
149
- type: String,
150
- value: '1'
151
- },
152
-
153
- cellClass: {
154
- type: String,
155
- value: 'default-cell'
156
- },
157
-
158
- headerCellClass: {
159
- type: String,
160
- value: 'default-header-cell'
161
- },
162
-
163
- styleModule: {
164
- type: String
165
- },
166
-
167
- /**
168
- * Allow column to overflow without triggering
169
- * column folding (good for long descriptions)
36
+ editable: { type: Boolean, notify: true },
37
+ /**
38
+ * Indicate that the column is loading/performing work
170
39
  */
171
- overflow: {
172
- type: Boolean,
173
- value: false
174
- },
175
-
176
- priority: {
177
- type: Number,
178
- value: 0
179
- },
180
-
181
- /**
182
- * Cell title function, can be overriden.
40
+ loading: { type: Boolean, value: false, notify: true },
41
+ externalValues: { type: Boolean, value: false, notify: true },
42
+ /**
43
+ * Column name for use with enabledColumns
183
44
  */
184
- cellTitleFn: {
185
- type: Function,
186
- value() {
187
- /**
188
- * @this item
189
- * @param {object} item Row item.
190
- * @param {string} valuePath Path in the item.
191
- * @returns {string} Cell title.
192
- */
193
- return function (item, valuePath = this.valuePath) {
194
- return this.getString(item, valuePath);
195
- };
196
- }
197
- },
198
-
199
- hidden: {
200
- type: Boolean,
201
- reflectToAttribute: true,
202
- observer: '_hiddenChanged'
203
- },
204
-
205
- preferredDropdownHorizontalAlign: {
206
- type: String,
207
- value: 'right'
208
- },
209
-
210
- renderHeader: {
211
- type: Function
212
- },
213
-
214
- renderCell: {
215
- type: Function
216
- },
217
-
218
- renderEditCell: {
219
- type: Function
220
- },
221
-
222
- renderGroup: {
223
- type: Function
224
- }
225
- };
226
- }
227
-
228
- static get observers() {
229
- return [
230
- '__filterChanged(filter.*)',
231
- '_pathsChanged(valuePath, groupOn, sortOn)'
232
- ];
233
- }
234
-
235
- ready() {
236
- super.ready();
237
- this._itemCells = [];
238
- }
239
-
240
- _externalValuesChanged(newExternalValuesValue) {
241
- if (newExternalValuesValue) {
242
- return;
45
+ name: { type: String },
46
+ sortOn: { type: String },
47
+ groupOn: { type: String },
48
+ width: { type: String, value: '75px' },
49
+ minWidth: { type: String, value: '40px' },
50
+ flex: { type: String, value: '1' },
51
+ cellClass: { type: String, value: 'default-cell' },
52
+ headerCellClass: { type: String, value: 'default-header-cell' },
53
+ priority: { type: Number, value: 0 },
54
+ hidden: { type: Boolean, notify: true },
55
+ preferredDropdownHorizontalAlign: { type: String, value: 'right' },
56
+ renderHeader: { type: Function },
57
+ renderCell: { type: Function },
58
+ renderEditCell: { type: Function },
59
+ renderGroup: { type: Function }
60
+ };
243
61
  }
244
- this.dispatchEvent(new CustomEvent('cosmoz-column-values-update', {
245
- bubbles: true,
246
- composed: true,
247
- detail: {
248
- column: this
249
- }
250
- }));
251
- }
252
62
 
253
- /**
63
+ /**
254
64
  * Override this in column elements if you need a different default width
255
65
  */
256
66
 
257
- getString(item, valuePath = this.valuePath) {
258
- if (valuePath === undefined) {
259
- // eslint-disable-next-line no-console
260
- console.error(this, 'has undefined valuePath arg', valuePath, 'for item', item);
261
- return;
262
- }
263
- return this.get(valuePath, item);
264
- }
265
- toXlsxValue(item, valuePath = this.valuePath) {
266
- if (!valuePath) {
267
- return '';
268
- }
269
- return this.get(valuePath, item);
270
- }
271
- _unique(values, valueProperty) {
272
- if (!Array.isArray(values)) {
273
- return;
274
- }
275
- const used = [];
276
- return values.reduce((acc, cur) => {
277
- if (Array.isArray(cur)) {
278
- cur.forEach(subcur => {
279
- acc.push(subcur);
280
- });
281
- return acc;
282
- }
283
- acc.push(cur);
284
- return acc;
285
- }, [])
286
- .filter((item, index, array) => {
287
- if (array.indexOf(item) !== index) {
288
- return false;
289
- }
290
- if (valueProperty) {
291
- const value = this.get(valueProperty, item);
292
- if (used.indexOf(value) !== -1) {
293
- return false;
294
- }
295
- used.push(value);
296
- }
297
- return true;
298
- });
299
- }
300
- _pathsChanged(valuePath, groupOn, sortOn) {
301
- if (valuePath == null) {
302
- return;
303
- }
67
+ // eslint-disable-next-line no-empty-function
68
+ getFilterFn() {}
304
69
 
305
- if (groupOn == null) {
306
- this.set('groupOn', valuePath);
70
+ getString(column, item) {
71
+ return getString(column, item);
307
72
  }
308
73
 
309
- if (sortOn == null) {
310
- this.set('sortOn', valuePath);
74
+ toXlsxValue(column, item) {
75
+ return toXlsxValue(column, item);
311
76
  }
312
- }
313
-
314
- getComparableValue(item, valuePath) {
315
- return this.get(valuePath, item);
316
- }
317
-
318
- _valueChanged(event) {
319
- const input = event.target,
320
- value = input.value,
321
- item = event.model.item,
322
- oldValue = this.get(this.valuePath, item);
323
-
324
- this.set(this.valuePath, value, item);
325
- this._fireItemChangeEvent(item, this.valuePath, oldValue);
326
- }
327
-
328
- _fireItemChangeEvent(item, valuePath, oldValue, formatFn) {
329
- const value = this.get(valuePath, item),
330
- change = {
331
- item,
332
- valuePath: this.valuePath,
333
- value: formatFn ? formatFn(value) : value,
334
- oldValue: formatFn ? formatFn(oldValue) : oldValue
335
- };
336
-
337
- this.dispatchEvent(new CustomEvent('column-item-changed', {
338
- bubbles: true,
339
- detail: change
340
- }));
341
- }
342
77
 
343
- getFilterFn() {
344
- if (!this.filter) {
345
- return;
78
+ cellTitleFn(column, item) {
79
+ return getString(column, item);
346
80
  }
347
- if (!Array.isArray(this.filter)) {
348
- return this._applySingleFilter.bind(this, this.filter.toString().toLowerCase());
349
- }
350
- if (this.filter.length === 0) {
351
- return;
352
- }
353
- return this._applyMultiFilter.bind(this, this.filter);
354
- }
355
81
 
356
- resetFilter() {
357
- this.filter = this._getDefaultFilter();
358
- }
359
-
360
- /**
361
- * Returns whether `filterNotify.base` or `filter` is set to a usable value.
362
- * @param {Object|void} filterNotify filter.*
363
- * @returns {Boolean} True if filter or filterNotify.base is set
364
- */
365
- hasFilter(filterNotify) {
366
- const filter = filterNotify == null ? this.filter : filterNotify.base;
367
-
368
- if (filter == null || filter === '') {
369
- return false;
82
+ serializeFilter(column, filter) {
83
+ return serializeFilter(column, filter);
370
84
  }
371
85
 
372
- if (Array.isArray(filter)) {
373
- return filter.length > 0;
86
+ deserializeFilter(column, filter) {
87
+ return filter;
374
88
  }
375
89
 
376
- if (typeof filter === 'object') {
377
- const setFilters = Object.keys(filter).filter(k => filter[k] != null);
378
- return setFilters.length > 0;
90
+ getComparableValue(item, valuePath) {
91
+ return get(item, valuePath);
379
92
  }
380
93
 
381
- return true;
382
- }
383
-
384
- _getDefaultFilter() {
385
- const type = this.get('properties.filter.type');
386
-
387
- if (type === Array) {
388
- return [];
94
+ computeSource() {
95
+ return null;
389
96
  }
390
- return null;
391
- }
392
97
 
393
- _applySingleFilter(filterString, item) {
394
- const value = this.get(this.valuePath, item);
395
- if (value == null) {
396
- return false;
98
+ _propertiesChanged(currentProps, changedProps, oldProps) {
99
+ super._propertiesChanged(currentProps, changedProps, oldProps);
100
+ this.dispatchEvent(new CustomEvent('cosmoz-column-prop-changed', { bubbles: true }));
397
101
  }
398
- return value.toString().toLowerCase().indexOf(filterString) > -1;
399
- }
400
-
401
- _applyMultiFilter(filter, item) {
402
- const filterArray = filter.map(element => element.toString().toLowerCase()),
403
- value = this.get(this.valuePath, item).toString().toLowerCase();
404
-
405
- return filterArray.some(filter => value.indexOf(filter) >= 0);
406
- }
407
-
408
- _titleChanged() {
409
- this.dispatchEvent(new CustomEvent('cosmoz-column-title-changed', {
410
- bubbles: true,
411
- detail: { column: this }
412
- }));
413
- }
414
-
415
- _hiddenChanged() {
416
- this.dispatchEvent(new CustomEvent('cosmoz-column-hidden-changed', {
417
- bubbles: true,
418
- detail: { column: this }
419
- }));
420
- }
421
- _disabledChanged() {
422
- this.dispatchEvent(new CustomEvent('cosmoz-column-disabled-changed', {
423
- bubbles: true,
424
- detail: { column: this }
425
- }));
426
- }
427
-
428
- _editableChanged() {
429
- this.dispatchEvent(new CustomEvent('cosmoz-column-editable-changed', {
430
- bubbles: true,
431
- detail: { column: this }
432
- }));
433
- }
434
-
435
- __filterChanged() {
436
- this.dispatchEvent(new CustomEvent('cosmoz-column-filter-changed', {
437
- bubbles: true,
438
- detail: { column: this }
439
- }));
440
- }
441
-
442
- _propertiesChanged(currentProps, changedProps, oldProps) {
443
- super._propertiesChanged(currentProps, changedProps, oldProps);
444
- // let the repeaters know that this column has been updated and that it needs to re-render the templates
445
- this.dispatchEvent(new CustomEvent('cosmoz-column-prop-changed'));
446
- }
447
-
448
- _serializeFilter(obj = this.filter) {
449
- const type = {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();
450
- let value = obj;
451
-
452
- if (type === 'array' && !value.length) {
453
- value = null;
454
- } else if (type === 'object') {
455
- const keys = Object.keys(obj).filter(k => obj[k] != null);
456
- if (keys.length > 0) {
457
- value = keys.reduce((acc, k) => {
458
- acc[k] = obj[k];
459
- return acc;
460
- }, {});
461
- } else {
462
- value = null;
463
- }
464
- }
465
- return super._serializeValue(value);
466
- }
467
-
468
- _deserializeFilter(obj) {
469
- const type = this.filter && this.filter.constructor || Object;
470
-
471
- if (type === Object && obj == null) {
472
- return {};
473
- }
474
- if (type === Array && obj == null) {
475
- return [];
476
- }
477
- return super._deserializeValue(obj, type);
478
- }
479
-
480
- });
102
+ };