@neovici/cosmoz-omnitable 8.14.3 → 9.1.0

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.
@@ -27,6 +27,8 @@ import { saveAsCsvAction } from './lib/save-as-csv-action';
27
27
  import { saveAsXlsxAction } from './lib/save-as-xlsx-action';
28
28
  import { defaultPlacement } from '@neovici/cosmoz-dropdown';
29
29
  import { without } from '@neovici/cosmoz-utils/lib/array';
30
+ import { indexSymbol } from './lib/utils';
31
+
30
32
  /**
31
33
  * @polymer
32
34
  * @customElement
@@ -36,232 +38,359 @@ import { without } from '@neovici/cosmoz-utils/lib/array';
36
38
  * @demo demo/index.html
37
39
  */
38
40
 
39
- class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translatable(PolymerElement))) {
41
+ class Omnitable extends hauntedPolymer(useOmnitable)(
42
+ mixin({ isEmpty }, translatable(PolymerElement))
43
+ ) {
40
44
  /* eslint-disable-next-line max-lines-per-function */
41
45
  static get template() {
42
46
  const template = html`
43
- ${ html([styles]) }
44
- <div id="layoutStyle"></div>
45
-
46
- <div class="mainContainer">
47
- <sort-and-group-provider value="[[ sortAndGroup ]]">
48
- <div class="header" id="header">
49
- <input class="checkbox all" type="checkbox" checked="[[ _allSelected ]]" on-input="_onAllCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
50
- <cosmoz-omnitable-header-row
51
- data="[[ data ]]"
52
- columns="[[ columns ]]"
53
- filters="[[ filters ]]"
54
- group-on-column="[[ groupOnColumn ]]"
55
- set-filter-state="[[ setFilterState ]]"
56
- settings-config="[[ settingsConfig ]]"
57
- ></cosmoz-omnitable-header-row>
58
- </div>
59
- </sort-and-group-provider>
60
- <div class="tableContent" id="tableContent">
61
- <template is="dom-if" if="[[ !_dataIsValid ]]">
62
- <div class="tableContent-empty">
63
- <slot name="empty-set-message">
47
+ ${html([styles])}
48
+ <div id="layoutStyle"></div>
49
+
50
+ <div class="mainContainer">
51
+ <sort-and-group-provider value="[[ sortAndGroup ]]">
52
+ <div class="header" id="header">
53
+ <input
54
+ class="checkbox all"
55
+ type="checkbox"
56
+ checked="[[ _allSelected ]]"
57
+ on-input="_onAllCheckboxChange"
58
+ disabled$="[[ !_dataIsValid ]]"
59
+ />
60
+ <cosmoz-omnitable-header-row
61
+ data="[[ data ]]"
62
+ columns="[[ columns ]]"
63
+ filters="[[ filters ]]"
64
+ group-on-column="[[ groupOnColumn ]]"
65
+ set-filter-state="[[ setFilterState ]]"
66
+ settings-config="[[ settingsConfig ]]"
67
+ ></cosmoz-omnitable-header-row>
68
+ </div>
69
+ </sort-and-group-provider>
70
+ <div class="tableContent" id="tableContent">
71
+ <template is="dom-if" if="[[ !_dataIsValid ]]">
72
+ <div class="tableContent-empty">
73
+ <slot name="empty-set-message">
74
+ <iron-icon icon="icons:announcement"></iron-icon>
75
+ <div class="tableContent-empty-message">
76
+ <h3>[[ _('Working set empty', t) ]]</h3>
77
+ <p>[[ _('No data to display', t) ]]</p>
78
+ </div>
79
+ </slot>
80
+ </div>
81
+ </template>
82
+ <template is="dom-if" if="[[ _filterIsTooStrict ]]">
83
+ <div class="tableContent-empty">
64
84
  <iron-icon icon="icons:announcement"></iron-icon>
65
- <div class="tableContent-empty-message">
66
- <h3>[[ _('Working set empty', t) ]]</h3>
67
- <p>[[ _('No data to display', t) ]]</p>
85
+ <div>
86
+ <h3>[[ _('Filter too strict', t) ]]</h3>
87
+ <p>[[ _('No matches for selection', t) ]]</p>
68
88
  </div>
69
- </slot>
70
- </div>
71
- </template>
72
- <template is="dom-if" if="[[ _filterIsTooStrict ]]">
73
- <div class="tableContent-empty">
74
- <iron-icon icon="icons:announcement"></iron-icon>
75
- <div>
76
- <h3>[[ _('Filter too strict', t) ]]</h3>
77
- <p>[[ _('No matches for selection', t) ]]</p>
78
89
  </div>
79
- </div>
80
- </template>
81
- <template is="dom-if" if="[[ loading ]]">
82
- <div class="tableContent-empty overlay">
83
- <paper-spinner-lite active="[[ loading ]]"></paper-spinner-lite>
84
- <div>
85
- <h3>[[ _('Data set is updating', t) ]]</h3>
90
+ </template>
91
+ <template is="dom-if" if="[[ loading ]]">
92
+ <div class="tableContent-empty overlay">
93
+ <paper-spinner-lite active="[[ loading ]]"></paper-spinner-lite>
94
+ <div>
95
+ <h3>[[ _('Data set is updating', t) ]]</h3>
96
+ </div>
86
97
  </div>
98
+ </template>
99
+ <div class="tableContent-scroller" id="scroller">
100
+ <cosmoz-grouped-list
101
+ id="groupedList"
102
+ data="{{ sortedFilteredGroupedItems }}"
103
+ selected-items="{{ selectedItems }}"
104
+ display-empty-groups="[[ displayEmptyGroups ]]"
105
+ compare-items-fn="[[ compareItemsFn ]]"
106
+ render-item="[[ renderItem(collapsedColumns) ]]"
107
+ render-group="[[ renderGroup ]]"
108
+ ></cosmoz-grouped-list>
87
109
  </div>
88
- </template>
89
- <div class="tableContent-scroller" id="scroller">
90
- <cosmoz-grouped-list id="groupedList"
91
- data="{{ sortedFilteredGroupedItems }}"
92
- selected-items="{{ selectedItems }}"
93
- display-empty-groups="[[ displayEmptyGroups ]]"
94
- compare-items-fn="[[ compareItemsFn ]]"
95
- >
96
- <template slot="templates" data-type="item">
97
- <div class="item-row-wrapper">
98
- <div selected$="[[ selected ]]" part="itemRow itemRow-[[ index ]]" data-index$="[[ index ]]" class="itemRow" on-click="onItemClick">
99
- <input class="checkbox" type="checkbox" checked="[[ selected ]]" on-input="_onCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
100
- <cosmoz-omnitable-item-row columns="[[ columns ]]"
101
- selected="[[ selected ]]" expanded="{{ expanded }}" item="[[ item ]]" group-on-column="[[ groupOnColumn ]]"
102
- on-item-change="[[ onItemChange ]]">
103
- </cosmoz-omnitable-item-row>
104
- <paper-icon-button
105
- class="expand"
106
- hidden="[[ isEmpty(collapsedColumns.length) ]]"
107
- icon="[[ _getFoldIcon(expanded) ]]"
108
- on-tap="_toggleItem"
109
- ></paper-icon-button>
110
- </div>
111
- <cosmoz-omnitable-item-expand columns="[[ collapsedColumns ]]"
112
- item="[[item]]" selected="{{ selected }}" expanded$="{{ expanded }}" group-on-column="[[ groupOnColumn ]]"
113
- part="item-expand" on-expanded="onExpanded">
114
- </cosmoz-omnitable-item-expand>
115
- </div>
116
- </template>
117
- <template slot="templates" data-type="group">
118
- <div class$="[[ _getGroupRowClasses(folded) ]]">
119
- <input class="checkbox" type="checkbox" checked="[[ selected ]]" on-input="_onCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
120
- <h3 class="groupRow-label">
121
- <div><span>[[ groupOnColumn.title ]]</span>: &nbsp;</div>
122
- <cosmoz-omnitable-group-row column="[[ groupOnColumn ]]" item="[[ item.items.0 ]]" selected="[[ selected ]]" folded="[[ folded ]]"
123
- group="[[ item ]]"
124
- ></cosmoz-omnitable-group-row>
125
- </h3>
126
- <div class="groupRow-badge">[[ item.items.length ]]</div>
127
- <paper-icon-button class="fold" icon="[[ _getFoldIcon(folded) ]]" on-tap="_toggleGroup"></paper-icon-button>
128
- </div>
129
- </template>
130
- </cosmoz-grouped-list>
131
110
  </div>
132
- </div>
133
- <div class="footer">
134
- <div class="footer-controls" part="footer-controls">
135
- <cosmoz-autocomplete
136
- label="[[ _('Group on', t) ]] [[ _computeSortDirection(groupOnDescending, t) ]]" placeholder="[[ _('No grouping', t) ]]"
137
- source="[[ _onCompleteValues(columns, 'groupOn', groupOnColumn) ]]" value="[[ groupOnColumn ]]" limit="1" text-property="title"
138
- always-float-label item-height="48" item-limit="8"
139
- class="footer-control" on-change="[[ _onCompleteChange('groupOn') ]]" on-select="[[ _onCompleteSelect ]]" default-index="-1" show-single
140
- >
141
- <svg slot="suffix" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" width="24" fill="currentColor"><path d="M7 10l5 5 5-5z"></path></svg>
142
- </cosmoz-autocomplete>
143
- <cosmoz-autocomplete
144
- label="[[ _('Sort on', t) ]] [[ _computeSortDirection(descending, t) ]]" placeholder="[[ _('No sorting', t) ]]"
145
- source="[[ _onCompleteValues(columns, 'sortOn', sortOnColumn) ]]" value="[[ sortOnColumn ]]" limit="1" text-property="title"
146
- always-float-label item-height="48" item-limit="8"
147
- class="footer-control" on-change="[[ _onCompleteChange('sortOn') ]]" on-select="[[ _onCompleteSelect ]]" default-index="-1" show-single
111
+ <div class="footer">
112
+ <div class="footer-controls" part="footer-controls">
113
+ <cosmoz-autocomplete
114
+ label="[[ _('Group on', t) ]] [[ _computeSortDirection(groupOnDescending, t) ]]"
115
+ placeholder="[[ _('No grouping', t) ]]"
116
+ source="[[ _onCompleteValues(columns, 'groupOn', groupOnColumn) ]]"
117
+ value="[[ groupOnColumn ]]"
118
+ limit="1"
119
+ text-property="title"
120
+ always-float-label
121
+ item-height="48"
122
+ item-limit="8"
123
+ class="footer-control"
124
+ on-change="[[ _onCompleteChange('groupOn') ]]"
125
+ on-select="[[ _onCompleteSelect ]]"
126
+ default-index="-1"
127
+ show-single
128
+ >
129
+ <svg
130
+ slot="suffix"
131
+ viewBox="0 0 24 24"
132
+ preserveAspectRatio="xMidYMid meet"
133
+ focusable="false"
134
+ width="24"
135
+ fill="currentColor"
136
+ >
137
+ <path d="M7 10l5 5 5-5z"></path>
138
+ </svg>
139
+ </cosmoz-autocomplete>
140
+ <cosmoz-autocomplete
141
+ label="[[ _('Sort on', t) ]] [[ _computeSortDirection(descending, t) ]]"
142
+ placeholder="[[ _('No sorting', t) ]]"
143
+ source="[[ _onCompleteValues(columns, 'sortOn', sortOnColumn) ]]"
144
+ value="[[ sortOnColumn ]]"
145
+ limit="1"
146
+ text-property="title"
147
+ always-float-label
148
+ item-height="48"
149
+ item-limit="8"
150
+ class="footer-control"
151
+ on-change="[[ _onCompleteChange('sortOn') ]]"
152
+ on-select="[[ _onCompleteSelect ]]"
153
+ default-index="-1"
154
+ show-single
155
+ >
156
+ <svg
157
+ slot="suffix"
158
+ viewBox="0 0 24 24"
159
+ preserveAspectRatio="xMidYMid meet"
160
+ focusable="false"
161
+ width="24"
162
+ fill="currentColor"
163
+ >
164
+ <path d="M7 10l5 5 5-5z"></path>
165
+ </svg>
166
+ </cosmoz-autocomplete>
167
+ <slot id="controlsSlot" name="controls"></slot>
168
+ </div>
169
+ <div class="footer-tableStats">
170
+ <span
171
+ >[[ ngettext('{0} group', '{0} groups', groupsCount, t) ]]</span
172
+ >
173
+ <span
174
+ >[[ _renderRowStats(numProcessedItems, totalAvailable, t) ]]</span
175
+ >
176
+ </div>
177
+ <cosmoz-bottom-bar
178
+ id="bottomBar"
179
+ class="footer-actionBar"
180
+ match-parent
181
+ on-action="_onAction"
182
+ active$="[[ !isEmpty(selectedItems.length) ]]"
148
183
  >
149
- <svg slot="suffix" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" width="24" fill="currentColor"><path d="M7 10l5 5 5-5z"></path></svg>
150
- </cosmoz-autocomplete>
151
- <slot id="controlsSlot" name="controls"></slot>
152
- </div>
153
- <div class="footer-tableStats">
154
- <span>[[ ngettext('{0} group', '{0} groups', groupsCount, t) ]]</span>
155
- <span>[[ _renderRowStats(numProcessedItems, totalAvailable, t) ]]</span>
156
- </div>
157
- <cosmoz-bottom-bar id="bottomBar" class="footer-actionBar" match-parent
158
- on-action="_onAction" active$="[[ !isEmpty(selectedItems.length) ]]">
159
- <slot name="info" slot="info">[[ ngettext('{0} selected item', '{0} selected items', selectedItems.length, t) ]]</slot>
160
- <slot name="actions" id="actions"></slot>
161
- <!-- These slots are needed by cosmoz-bottom-bar
184
+ <slot name="info" slot="info"
185
+ >[[ ngettext('{0} selected item', '{0} selected items',
186
+ selectedItems.length, t) ]]</slot
187
+ >
188
+ <slot name="actions" id="actions"></slot>
189
+ <!-- These slots are needed by cosmoz-bottom-bar
162
190
  as it might change the slot of the actions to distribute them in the menu -->
163
- <slot name="bottom-bar-toolbar" slot="bottom-bar-toolbar"></slot>
164
- <slot name="bottom-bar-menu" slot="bottom-bar-menu"></slot>
165
- <cosmoz-dropdown-menu slot="extra" placement="[[ topPlacement ]]">
166
- <svg slot="button" width="14" height="18" viewBox="0 0 14 18" fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg">
167
- <path d="M1 8.5L7.00024 14.5L13 8.5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
168
- <path d="M13 17L1 17" stroke-width="2" stroke-linecap="round"/>
169
- <path d="M7 1V13" stroke-width="2" stroke-linecap="round"/>
170
- </svg>
171
- <button on-click="_saveAsCsvAction">[[ _('Save as CSV', t) ]]</button>
172
- <button on-click="_saveAsXlsxAction">[[ _('Save as XLSX', t) ]]</button>
173
- <slot name="download-menu"></slot>
174
- </cosmoz-dropdown-menu>
175
- </cosmoz-bottom-bar>
191
+ <slot name="bottom-bar-toolbar" slot="bottom-bar-toolbar"></slot>
192
+ <slot name="bottom-bar-menu" slot="bottom-bar-menu"></slot>
193
+ <cosmoz-dropdown-menu slot="extra" placement="[[ topPlacement ]]">
194
+ <svg
195
+ slot="button"
196
+ width="14"
197
+ height="18"
198
+ viewBox="0 0 14 18"
199
+ fill="none"
200
+ stroke="currentColor"
201
+ xmlns="http://www.w3.org/2000/svg"
202
+ >
203
+ <path
204
+ d="M1 8.5L7.00024 14.5L13 8.5"
205
+ stroke-width="2"
206
+ stroke-linecap="round"
207
+ stroke-linejoin="round"
208
+ />
209
+ <path d="M13 17L1 17" stroke-width="2" stroke-linecap="round" />
210
+ <path d="M7 1V13" stroke-width="2" stroke-linecap="round" />
211
+ </svg>
212
+ <button on-click="_saveAsCsvAction">
213
+ [[ _('Save as CSV', t) ]]
214
+ </button>
215
+ <button on-click="_saveAsXlsxAction">
216
+ [[ _('Save as XLSX', t) ]]
217
+ </button>
218
+ <slot name="download-menu"></slot>
219
+ </cosmoz-dropdown-menu>
220
+ </cosmoz-bottom-bar>
221
+ </div>
176
222
  </div>
177
- </div>
178
223
 
179
- <div id="columns">
180
- <slot id="columnsSlot"></slot>
181
- </div>
182
- `;
224
+ <div id="columns">
225
+ <slot id="columnsSlot"></slot>
226
+ </div>
227
+ `;
183
228
  template.setAttribute('strip-whitespace', '');
184
229
  return template;
185
230
  }
186
231
 
232
+ renderItem(collapsedColumns) {
233
+ return (
234
+ item,
235
+ index,
236
+ { selected, expanded, toggleCollapse }
237
+ ) => {
238
+ return litHtml`
239
+ <div class="item-row-wrapper">
240
+ <div ?selected=${selected}
241
+ part="itemRow itemRow-${item[indexSymbol]}"
242
+ .dataIndex=${item[indexSymbol]}
243
+ .dataItem=${item}
244
+ class="itemRow"
245
+ @click=${this.onItemClick}
246
+ >
247
+ <input class="checkbox"
248
+ type="checkbox"
249
+ .checked=${selected}
250
+ .dataItem=${item}
251
+ @input=${this._onCheckboxChange}
252
+ ?disabled=${!this._dataIsValid} />
253
+ <cosmoz-omnitable-item-row
254
+ .columns=${this.columns}
255
+ .index=${index}
256
+ .selected=${selected}
257
+ .expanded=${expanded}
258
+ .item=${item}
259
+ .groupOnColumn=${this.groupOnColumn}
260
+ .onItemChange=${this.onItemChange}>
261
+ </cosmoz-omnitable-item-row>
262
+ <paper-icon-button
263
+ class="expand"
264
+ ?hidden=${isEmpty(collapsedColumns.length)}
265
+ .icon=${this._getFoldIcon(expanded)}
266
+ @click=${toggleCollapse}
267
+ ></paper-icon-button>
268
+ </div>
269
+ <cosmoz-omnitable-item-expand .columns=${collapsedColumns}
270
+ .item=${item}
271
+ .index=${index}
272
+ ?selected=${selected}
273
+ ?expanded=${expanded}
274
+ .groupOnColumn=${this.groupOnColumn}
275
+ part="item-expand">
276
+ </cosmoz-omnitable-item-expand>
277
+ </div>`;
278
+ };
279
+ }
280
+
281
+ renderGroup(item, index, { selected, folded, toggleFold }) {
282
+ return litHtml`
283
+ <div class="${this._getGroupRowClasses(folded)}"
284
+ part="groupRow groupRow-${item[indexSymbol]}">
285
+ <input class="checkbox"
286
+ type="checkbox"
287
+ .checked=${selected}
288
+ .dataItem=${item}
289
+ @input=${this._onCheckboxChange}
290
+ ?disabled=${!this._dataIsValid} />
291
+ <h3 class="groupRow-label">
292
+ <div><span>${this.groupOnColumn?.title}</span>: &nbsp;</div>
293
+ <cosmoz-omnitable-group-row
294
+ .column=${this.groupOnColumn}
295
+ .item=${item.items?.[0]}
296
+ .selected=${selected}
297
+ .folded=${folded}
298
+ .group=${item}
299
+ ></cosmoz-omnitable-group-row>
300
+ </h3>
301
+ <div class="groupRow-badge">${item.items.length}</div>
302
+ <paper-icon-button
303
+ class="fold"
304
+ .icon=${this._getFoldIcon(folded)}
305
+ @click=${toggleFold}></paper-icon-button>
306
+ </div>`;
307
+ }
308
+
187
309
  /* eslint-disable-next-line max-lines-per-function */
188
310
  static get properties() {
189
311
  return {
190
312
  /**
191
- * Filename when saving as CSV
192
- */
313
+ * Filename when saving as CSV
314
+ */
193
315
  csvFilename: { type: String, value: 'omnitable.csv' },
194
316
 
195
317
  /**
196
- * Filename when saving as XLSX
197
- */
318
+ * Filename when saving as XLSX
319
+ */
198
320
  xlsxFilename: { type: String, value: 'omnitable.xlsx' },
199
321
 
200
322
  /**
201
- * Sheet name when saving as XLSX
202
- */
323
+ * Sheet name when saving as XLSX
324
+ */
203
325
  xlsxSheetname: { type: String, value: 'Omnitable' },
204
326
 
205
327
  /**
206
- * Array used to list items.
207
- */
328
+ * Array used to list items.
329
+ */
208
330
  data: { type: Array },
209
331
 
210
332
  /**
211
333
  * This function is used to determine which items are kept selected across data updates
212
- * TODO: probably broken
213
334
  */
214
335
  compareItemsFn: Function,
215
336
 
216
337
  /**
217
- * True if data is a valid and not empty array.
218
- */
219
- _dataIsValid: { type: Boolean, value: false, computed: '_computeDataValidity(data.*)' },
338
+ * True if data is a valid and not empty array.
339
+ */
340
+ _dataIsValid: {
341
+ type: Boolean,
342
+ value: false,
343
+ computed: '_computeDataValidity(data.*)',
344
+ },
220
345
 
221
346
  /**
222
- * If set to true, then group a row will be displayed for groups that contain no items.
223
- */
347
+ * If set to true, then group a row will be displayed for groups that contain no items.
348
+ */
224
349
  displayEmptyGroups: { type: Boolean, value: false },
225
350
 
226
351
  /**
227
- * Specific columns to enable
228
- */
352
+ * Specific columns to enable
353
+ */
229
354
  enabledColumns: { type: Array, notify: true },
230
355
 
231
356
  /**
232
- * Whether bottom-bar has actions.
233
- */
357
+ * Whether bottom-bar has actions.
358
+ */
234
359
  hasActions: { type: Boolean, value: false },
235
360
 
236
361
  /**
237
- * Shows a loading overlay to indicate data will be updated
238
- */
362
+ * Shows a loading overlay to indicate data will be updated
363
+ */
239
364
  loading: { type: Boolean, value: false },
240
365
 
241
366
  /**
242
- * List of selected rows/items in `data`.
243
- */
244
- selectedItems: { type: Array, notify: true },
367
+ * List of selected rows/items in `data`.
368
+ */
369
+ selectedItems: { type: Array, notify: true, value: () => [] },
245
370
  descending: { type: Boolean, value: false, notify: true },
246
371
  sortOn: { type: String, value: '', notify: true },
247
372
  groupOnDescending: { type: Boolean, value: false },
248
373
 
249
374
  /**
250
- * The column name to group on.
251
- */
375
+ * The column name to group on.
376
+ */
252
377
  groupOn: { type: String, notify: true, value: '' },
253
378
 
254
379
  /**
255
- * Sorted items structure after filtering and grouping.
256
- */
380
+ * Sorted items structure after filtering and grouping.
381
+ */
257
382
  sortedFilteredGroupedItems: { type: Array, notify: true },
258
383
 
259
384
  /**
260
- * List of columns definition for this table.
261
- */
262
- columns: { type: Array, notify: true, value: () => []},
385
+ * List of columns definition for this table.
386
+ */
387
+ columns: { type: Array, notify: true, value: () => [] },
263
388
  settings: { type: Object, notify: true },
264
- _filterIsTooStrict: { type: Boolean, computed: '_computeFilterIsTooStrict(_dataIsValid, sortedFilteredGroupedItems.length)' },
389
+ _filterIsTooStrict: {
390
+ type: Boolean,
391
+ computed:
392
+ '_computeFilterIsTooStrict(_dataIsValid, sortedFilteredGroupedItems.length)',
393
+ },
265
394
  hashParam: { type: String },
266
395
 
267
396
  /**
@@ -271,21 +400,22 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
271
400
  computedBarHeight: { type: Number },
272
401
  settingsId: { type: String, value: undefined },
273
402
  topPlacement: {
274
- value: ['top-right', ...defaultPlacement]
275
- }
403
+ value: ['top-right', ...defaultPlacement],
404
+ },
276
405
  };
277
406
  }
278
407
 
279
408
  static get observers() {
280
- return [
281
- '_selectedItemsChanged(selectedItems.*)'
282
- ];
409
+ return ['_selectedItemsChanged(selectedItems.*)'];
283
410
  }
284
411
 
285
412
  constructor() {
286
413
  super();
287
414
 
288
415
  this._onKey = this._onKey.bind(this);
416
+ this._onCheckboxChange = this._onCheckboxChange.bind(this);
417
+ this.renderItem = this.renderItem.bind(this);
418
+ this.renderGroup = this.renderGroup.bind(this);
289
419
  }
290
420
 
291
421
  connectedCallback() {
@@ -293,7 +423,6 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
293
423
 
294
424
  this.$.groupedList.scrollTarget = this.$.scroller;
295
425
 
296
- this.addEventListener('update-item-size', this._onUpdateItemSize);
297
426
  window.addEventListener('keydown', this._onKey);
298
427
  window.addEventListener('keyup', this._onKey);
299
428
  }
@@ -301,7 +430,6 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
301
430
  disconnectedCallback() {
302
431
  super.disconnectedCallback();
303
432
 
304
- this.removeEventListener('update-item-size', this._onUpdateItemSize);
305
433
  window.removeEventListener('keydown', this._onKey);
306
434
  window.removeEventListener('keyup', this._onKey);
307
435
  }
@@ -318,15 +446,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
318
446
 
319
447
  _computeSortDirection(descending) {
320
448
  const direction = descending ? this._('Descending') : this._('Ascending');
321
- return `(${ direction })`;
322
- }
323
-
324
- _onUpdateItemSize(event) {
325
- const { detail } = event;
326
- if (detail && detail.item) {
327
- this.$.groupedList.updateSize(detail.item);
328
- }
329
- event.stopPropagation();
449
+ return `(${direction})`;
330
450
  }
331
451
 
332
452
  _onKey(e) {
@@ -335,7 +455,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
335
455
  }
336
456
 
337
457
  _onCheckboxChange(event) {
338
- const item = event.model.item,
458
+ const item = event.target.dataItem,
339
459
  selected = event.target.checked;
340
460
  if (this._shiftKey) {
341
461
  this.$.groupedList.toggleSelectTo(item, selected);
@@ -363,7 +483,12 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
363
483
  * @returns {undefined}
364
484
  */
365
485
  _saveAsXlsxAction() {
366
- saveAsXlsxAction(this.columns, this.selectedItems, this.xlsxFilename, this.xlsxSheetname);
486
+ saveAsXlsxAction(
487
+ this.columns,
488
+ this.selectedItems,
489
+ this.xlsxFilename,
490
+ this.xlsxSheetname
491
+ );
367
492
  }
368
493
 
369
494
  /** view functions */
@@ -375,23 +500,6 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
375
500
  return expanded ? 'expand-less' : 'expand-more';
376
501
  }
377
502
 
378
- /**
379
- * Toggle folding of a group
380
- * @param {Event} event event
381
- * @returns {undefined}
382
- */
383
- _toggleGroup(event) {
384
- this.$.groupedList.toggleFold(event.model.item);
385
- }
386
-
387
- _toggleItem(event) {
388
- const item = event.model.item;
389
- this.$.groupedList.toggleCollapse(item);
390
- }
391
-
392
- onExpanded() {
393
- requestAnimationFrame(() => this.$.groupedList.$.list._render());
394
- }
395
503
  /**
396
504
  * Turn an `action` event into a `run` event
397
505
  * @param {Event} event `action` event
@@ -399,20 +507,28 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
399
507
  * @returns {undefined}
400
508
  */
401
509
  _onAction(event, detail) {
402
- detail.item.dispatchEvent(new window.CustomEvent('run', {
403
- bubbles: true,
404
- cancelable: true,
405
- detail: {
406
- omnitable: this,
407
- items: this.selectedItems
408
- }
409
- }));
510
+ detail.item.dispatchEvent(
511
+ new window.CustomEvent('run', {
512
+ bubbles: true,
513
+ cancelable: true,
514
+ detail: {
515
+ omnitable: this,
516
+ items: this.selectedItems,
517
+ },
518
+ })
519
+ );
410
520
  event.stopPropagation();
411
521
  }
412
522
 
413
523
  _selectedItemsChanged(change) {
414
- if (change.path === 'selectedItems' || change.path === 'selectedItems.splices') {
415
- this._allSelected = this.data && this.data.length > 0 && change.base.length === this.data.length;
524
+ if (
525
+ change.path === 'selectedItems' ||
526
+ change.path === 'selectedItems.splices'
527
+ ) {
528
+ this._allSelected =
529
+ this.data &&
530
+ this.data.length > 0 &&
531
+ change.base.length === this.data.length;
416
532
  }
417
533
  }
418
534
 
@@ -426,20 +542,6 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
426
542
 
427
543
  // TODO: move to publicInterface mixin
428
544
  /** PUBLIC */
429
-
430
- suppressNextScrollReset() {
431
- const list = this.$.groupedList.$.list;
432
- // HACK: Replace _resetScrollPosition for one call to maintain scroll position
433
- if (list._scrollTop > 0 && !list._resetScrollPosition.suppressed) {
434
- const reset = list._resetScrollPosition;
435
- list._resetScrollPosition = () => {
436
- // restore hack
437
- list._resetScrollPosition = reset;
438
- };
439
- list._resetScrollPosition.suppressed = true;
440
- }
441
- }
442
-
443
545
  /**
444
546
  * Remove multiple items from `data`
445
547
  * @param {Array} items Array of items to remove
@@ -481,7 +583,6 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
481
583
  }
482
584
  }
483
585
  replaceItemAtIndex(index, newItem) {
484
- this.suppressNextScrollReset();
485
586
  this.splice('data', index, 1, newItem);
486
587
  this.data = this.data.slice();
487
588
  }
@@ -513,16 +614,26 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
513
614
 
514
615
  _renderRowStats(numRows, totalAvailable) {
515
616
  if (Number.isInteger(totalAvailable) && totalAvailable > numRows) {
516
- return this.ngettext('{1} / {0} row', '{1} / {0} rows', totalAvailable, numRows);
617
+ return this.ngettext(
618
+ '{1} / {0} row',
619
+ '{1} / {0} rows',
620
+ totalAvailable,
621
+ numRows
622
+ );
517
623
  }
518
624
  return this.ngettext('{0} row', '{0} rows', numRows);
519
625
  }
520
626
 
521
- _onCompleteValues(columns, type, value) { /* eslint-disable-next-line no-bitwise */
522
- return columns?.filter?.(c => c[type]).sort((a, b) => ((b === value) >> 0) - ((a === value) >> 0));
627
+ _onCompleteValues(columns, type, value) {
628
+ return (
629
+ columns
630
+ ?.filter?.((c) => c[type])
631
+ /* eslint-disable-next-line no-bitwise */
632
+ .sort((a, b) => ((b === value) >> 0) - ((a === value) >> 0))
633
+ );
523
634
  }
524
635
 
525
- _onCompleteSelect(newVal, {value, onChange, onText, limit}) {
636
+ _onCompleteSelect(newVal, { value, onChange, onText, limit }) {
526
637
  onText('');
527
638
  onChange([...without(newVal)(value), newVal].slice(-limit));
528
639
  }
@@ -531,11 +642,14 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
531
642
  return (val, close) => {
532
643
  const value = (val[0] ?? val)?.name ?? '',
533
644
  setter = type === 'groupOn' ? this.setGroupOn : this.setSortOn,
534
- directionSetter = type === 'groupOn' ? this.setGroupOnDescending : this.setDescending;
645
+ directionSetter =
646
+ type === 'groupOn' ? this.setGroupOnDescending : this.setDescending;
535
647
 
536
- setter(oldValue => {
648
+ setter((oldValue) => {
537
649
  if (value) {
538
- directionSetter(oldDirection => value === oldValue ? !oldDirection : false);
650
+ directionSetter((oldDirection) =>
651
+ value === oldValue ? !oldDirection : false
652
+ );
539
653
  } else {
540
654
  directionSetter(null);
541
655
  }
@@ -554,14 +668,13 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
554
668
  return;
555
669
  }
556
670
 
557
-
558
671
  this.dispatchEvent(
559
672
  new window.CustomEvent('omnitable-item-click', {
560
673
  bubbles: true,
561
674
  composed: true,
562
675
  detail: {
563
- item: e.model.item,
564
- index: e.model.index,
676
+ item: e.currentTarget.dataItem,
677
+ index: e.currentTarget.dataIndex,
565
678
  },
566
679
  })
567
680
  );
@@ -575,6 +688,5 @@ const tmplt = `
575
688
  <slot name="bottom-bar-menu" slot="bottom-bar-menu"></slot>
576
689
  `;
577
690
 
578
- export const
579
- actionSlots = litHtml([tmplt]),
691
+ export const actionSlots = litHtml([tmplt]),
580
692
  actionSlotsPolymer = html([tmplt]);