@neovici/cosmoz-omnitable 7.1.0 → 7.3.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.
@@ -248,19 +248,13 @@ export const rangeColumnMixin = dedupingMixin(base => // eslint-disable-line max
248
248
  }
249
249
 
250
250
  _applySingleFilter(filter, item) {
251
- const value = this.getComparableValue(item, this.valuePath),
252
- minValue = this.getComparableValue(filter, 'min'),
253
- maxValue = this.getComparableValue(filter, 'max');
254
-
255
- if (value < minValue) {
256
- return false;
257
- }
258
-
259
- if (value > maxValue) {
251
+ const value = this.getComparableValue(item, this.valuePath);
252
+ if (value == null) {
260
253
  return false;
261
254
  }
262
-
263
- return true;
255
+ const min = this.getComparableValue(filter, 'min'),
256
+ max = this.getComparableValue(filter, 'max');
257
+ return !(value < min || value > max);
264
258
  }
265
259
 
266
260
  _computeFilterText(change) {
@@ -1,91 +1,16 @@
1
- import { PolymerElement } from '@polymer/polymer/polymer-element';
2
- import { html } from '@polymer/polymer/lib/utils/html-tag';
1
+ import { component, useMemo } from 'haunted';
2
+ import { nothing } from 'lit-html';
3
+ import { useRenderOnColumnUpdates } from './lib/use-render-on-column-updates';
3
4
 
4
- import { repeaterMixin } from './cosmoz-omnitable-repeater-mixin';
5
+ const GroupRow = ({ column, item, selected, folded }) => {
6
+ const columns = useMemo(() => column ? [column] : [], [column]);
7
+ useRenderOnColumnUpdates(columns);
5
8
 
6
- /**
7
- * @polymer
8
- * @customElement
9
- * @appliesMixin repeaterMixin
10
- */
11
- class OmnitableGroupRow extends repeaterMixin(PolymerElement) {
12
- static get template() {
13
- return html`
14
- <slot name="group-row"></slot>
15
- `;
9
+ if (!column) {
10
+ return nothing;
16
11
  }
17
12
 
18
- static get is() {
19
- return 'cosmoz-omnitable-group-row';
20
- }
21
-
22
- static get properties() {
23
- return {
24
- column: {
25
- type: Object,
26
- observer: '_columnChanged'
27
- },
28
-
29
- item: {
30
- type: Object
31
- },
32
-
33
- selected: {
34
- type: Boolean,
35
- observer: '_selectedChanged'
36
- },
37
-
38
- folded: {
39
- type: Boolean,
40
- observer: '_foldedChanged'
41
- }
42
- };
43
- }
44
-
45
- static get observers() {
46
- return [
47
- '_itemUpdated(item.*)'
48
- ];
49
- }
50
-
51
- get _elementType() {
52
- return 'div';
53
- }
54
-
55
- get _slotName() {
56
- return 'group-row';
57
- }
13
+ return (column.renderGroup ?? column.renderCell)(column, { item, selected, folded });
14
+ };
58
15
 
59
- constructor() {
60
- super();
61
- this.trackColumns();
62
- }
63
-
64
- _columnChanged(newColumn) {
65
- if (!newColumn) {
66
- return;
67
- }
68
- if (this.columns && this.columns.length > 0) {
69
- this.splice('columns', 0, 1, newColumn);
70
- return;
71
- }
72
- this.columns = [newColumn];
73
- }
74
-
75
- _getRenderFn(column) {
76
- return column.renderGroup ?? column.renderCell;
77
- }
78
-
79
- _itemUpdated(changeRecord) {
80
- this.forwardPathChange(changeRecord);
81
- }
82
-
83
- _selectedChanged(selected) {
84
- this.forwardChange('selected', selected);
85
- }
86
-
87
- _foldedChanged(folded) {
88
- this.forwardChange('folded', folded);
89
- }
90
- }
91
- customElements.define(OmnitableGroupRow.is, OmnitableGroupRow);
16
+ customElements.define('cosmoz-omnitable-group-row', component(GroupRow, { useShadowDOM: false }));
@@ -1,129 +1,23 @@
1
- import { PolymerElement } from '@polymer/polymer/polymer-element';
2
- import { html } from '@polymer/polymer/lib/utils/html-tag';
3
-
4
- import { repeaterMixin } from './cosmoz-omnitable-repeater-mixin';
5
-
6
- /**
7
- * @polymer
8
- * @customElement
9
- * @appliesMixin repeaterMixin
10
- */
11
- class OmnitableItemRow extends repeaterMixin(PolymerElement) {
12
- static get template() {
13
- return html`
14
- <style>
15
- :host {
16
- display: flex;
17
- white-space: nowrap;
18
- }
19
-
20
- :host > ::slotted(*) {
21
- display: block;
22
- flex: none;
23
- padding: 0 3px;
24
- white-space: nowrap;
25
- overflow: hidden;
26
- text-overflow: ellipsis;
27
- box-sizing: border-box;
28
- align-self: center;
29
- }
30
-
31
- :host > ::slotted([hidden]),
32
- :host [hidden] {
33
- display: none !important;
34
- }
35
- </style>
36
- <slot name="item-cell"></slot>
37
- `;
38
- }
39
-
40
- static get is() {
41
- return 'cosmoz-omnitable-item-row';
42
- }
43
-
44
- static get properties() {
45
- return {
46
- item: {
47
- type: Object
48
- },
49
-
50
- selected: {
51
- type: Boolean,
52
- observer: '_selectedChanged'
53
- },
54
-
55
- expanded: {
56
- type: Boolean,
57
- observer: '_expandedChanged'
58
- }
59
- };
60
- }
61
-
62
- static get observers() {
63
- return [
64
- '_itemUpdated(item.*)'
65
- ];
66
- }
67
-
68
- get _elementType() {
69
- return 'div';
70
- }
71
-
72
- get _slotName() {
73
- return 'item-cell';
74
- }
75
-
76
- constructor() {
77
- super();
78
- this.trackColumns();
79
- }
80
-
81
- _getRenderFn(column) {
82
- return column.editable
83
- ? column.renderEditCell
84
- : column.renderCell;
85
- }
86
-
87
- /**
88
- * @inheritdoc
89
- */
90
- _configureElement(element, column, instance) {
91
- super._configureElement(element, column, instance);
92
- element.style.minHeight = '0.5px';
93
- element.toggleAttribute('editable', !!column.editable);
94
- element.setAttribute('title', this._getCellTitle(column, this.item));
95
- element.setAttribute('class', this._computeItemRowCellClasses(column));
96
- element.setAttribute('name', column.name);
97
- }
98
-
99
- _itemUpdated(changeRecord) {
100
- this.forwardPathChange(changeRecord);
101
- this.forEachElement((element, column) => {
102
- element.setAttribute('title', this._getCellTitle(column, this.item));
1
+ import { component, html } from 'haunted';
2
+ import { repeat } from 'lit-html/directives/repeat';
3
+ import { useRenderOnColumnUpdates } from './lib/use-render-on-column-updates';
4
+
5
+ const
6
+ renderCell = (column, data) => column.editable
7
+ ? column.renderEditCell(column, data)
8
+ : column.renderCell(column, data),
9
+ ItemRow = ({ columns, groupOnColumn, item, selected, expanded }) => {
10
+ useRenderOnColumnUpdates(columns);
11
+
12
+ return repeat(columns, column => column.name, column => {
13
+ return html`<div
14
+ class="cell itemRow-cell ${ column.cellClass ?? '' }"
15
+ ?hidden=${ column === groupOnColumn }
16
+ ?editable=${ column.editable }
17
+ title=${ column.cellTitleFn(item, column.valuePath) }
18
+ name=${ column.name }
19
+ >${ renderCell(column, { item, selected, expanded }) }</div>`;
103
20
  });
104
- }
105
-
106
- _selectedChanged(selected) {
107
- this.forwardChange('selected', selected);
108
- }
109
-
110
- _expandedChanged(expanded) {
111
- this.forwardChange('expanded', expanded);
112
- }
113
-
114
- /**
115
- * Get cell title displayed when hovering on the cell.
116
- * @param {object} column Column data.
117
- * @param {object} item Row item.
118
- * @returns {string} Cell title.
119
- */
120
- _getCellTitle(column, item) {
121
- return column && column.cellTitleFn(item, column.valuePath);
122
- }
21
+ };
123
22
 
124
- _computeItemRowCellClasses(column) {
125
- return 'cell itemRow-cell'
126
- + (column.cellClass ? ' ' + column.cellClass + ' ' : '');
127
- }
128
- }
129
- customElements.define(OmnitableItemRow.is, OmnitableItemRow);
23
+ customElements.define('cosmoz-omnitable-item-row', component(ItemRow, { useShadowDOM: false }));
@@ -21,7 +21,7 @@ const checkbox = `
21
21
  }
22
22
 
23
23
  .checkbox:checked {
24
- background-color: var(--paper-checkbox-checked-color, var(--primary-color));
24
+ background-color: var(--cosmoz-omnitable-checkbox-checked-color, var(--primary-color));
25
25
  box-shadow: none;
26
26
  }
27
27
 
@@ -62,7 +62,7 @@ const checkbox = `
62
62
  height: 2px;
63
63
  left: 4px;
64
64
  top: 8px;
65
- background-color: var(--paper-checkbox-checked-color, var(--primary-color));
65
+ background-color: var(--cosmoz-omnitable-checkbox-checked-color, var(--primary-color));
66
66
  }
67
67
  `;
68
68
 
@@ -160,6 +160,27 @@ export default `<style>
160
160
  position: relative;
161
161
  }
162
162
 
163
+
164
+ cosmoz-omnitable-item-row {
165
+ display: flex;
166
+ white-space: nowrap;
167
+ }
168
+
169
+ cosmoz-omnitable-item-row > div {
170
+ display: block;
171
+ flex: none;
172
+ padding: 0 3px;
173
+ white-space: nowrap;
174
+ overflow: hidden;
175
+ text-overflow: ellipsis;
176
+ box-sizing: border-box;
177
+ align-self: center;
178
+ }
179
+
180
+ cosmoz-omnitable-item-row > div[hidden] {
181
+ display: none !important;
182
+ }
183
+
163
184
  .tableContent {
164
185
  overflow-y: auto;
165
186
  min-height: 90px;
@@ -260,7 +281,7 @@ export default `<style>
260
281
  display: flex;
261
282
  align-items: center;
262
283
  border-top: solid 1px #e8e8e8;
263
- background-color: #f5f5f5;
284
+ background-color: var(--cosmoz-omnitable-footer-bg-color, #f5f5f5);
264
285
  min-height: 25px;
265
286
  height: 63px;
266
287
  margin-bottom: 1px; /* Chrome overflow rendering bug? When only component in a view */
@@ -3,7 +3,6 @@ import '@polymer/iron-icons/iron-icons';
3
3
  import '@polymer/iron-icon/iron-icon';
4
4
  import '@polymer/iron-label/iron-label';
5
5
  import '@polymer/paper-button/paper-button';
6
- import '@polymer/paper-checkbox/paper-checkbox';
7
6
  import '@polymer/paper-dropdown-menu/paper-dropdown-menu';
8
7
  import '@polymer/paper-icon-button/paper-icon-button';
9
8
  import '@polymer/paper-item/paper-item';
@@ -37,6 +36,7 @@ import { mixin, hauntedPolymer } from '@neovici/cosmoz-utils';
37
36
  import { isEmpty } from '@neovici/cosmoz-utils/lib/template.js';
38
37
  import { useOmnitable } from './lib/use-omnitable';
39
38
  import './lib/cosmoz-omnitable-settings';
39
+ import { genericSorter } from './lib/generic-sorter';
40
40
 
41
41
  const PROPERTY_HASH_PARAMS = ['sortOn', 'groupOn', 'descending', 'groupOnDescending'];
42
42
 
@@ -64,7 +64,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
64
64
  <cosmoz-omnitable-header-row
65
65
  columns="[[ normalizedColumns ]]"
66
66
  group-on-column="[[ groupOnColumn ]]"
67
- content="[[ _renderSettings(normalizedSettings, collapsedColumns, settingsId, hasChangedSettings) ]]"
67
+ content="[[ _renderSettings(normalizedSettings, collapsedColumns, settingsId, hasChangedSettings, hasHiddenFilter) ]]"
68
68
  >
69
69
  </div>
70
70
  <div class="tableContent" id="tableContent">
@@ -377,7 +377,8 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
377
377
  */
378
378
  columns: {
379
379
  type: Array,
380
- notify: true
380
+ notify: true,
381
+ value: () => []
381
382
  },
382
383
 
383
384
  settings: {
@@ -455,6 +456,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
455
456
  window.addEventListener('keydown', this._onKey);
456
457
  window.addEventListener('keyup', this._onKey);
457
458
  this._resizeObserver.observe(this);
459
+ this._updateParamsFromHash();
458
460
  }
459
461
 
460
462
  disconnectedCallback() {
@@ -521,7 +523,6 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
521
523
  }
522
524
 
523
525
  _onColumnTitleChanged(event) {
524
-
525
526
  event.stopPropagation();
526
527
 
527
528
  if (!Array.isArray(this.columns)) {
@@ -729,16 +730,11 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
729
730
  * @param {String} attribute The attribute name of the column.
730
731
  * @returns {Object} The found column.
731
732
  */
732
- _getColumn(attributeValue, attribute = 'name', columns = this.columns) {
733
+ _getColumn(attributeValue, attribute = 'name', columns) {
733
734
  if (!attributeValue || !columns) {
734
735
  return;
735
736
  }
736
- const column = columns.find(column => column[attribute] === attributeValue);
737
- if (!column) {
738
- // eslint-disable-next-line no-console
739
- console.warn(`Cannot find column with ${ attribute } ${ attributeValue }`);
740
- }
741
- return column;
737
+ return columns.find(column => column[attribute] === attributeValue);
742
738
  }
743
739
 
744
740
  _filterChanged({ detail }) {
@@ -749,12 +745,8 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
749
745
  this._filterForRouteChanged(detail.column);
750
746
  }
751
747
 
752
- _groupOnColumnChanged(column) {
753
- if (column && column.hasFilter()) {
754
- column.resetFilter();
755
- } else {
756
- this._debounceProcessItems();
757
- }
748
+ _groupOnColumnChanged() {
749
+ this._debounceProcessItems();
758
750
  }
759
751
 
760
752
  _debounceProcessItems() {
@@ -837,7 +829,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
837
829
  const v1 = groupOnColumn.getComparableValue(a.items[0], groupOnColumn.groupOn),
838
830
  v2 = groupOnColumn.getComparableValue(b.items[0], groupOnColumn.groupOn);
839
831
 
840
- return this._genericSorter(v1, v2);
832
+ return genericSorter(v1, v2);
841
833
  });
842
834
 
843
835
  if (this.groupOnDescending) {
@@ -848,42 +840,6 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
848
840
  this.filteredGroupedItems = groups;
849
841
  }
850
842
 
851
- /* eslint-disable-next-line max-statements */
852
- _genericSorter(a, b) {
853
- if (a === b) {
854
- return 0;
855
- }
856
-
857
- if (a === undefined) {
858
- return -1;
859
- }
860
-
861
- if (b === undefined) {
862
- return 1;
863
- }
864
-
865
- if (typeof a === 'object' && typeof b === 'object') {
866
- // HACK(pasleq): worst case, compare using values converted to string
867
- return a.toString() < b.toString() ? -1 : 1;
868
- }
869
-
870
- if (typeof a === 'number' && typeof b === 'number') {
871
- return a - b;
872
- }
873
-
874
- if (typeof a === 'string' && typeof b === 'string') {
875
- return a < b ? -1 : 1;
876
- }
877
-
878
- if (typeof a === 'boolean' && typeof b === 'boolean') {
879
- return a ? -1 : 1;
880
- }
881
-
882
- // eslint-disable-next-line no-console
883
- console.warn('unsupported sort', typeof a, a, typeof b, b);
884
- return 0;
885
- }
886
-
887
843
  /**
888
844
  * compareFunction for sort(), can be overridden
889
845
  * @see Array.prototype.sort()
@@ -896,7 +852,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
896
852
  const v1 = this.sortOnColumn.getComparableValue(a, this.sortOnColumn.sortOn),
897
853
  v2 = this.sortOnColumn.getComparableValue(b, this.sortOnColumn.sortOn);
898
854
 
899
- return this._genericSorter(v1, v2);
855
+ return genericSorter(v1, v2);
900
856
  }
901
857
 
902
858
  /* eslint-disable-next-line max-statements */
@@ -1173,14 +1129,9 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
1173
1129
  const column = this.columns.find(c => c.name === key);
1174
1130
 
1175
1131
  if (!column) {
1176
- // eslint-disable-next-line no-console
1177
- console.warn('column with name', name, 'for param', key, 'not found!');
1178
1132
  return;
1179
1133
  }
1180
1134
 
1181
- if (column === this.groupOnColumn) {
1182
- return;
1183
- }
1184
1135
  if (value === column._serializeFilter()) {
1185
1136
  return;
1186
1137
  }
@@ -1285,7 +1236,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
1285
1236
  };
1286
1237
  }
1287
1238
 
1288
- _renderSettings(normalizedSettings, collapsed, settingsId, hasChangedSettings) {
1239
+ _renderSettings(normalizedSettings, collapsed, settingsId, hasChangedSettings, hasHiddenFilter) {
1289
1240
  return litHtml`<cosmoz-omnitable-settings
1290
1241
  .settings=${ normalizedSettings }
1291
1242
  .onSettings=${ this.setSettings }
@@ -1294,6 +1245,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
1294
1245
  .hasChanges=${ hasChangedSettings }
1295
1246
  .onSave=${ this.onSettingsSave }
1296
1247
  .onReset=${ this.onSettingsReset }
1248
+ .badge=${ hasHiddenFilter }
1297
1249
  >`;
1298
1250
  }
1299
1251
  }
@@ -5,6 +5,7 @@ import { portal } from '@neovici/cosmoz-utils/lib/directives/portal';
5
5
  import { useMeta } from '@neovici/cosmoz-utils/lib/hooks/use-meta';
6
6
  import { checkbox } from '../cosmoz-omnitable-styles';
7
7
  import { nothing } from 'lit-html';
8
+ import { columnSymbol } from './normalize-settings';
8
9
  const settingsStyles = `
9
10
  :host {
10
11
  position: fixed;
@@ -54,6 +55,9 @@ const settingsStyles = `
54
55
  .title {
55
56
  flex: 1;
56
57
  }
58
+ .title[has-filter] {
59
+ font-weight: bold;
60
+ }
57
61
  ${ checkbox }
58
62
  .footer {
59
63
  display: flex;
@@ -177,7 +181,7 @@ const settingsStyles = `
177
181
  <button class="sort" mousedown="">
178
182
  <svg viewBox="0 0 24 24" width="24"><path d="M20 9H4v2h16V9zM4 15h16v-2H4v2z"></path></svg>
179
183
  </button>
180
- <label class="title">${ column.title }</label>
184
+ <label class="title" ?has-filter=${ !!column[columnSymbol].getFilterFn() }>${ column.title }</label>
181
185
  <input class="checkbox" type="checkbox" .checked=${ checked } @click=${ onToggle }
182
186
  .indeterminate=${ indeterminate } .windeterminate=${ indeterminate }
183
187
  >
@@ -201,7 +205,7 @@ const settingsStyles = `
201
205
  },
202
206
 
203
207
  Settings = host => {
204
- const { settings, onSettings, onSave, onReset, collapsed, settingsId, hasChanges } = host,
208
+ const { settings, onSettings, onSave, onReset, collapsed, settingsId, hasChanges, badge } = host,
205
209
  { active, onFocus, onToggle } = useFocus(host),
206
210
  anchor = useCallback(() => host.shadowRoot.querySelector('.anchor'), []);
207
211
 
@@ -216,14 +220,25 @@ const settingsStyles = `
216
220
  outline: none;
217
221
  background: transparent;
218
222
  cursor: pointer;
223
+ position: relative;
219
224
  }
220
225
  .anchor * { pointer-events: none; }
221
226
  .anchor:hover {
222
227
  background: #eee;
223
228
  }
229
+ .badge {
230
+ position: absolute;
231
+ top: 1px;
232
+ right: -4px;
233
+ background-color: var(--cosmoz-omnitable-checkbox-checked-color, var(--primary-color));
234
+ width: 8px;
235
+ height: 8px;
236
+ border-radius: 100%;
237
+ }
224
238
  </style>
225
- <button class="anchor" @focus=${ onFocus } @blur=${ onFocus } @click=${ onToggle } @mousedown=${ preventDefault }>
239
+ <button class="anchor" @focus=${ onFocus } @blur=${ onFocus } @click=${ onToggle } @mousedown=${ preventDefault }>
226
240
  <svg viewBox="0 0 24 24" width="24"><path d="M10 18h5V5h-5v13zm-6 0h5V5H4v13zM16 5v13h5V5h-5z"></path></svg>
241
+ ${ badge ? html`<div class="badge"></div>` : nothing }
227
242
  </button>
228
243
  ${ active
229
244
  ? portal(html`<cosmoz-omnitable-settings-ui
@@ -0,0 +1,35 @@
1
+ // eslint-disable-next-line max-statements
2
+ export const genericSorter = (a, b) => {
3
+ if (a === b) {
4
+ return 0;
5
+ }
6
+
7
+ if (a === undefined) {
8
+ return -1;
9
+ }
10
+
11
+ if (b === undefined) {
12
+ return 1;
13
+ }
14
+
15
+ if (typeof a === 'object' && typeof b === 'object') {
16
+ // HACK(pasleq): worst case, compare using values converted to string
17
+ return a.toString() < b.toString() ? -1 : 1;
18
+ }
19
+
20
+ if (typeof a === 'number' && typeof b === 'number') {
21
+ return a - b;
22
+ }
23
+
24
+ if (typeof a === 'string' && typeof b === 'string') {
25
+ return a < b ? -1 : 1;
26
+ }
27
+
28
+ if (typeof a === 'boolean' && typeof b === 'boolean') {
29
+ return a ? -1 : 1;
30
+ }
31
+
32
+ // eslint-disable-next-line no-console
33
+ console.warn('unsupported sort', typeof a, a, typeof b, b);
34
+ return 0;
35
+ };
@@ -6,12 +6,26 @@ import { useSavedSettings } from './use-saved-settings';
6
6
  // eslint-disable-next-line max-lines-per-function
7
7
  export const useOmnitable = host => {
8
8
  const
9
- { columns, groupOnColumn, resizeSpeedFactor, settingsId } = host,
9
+ { columns, groupOnColumn, resizeSpeedFactor, settingsId, sortedFilteredGroupedItems } = host,
10
10
  [settings, setSettings] = useState([]),
11
11
  { savedSettings, onSettingsSave, onSettingsReset, hasChangedSettings } = useSavedSettings(settingsId, settings, setSettings),
12
12
  normalizedSettings = useMemo(() => normalizeSettings(columns, settings, savedSettings), [columns, settings, savedSettings]),
13
13
  normalizedColumns = useMemo(() => normalizedSettings.map(s => s[columnSymbol]), [columns, ...normalizedSettings.map(s => s.name)]),
14
- { layoutCss, collapsedColumns } = useFastLayout({ host, settings: normalizedSettings, setSettings, groupOnColumn, resizeSpeedFactor });
14
+ { layoutCss, collapsedColumns } = useFastLayout({ host, settings: normalizedSettings, setSettings, groupOnColumn, resizeSpeedFactor }),
15
+ filterFunctions = useMemo(() => {
16
+ if (!columns) {
17
+ return {};
18
+ }
19
+ return Object.fromEntries(columns
20
+ .map(col => [col.name, col.getFilterFn()])
21
+ .filter(([, fn]) => fn !== undefined));
22
+ }, [sortedFilteredGroupedItems]),
23
+ columnsWithFilters = useMemo(() => Object.keys(filterFunctions), [filterFunctions]),
24
+ hasHiddenFilter = useMemo(
25
+ () => [groupOnColumn, ...collapsedColumns, ...normalizedSettings.filter(s => s.disabled)]
26
+ .some(column => column && columnsWithFilters.includes(column.name)),
27
+ [columnsWithFilters, normalizedSettings, collapsedColumns]
28
+ );
15
29
 
16
30
  return {
17
31
  layoutCss,
@@ -19,6 +33,7 @@ export const useOmnitable = host => {
19
33
  normalizedSettings,
20
34
  normalizedColumns,
21
35
  collapsedColumns,
36
+ hasHiddenFilter,
22
37
  onSettingsSave,
23
38
  onSettingsReset,
24
39
  hasChangedSettings
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useMemo, useState } from 'haunted';
2
- import { omit } from '@neovici/cosmoz-utils/lib/object';
2
+ import { props } from '@neovici/cosmoz-utils/lib/object';
3
3
 
4
4
  const storagePrefix = 'omnitable-';
5
5
 
@@ -27,7 +27,7 @@ export const useSavedSettings = (settingsId, settings, setSettings) => {
27
27
  }
28
28
 
29
29
  try {
30
- localStorage.setItem(storagePrefix + settingsId, JSON.stringify(settings.map(omit(['title', 'minWidth']))));
30
+ localStorage.setItem(storagePrefix + settingsId, JSON.stringify(settings.map(props(['name', 'priority', 'width', 'flex', 'disabled']))));
31
31
  setSettings([]);
32
32
  setCounter(counter => counter + 1);
33
33
  } catch (e) {
@@ -36,8 +36,17 @@ export const useSavedSettings = (settingsId, settings, setSettings) => {
36
36
  }
37
37
  }, [settings]),
38
38
 
39
- onSettingsReset: () => {
39
+ onSettingsReset: e => {
40
40
  setSettings([]);
41
+
42
+ if (e.shiftKey) {
43
+ try {
44
+ localStorage.removeItem(storagePrefix + settingsId);
45
+ setCounter(counter => counter + 1);
46
+ } catch (err) {
47
+ // ignore error
48
+ }
49
+ }
41
50
  },
42
51
 
43
52
  hasChangedSettings: settings.length !== 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neovici/cosmoz-omnitable",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "description": "[![Build Status](https://travis-ci.org/Neovici/cosmoz-omnitable.svg?branch=master)](https://travis-ci.org/Neovici/cosmoz-omnitable)",
5
5
  "keywords": [
6
6
  "web-components"
@@ -64,7 +64,6 @@
64
64
  "@polymer/iron-icons": "^3.0.0",
65
65
  "@polymer/iron-label": "^3.0.0",
66
66
  "@polymer/paper-button": "^3.0.0",
67
- "@polymer/paper-checkbox": "^3.0.0",
68
67
  "@polymer/paper-dropdown-menu": "^3.2.0",
69
68
  "@polymer/paper-icon-button": "^3.0.0",
70
69
  "@polymer/paper-input": "^3.2.0",
@@ -1,294 +0,0 @@
1
- import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin';
2
- import { render } from 'lit-html';
3
- import { set } from '@polymer/polymer/lib/utils/path';
4
-
5
- /**
6
- * Allows manipulation of elements.
7
- * @callback OmnitableRepeaterMixin~forEachElementCallback
8
- * @param {HTMLElement} element
9
- * @param {OmnitableColumn} column
10
- * @param {TemplateInstance} instance
11
- * @return {void}
12
- */
13
-
14
- const editableRegex = /columns\.(\d*)\.editable/iu;
15
-
16
- /**
17
- *
18
- * @polymer
19
- * @mixinFunction
20
- */
21
- export const repeaterMixin = dedupingMixin(base => class extends base { // eslint-disable-line max-lines-per-function
22
- static get properties() {
23
- return {
24
- columns: {
25
- type: Array
26
- },
27
-
28
- groupOnColumn: {
29
- type: Object,
30
- observer: '_groupOnColumnChanged'
31
- }
32
- };
33
- }
34
-
35
- constructor() {
36
- super();
37
- this._elements = [];
38
- this._columnsObserver = null;
39
- }
40
-
41
- /**
42
- * Adds an observer to render the cells when the columns are changed.
43
- * @return {void}
44
- */
45
- trackColumns() {
46
- if (this._columnsObserver) {
47
- throw new Error('The columns are already tracked.');
48
- }
49
-
50
- this._createMethodObserver('_columnsChanged(columns.*)');
51
-
52
- // HACK we have to muck around with the property effect references because
53
- // _createMethodObserver does not provide any reference to the property
54
- // effect so we can easily remove it
55
- this._columnsObserver = this[this.PROPERTY_EFFECT_TYPES.OBSERVE]['columns']
56
- .find(fx => fx.info.methodName === '_columnsChanged');
57
- }
58
-
59
- /**
60
- * Stops reacting to column changes.
61
- * @return {void}
62
- */
63
- stopTrackingColumns() {
64
- if (!this._columnsObserver) {
65
- throw new Error('The columns are not tracked.');
66
- }
67
-
68
- this._removePropertyEffect('columns', this.PROPERTY_EFFECT_TYPES.OBSERVE, this._columnsObserver);
69
- this._columnsObserver = null;
70
- }
71
-
72
- /**
73
- * Renders all cells.
74
- * @return {void}
75
- */
76
- renderCells() {
77
- this._addElements(0, this.columns.length);
78
- }
79
-
80
- /**
81
- * Destroys all cells.
82
- * @return {void}
83
- */
84
- destroyCells() {
85
- this._removeElements(0, this.columns);
86
- }
87
-
88
- /**
89
- * Forwards a property change to all cells.
90
- * @param {String} property the property
91
- * @param {any} value the new value
92
- * @return {void}
93
- */
94
- forwardChange(property, value) {
95
- this.forwardPathChange({
96
- path: property,
97
- value
98
- });
99
- }
100
-
101
- /**
102
- * Forwards a path change to all cells.
103
- * @param {Object} changeRecord the change record
104
- * @return {void}
105
- */
106
- forwardPathChange(changeRecord) {
107
- this._elements.forEach(element =>
108
- this._forwardNotifyPath(element.__instance, changeRecord.path, changeRecord.value, true)
109
- );
110
- }
111
-
112
- /**
113
- * Runs a callback on each element.
114
- * @param {OmnitableRepeaterMixin~forEachElementCallback} callback the callback
115
- * @return {void}
116
- */
117
- forEachElement(callback) {
118
- this._elements.forEach(element => callback(element, element.__column, element.__instance));
119
- }
120
-
121
- /* eslint-disable no-empty-function, no-unused-vars */
122
-
123
- /**
124
- * The type of element to be repeated.
125
- * Must be defined in implementors.
126
- * @return {String} The type of element to be repeated.
127
- */
128
- get _elementType() {
129
- return null;
130
- }
131
-
132
- /**
133
- * Slot name assigned to the repeated elements.
134
- * Must be defined in implementors.
135
- * @return {String} Slot name assigned to the repeated elements.
136
- */
137
- get _slotName() {
138
- return null;
139
- }
140
-
141
- /**
142
- * Get a render function for the specified column
143
- * Must be defined in implementors.
144
- * @abstract
145
- * @param {OmnitableColumnMixin} column - The column.
146
- * @return {Function} - The render function.
147
- */
148
- _getRenderFn(column) {}
149
-
150
- /* eslint-enable no-empty-function, no-unused-vars */
151
-
152
- /**
153
- * Configure a newly created repeated element
154
- *
155
- * @param {HTMLElement} element the root cell element
156
- * @param {OmnitableColumnMixin} column the column
157
- * @param {TemplateInstance} instance the template instance
158
- * @return {void}
159
- */
160
- _configureElement(element, column, instance) {
161
- element.__instance = instance;
162
- element.__column = column;
163
-
164
- if (column === this.groupOnColumn) {
165
- element.setAttribute('hidden', true);
166
- }
167
-
168
- element.setAttribute('slot', this._slotName);
169
-
170
- column.addEventListener('cosmoz-column-prop-changed', instance.render);
171
-
172
- element.__cleanup = () => {
173
- element.__instance = element.__column = element.column = null;
174
- column.removeEventListener('cosmoz-column-prop-changed', instance.render);
175
- };
176
- }
177
-
178
- _columnsChanged({
179
- path, base, value
180
- }) {
181
- const reMatch = editableRegex.exec(path);
182
-
183
- if (path === 'columns') {
184
- if (this._elements && this._elements.length) {
185
- const removedColumns = this._elements.map(element => {
186
- return element.__column;
187
- });
188
- this._removeElements(0, removedColumns);
189
- } else {
190
- this._elements = [];
191
- }
192
- this._addElements(0, this.columns.length);
193
- } else if (path === 'columns.splices') {
194
- this._renderSplices(value.indexSplices);
195
- } else if (reMatch) {
196
- // eslint-disable-next-line no-bitwise
197
- const index = reMatch[1] >> 0,
198
- column = base[index];
199
- this._removeElements(index, [column]);
200
- this._addElements(index, 1);
201
- } else if (path !== 'columns.length') {
202
- // column property change
203
- // eslint-disable-next-line no-console
204
- console.warn('column property change', path);
205
- }
206
- }
207
-
208
- _renderSplices(splices) {
209
- splices.forEach(splice => {
210
- if (splice.removed.length) {
211
- this._removeElements(splice.index, splice.removed);
212
- }
213
- if (splice.addedCount > 0) {
214
- this._addElements(splice.index, splice.addedCount);
215
- }
216
- });
217
- }
218
-
219
- _getInstance(updateFn) {
220
- const state = {
221
- item: this.item,
222
- selected: this.selected,
223
- expanded: this.expanded,
224
- folded: this.folded
225
- };
226
- updateFn(state);
227
-
228
- return {
229
- _setPendingPropertyOrPath(path, value) {
230
- set(state, path, value);
231
- },
232
- _flushProperties() {
233
- updateFn(state);
234
- },
235
- render() {
236
- updateFn(state);
237
- }
238
- };
239
- }
240
-
241
- _addElements(start, count) {
242
- const end = start + count;
243
- for (let i = start; i < end; i++) {
244
- const element = document.createElement(this._elementType),
245
- column = this.columns[i],
246
- renderFn = this._getRenderFn(column),
247
- instance = this._getInstance(state => render(renderFn(column, state), element));
248
-
249
- this._configureElement(element, column, instance);
250
-
251
- if (i < this._elements.length) {
252
- this.insertBefore(element, this._elements[i]);
253
- } else {
254
- this.appendChild(element);
255
- }
256
-
257
- this._elements.splice(i, 0, element);
258
- }
259
- }
260
-
261
- _removeElements(start, removedColumns) {
262
- this._elements
263
- .splice(start, removedColumns.length)
264
- .forEach(element => {
265
- element.__cleanup();
266
- this.removeChild(element);
267
- });
268
- }
269
-
270
- _groupOnColumnChanged(column, previousColumn) {
271
- if (!this._elements || !this._elements.length) {
272
- return;
273
- }
274
- if (previousColumn) {
275
- const previousElement = this._elements.find(e => e.__column === previousColumn);
276
- if (previousElement) {
277
- previousElement.removeAttribute('hidden');
278
- }
279
- }
280
- if (column) {
281
- const element = this._elements.find(e => e.__column === column);
282
- if (element) {
283
- element.setAttribute('hidden', '');
284
- }
285
- }
286
- }
287
-
288
- _forwardNotifyPath(instance, path, value, flush = false) {
289
- instance._setPendingPropertyOrPath(path, value, false, true);
290
- if (flush) {
291
- instance._flushProperties(true);
292
- }
293
- }
294
- });