@neovici/cosmoz-omnitable 8.11.0 → 8.13.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.
package/README.md CHANGED
@@ -2,6 +2,5 @@ cosmoz-omnitable
2
2
  =================
3
3
 
4
4
  [![Build Status](https://github.com/Neovici/cosmoz-omnitable/workflows/Github%20CI/badge.svg)](https://github.com/Neovici/cosmoz-omnitable/actions?workflow=Github+CI)
5
- [![Maintainability](https://api.codeclimate.com/v1/badges/6b16292868f47977eee2/maintainability)](https://codeclimate.com/github/Neovici/cosmoz-omnitable/maintainability)
6
5
  [![codecov](https://codecov.io/gh/Neovici/cosmoz-omnitable/branch/master/graph/badge.svg?token=j46iVMxjcs)](https://codecov.io/gh/Neovici/cosmoz-omnitable)
7
6
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
@@ -46,7 +46,7 @@ class OmnitableColumnAutocomplete extends listColumnMixin(columnMixin(PolymerEle
46
46
  renderHeader(column, { filter, query }, setState, source) {
47
47
  const
48
48
  spinner = column.loading
49
- ? html`<paper-spinner-lite style="width: 20px; height: 20px;" suffix slot="suffix" active></paper-spinner-lite>`
49
+ ? html`<paper-spinner-lite style="width: 20px; height: 20px; flex: none;" suffix slot="suffix" active></paper-spinner-lite>`
50
50
  : nothing;
51
51
 
52
52
  return html`<cosmoz-autocomplete-ui
@@ -36,7 +36,7 @@ class OmnitableColumnList extends listColumnMixin(columnMixin(PolymerElement)) {
36
36
  renderHeader(column, { filter, query }, setState, source) {
37
37
  const
38
38
  spinner = column.loading
39
- ? html`<paper-spinner-lite style="width: 20px; height: 20px;" suffix slot="suffix" active></paper-spinner-lite>`
39
+ ? html`<paper-spinner-lite style="width: 20px; height: 20px; flex:none;" suffix slot="suffix" active></paper-spinner-lite>`
40
40
  : nothing;
41
41
 
42
42
  return html`<cosmoz-autocomplete-ui
@@ -44,6 +44,7 @@ class OmnitableColumnList extends listColumnMixin(columnMixin(PolymerElement)) {
44
44
  .label=${ column.title }
45
45
  .source=${ source }
46
46
  .textProperty=${ column.textProperty }
47
+ .valueProperty=${ column.valueProperty }
47
48
  .value=${ filter }
48
49
  .text=${ query }
49
50
  .onChange=${ onChange(setState) }
@@ -1,7 +1,6 @@
1
1
  import { html, component } from 'haunted';
2
2
  import { repeat } from 'lit-html/directives/repeat';
3
3
  import './lib/cosmoz-omnitable-resize-nub';
4
- import sort from './lib/cosmoz-omnitable-sort';
5
4
 
6
5
  const /* eslint-disable-next-line max-lines-per-function */
7
6
  renderHeaderRow = ({
@@ -28,7 +27,6 @@ const /* eslint-disable-next-line max-lines-per-function */
28
27
  (state) => setFilterState(column.name, state),
29
28
  column.source(column, data)
30
29
  ),
31
- sort(column.name),
32
30
  ]}
33
31
  </div>`,
34
32
  html`<cosmoz-omnitable-resize-nub
@@ -1,119 +1,80 @@
1
1
  /* eslint-disable max-lines */
2
2
  import { tagged as css } from '@neovici/cosmoz-utils';
3
3
  const checkbox = css`
4
- .checkbox {
5
- box-sizing: border-box;
6
- width: 18px;
7
- height: 18px;
8
- background: transparent;
9
- border-radius: 4px;
10
- appearance: none;
11
- -webkit-appearance: none;
12
- outline: none;
13
- position: relative;
14
- user-select: none;
15
- cursor: pointer;
16
- display: inline-block;
17
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.16) inset;
18
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
19
- vertical-align: middle;
20
- transition: background-color 140ms;
21
- margin: 1px 12px;
22
- flex: none;
23
- }
24
-
25
- .checkbox:checked {
26
- background-color: var(
27
- --cosmoz-omnitable-checkbox-checked-color,
28
- var(--primary-color)
29
- );
30
- box-shadow: none;
31
- }
32
-
33
- .checkbox:checked::before {
34
- content: '';
35
- position: absolute;
36
- box-sizing: content-box;
37
- width: 5px;
38
- height: 10px;
39
- border: 2.4px solid #fff;
40
- border-top: none;
41
- border-left: none;
42
- transform-origin: 5px 10px;
43
- transform: translate(3px) rotate(45deg);
44
- }
45
-
46
- .checkbox::after {
47
- content: '';
48
- display: block;
49
- bottom: -5px;
50
- left: -5px;
51
- right: -5px;
52
- top: -5px;
53
- }
54
-
55
- .checkbox:hover {
56
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 1) inset, 0 0 2px 6px #2021240f;
57
- }
58
-
59
- .checkbox:checked:hover {
60
- box-shadow: 0 0 2px 6px #2021240f;
61
- }
62
-
63
- .checkbox:indeterminate::before {
64
- content: '';
65
- position: absolute;
66
- width: 10px;
67
- height: 2px;
68
- left: 4px;
69
- top: 8px;
70
- background-color: var(
71
- --cosmoz-omnitable-checkbox-checked-color,
72
- var(--primary-color)
73
- );
74
- }
75
- `,
76
- sort = css`
77
- .sort {
78
- display: inline-flex;
79
- width: 10px;
80
- cursor: pointer;
81
- align-items: center;
82
- margin-top: 18px;
83
- overflow: hidden;
84
- flex: none;
85
- background: none;
86
- border: none;
87
- outline: none;
88
- color: inherit;
89
- padding: 0;
90
- }
91
- .sort svg {
92
- display: block;
93
- }
94
- .sort[data-sort] {
95
- color: var(
96
- --cosmoz-omnitable-checkbox-checked-color,
97
- var(--primary-color)
98
- );
99
- }
100
- .sort:not([data-sort='desc']) {
101
- transform: scaleY(-1);
102
- }
103
- :not(:hover) > * > .sort:not([data-sort]) {
104
- display: none;
105
- }
106
- .header-cell {
107
- display: inline-flex;
108
- position: relative;
109
- }
110
- .header-cell :not(.sort) {
111
- min-width: 0;
112
- flex: auto;
113
- }
114
- `;
115
-
116
- export { checkbox, sort };
4
+ .checkbox {
5
+ box-sizing: border-box;
6
+ width: 18px;
7
+ height: 18px;
8
+ background: transparent;
9
+ border-radius: 4px;
10
+ appearance: none;
11
+ -webkit-appearance: none;
12
+ outline: none;
13
+ position: relative;
14
+ user-select: none;
15
+ cursor: pointer;
16
+ display: inline-block;
17
+ box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.16) inset;
18
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
19
+ vertical-align: middle;
20
+ transition: background-color 140ms;
21
+ margin: 1px 12px;
22
+ flex: none;
23
+ }
24
+
25
+ .checkbox:checked {
26
+ background-color: var(
27
+ --cosmoz-omnitable-checkbox-checked-color,
28
+ var(--primary-color)
29
+ );
30
+ box-shadow: none;
31
+ }
32
+
33
+ .checkbox:checked::before {
34
+ content: '';
35
+ position: absolute;
36
+ box-sizing: content-box;
37
+ width: 5px;
38
+ height: 10px;
39
+ border: 2.4px solid #fff;
40
+ border-top: none;
41
+ border-left: none;
42
+ transform-origin: 5px 10px;
43
+ transform: translate(3px) rotate(45deg);
44
+ }
45
+
46
+ .checkbox::after {
47
+ content: '';
48
+ display: block;
49
+ bottom: -5px;
50
+ left: -5px;
51
+ right: -5px;
52
+ top: -5px;
53
+ }
54
+
55
+ .checkbox:hover {
56
+ box-shadow: 0 0 0 2px rgba(0, 0, 0, 1) inset, 0 0 2px 6px #2021240f;
57
+ }
58
+
59
+ .checkbox:checked:hover {
60
+ box-shadow: 0 0 2px 6px #2021240f;
61
+ }
62
+
63
+ .checkbox:indeterminate::before {
64
+ content: '';
65
+ position: absolute;
66
+ width: 10px;
67
+ height: 2px;
68
+ left: 4px;
69
+ top: 8px;
70
+ background-color: var(
71
+ --cosmoz-omnitable-checkbox-checked-color,
72
+ var(--primary-color)
73
+ );
74
+ }
75
+ `;
76
+
77
+ export { checkbox };
117
78
 
118
79
  export default `<style>
119
80
  :host {
@@ -520,5 +481,4 @@ export default `<style>
520
481
  .expand:hover, .fold:hover {
521
482
  color: #000;
522
483
  }
523
- ${sort}
524
484
  </style>`;
@@ -50,7 +50,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
50
50
  <input class="checkbox all" type="checkbox" checked="[[ _allSelected ]]" on-input="_onAllCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
51
51
  <cosmoz-omnitable-header-row
52
52
  data="[[ data ]]"
53
- columns="[[ normalizedColumns ]]"
53
+ columns="[[ columns ]]"
54
54
  filters="[[ filters ]]"
55
55
  group-on-column="[[ groupOnColumn ]]"
56
56
  set-filter-state="[[ setFilterState ]]"
@@ -98,7 +98,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
98
98
  <div class="item-row-wrapper">
99
99
  <div selected$="[[ selected ]]" class="itemRow">
100
100
  <input class="checkbox" type="checkbox" checked="[[ selected ]]" on-input="_onCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
101
- <cosmoz-omnitable-item-row columns="[[ normalizedColumns ]]"
101
+ <cosmoz-omnitable-item-row columns="[[ columns ]]"
102
102
  selected="[[ selected ]]" expanded="{{ expanded }}" item="[[ item ]]" group-on-column="[[ groupOnColumn ]]"
103
103
  on-item-change="[[ onItemChange ]]">
104
104
  </cosmoz-omnitable-item-row>
@@ -2,43 +2,50 @@ import { html } from 'haunted';
2
2
  import { ifDefined } from 'lit-html/directives/if-defined';
3
3
  import { triangle } from '../icons';
4
4
 
5
+ export const render = ({ columns, on, descending, setOn, setDescending }) =>
6
+ columns?.map(
7
+ (c) =>
8
+ html`<button
9
+ class="sg"
10
+ title=${c.title}
11
+ data-on=${ifDefined(
12
+ (c.name === on && (descending ? 'desc' : 'asc')) || undefined
13
+ )}
14
+ @click=${(e) => {
15
+ const on = e.currentTarget?.dataset.on;
16
+ if (!on) {
17
+ setOn(c.name);
18
+ setDescending(false);
19
+ }
20
+ if (on === 'asc') {
21
+ setDescending(true);
22
+ } else if (on === 'desc') {
23
+ setOn();
24
+ setDescending(false);
25
+ }
26
+ }}
27
+ >
28
+ <span>${c.title}</span> ${triangle}
29
+ </button>`
30
+ );
31
+
5
32
  export default () => html`
6
33
  <sort-and-group-consumer
7
- class="groups"
34
+ class="sgs"
8
35
  .render=${({
9
36
  columns,
37
+ groupOn: on,
38
+ setGroupOn: setOn,
10
39
  groupOnDescending: descending,
11
- groupOn,
12
- setGroupOn,
13
- setGroupOnDescending,
14
- } = {}) => {
15
- const items = columns?.filter?.((c) => c['groupOn']);
16
- return items?.map(
17
- (c) =>
18
- html`<button
19
- class="group"
20
- title=${c.title}
21
- data-group=${ifDefined(
22
- (c.name === groupOn && (descending ? 'desc' : 'asc')) || undefined
23
- )}
24
- @click=${(e) => {
25
- const group = e.currentTarget?.dataset.group;
26
- if (!group) {
27
- setGroupOn(c.name);
28
- setGroupOnDescending(false);
29
- }
30
- if (group === 'asc') {
31
- setGroupOnDescending(true);
32
- } else if (group === 'desc') {
33
- setGroupOn();
34
- setGroupOnDescending(false);
35
- }
36
- }}
37
- >
38
- <span>${c.title}</span> ${triangle}
39
- </button>`
40
- );
41
- }}
40
+ setGroupOnDescending: setDescending,
41
+ } = {}) =>
42
+ render({
43
+ columns: columns?.filter?.((c) => c['groupOn']),
44
+ on,
45
+ setOn,
46
+ descending,
47
+ setDescending,
48
+ })}
42
49
  >
43
50
  </sort-and-group-consumer>
44
51
  `;
@@ -4,7 +4,7 @@ import { _ } from '@neovici/cosmoz-i18next';
4
4
  import { isEmpty } from '@neovici/cosmoz-utils/lib/template';
5
5
  import { defaultPlacement } from '@neovici/cosmoz-dropdown';
6
6
  import '@neovici/cosmoz-collapse';
7
- import sort from '../cosmoz-omnitable-sort';
7
+ import sort from './cosmoz-omnitable-sort';
8
8
  import group from './cosmoz-omnitable-group';
9
9
  import style, { dropdown as dropdownStyle } from './style.css';
10
10
  import useSettingsUi from './use-settings-ui';
@@ -44,7 +44,6 @@ const placement = ['bottom-right', ...defaultPlacement],
44
44
  ?has-filter=${!isEmpty(filters[column.name]?.filter)}
45
45
  >${column.title}</label
46
46
  >
47
- ${sort(column.name)}
48
47
  <input
49
48
  class="checkbox"
50
49
  type="checkbox"
@@ -86,8 +85,18 @@ const placement = ['bottom-right', ...defaultPlacement],
86
85
  ${_('Columns')} ${arrow}
87
86
  </div>
88
87
  <cosmoz-collapse ?opened=${opened.columns}>
89
- <div class="list">${settings?.map(renderItem(thru))}</div>
88
+ <div class="list">${settings.columns?.map(renderItem(thru))}</div>
90
89
  </cosmoz-collapse>
90
+
91
+ <div
92
+ class="heading"
93
+ ?data-opened=${opened.sort}
94
+ @click=${() => setOpened((c) => ({ ...c, sort: !c.sort }))}
95
+ >
96
+ ${_('Sort on')} ${arrow}
97
+ </div>
98
+ <cosmoz-collapse ?opened=${opened.sort}> ${sort()} </cosmoz-collapse>
99
+
91
100
  <div
92
101
  class="heading"
93
102
  ?data-opened=${opened.group}
@@ -0,0 +1,23 @@
1
+ import { html } from 'haunted';
2
+ import { render } from './cosmoz-omnitable-group';
3
+
4
+ export default () => html`
5
+ <sort-and-group-consumer
6
+ class="sgs"
7
+ .render=${({
8
+ columns,
9
+ sortOn: on,
10
+ setSortOn: setOn,
11
+ descending,
12
+ setDescending,
13
+ } = {}) =>
14
+ render({
15
+ columns: columns?.filter?.((c) => c['sortOn']),
16
+ on,
17
+ setOn,
18
+ descending,
19
+ setDescending,
20
+ })}
21
+ >
22
+ </sort-and-group-consumer>
23
+ `;
@@ -1,6 +1,15 @@
1
+ /* eslint-disable import/group-exports */
2
+ import { props } from '@neovici/cosmoz-utils/lib/object';
3
+
4
+ export const sgProps = ['sortOn', 'descending', 'groupOn', 'groupOn'];
5
+
1
6
  const byName = (name) => (item) => item.name === name;
2
7
 
3
- export default (columns = [], settings = [], savedSettings = []) => {
8
+ export const normalizeColumns = (
9
+ columns = [],
10
+ settings = [],
11
+ savedSettings = []
12
+ ) => {
4
13
  const cols = columns.filter(
5
14
  (column) =>
6
15
  !settings.some(byName(column.name)) &&
@@ -38,3 +47,28 @@ export default (columns = [], settings = [], savedSettings = []) => {
38
47
  }),
39
48
  ];
40
49
  };
50
+
51
+ export const normalizeStore = (settings, current) => ({
52
+ ...current,
53
+ ...props(sgProps)(settings),
54
+ columns: settings.columns?.map(
55
+ props(['name', 'priority', 'width', 'flex', 'disabled'])
56
+ ),
57
+ });
58
+
59
+ export default ({ columns, settings, savedSettings, initial }) => {
60
+ const init = Object.fromEntries(
61
+ sgProps.flatMap((k) => (initial[k] != null ? [[k, initial[k]]] : []))
62
+ );
63
+
64
+ return {
65
+ ...init,
66
+ ...props(sgProps)(savedSettings),
67
+ ...settings,
68
+ columns: normalizeColumns(
69
+ columns,
70
+ settings?.columns,
71
+ savedSettings?.columns
72
+ ),
73
+ };
74
+ };
@@ -1,5 +1,5 @@
1
1
  import { tagged as css } from '@neovici/cosmoz-utils';
2
- import { checkbox, sort as sortStyle } from '../../cosmoz-omnitable-styles';
2
+ import { checkbox } from '../../cosmoz-omnitable-styles';
3
3
 
4
4
  export default css`
5
5
  :host {
@@ -106,14 +106,6 @@ export default css`
106
106
  margin: 4px 0;
107
107
  }
108
108
 
109
- ${sortStyle}
110
- .sort {
111
- order: initial;
112
- margin: 0;
113
- width: auto;
114
- padding: 8px;
115
- }
116
-
117
109
  .buttons {
118
110
  display: flex;
119
111
  gap: 8px;
@@ -145,7 +137,8 @@ export default css`
145
137
  color: inherit;
146
138
  text-decoration: underline;
147
139
  }
148
- .groups {
140
+ /* sortgroups */
141
+ .sgs {
149
142
  display: grid;
150
143
  column-gap: 7px;
151
144
  row-gap: 8px;
@@ -153,7 +146,7 @@ export default css`
153
146
  grid-template-rows: auto;
154
147
  padding: 14px;
155
148
  }
156
- .group {
149
+ .sg {
157
150
  border: 1px solid rgba(0, 0, 0, 0.2);
158
151
  border-radius: 2px;
159
152
  font-size: 13px;
@@ -166,25 +159,25 @@ export default css`
166
159
  align-items: center;
167
160
  justify-content: center;
168
161
  }
169
- .group span {
162
+ .sg span {
170
163
  overflow: hidden;
171
164
  text-overflow: ellipsis;
172
165
  }
173
- .group[data-group] {
166
+ .sg[data-on] {
174
167
  background: var(
175
168
  --cosmoz-omnitable-checkbox-checked-color,
176
169
  var(--primary-color)
177
170
  );
178
171
  }
179
- .group svg {
172
+ .sg svg {
180
173
  margin-left: 4px;
181
174
  flex: none;
182
175
  vertical-align: middle;
183
176
  }
184
- .group:not([data-group]) svg {
177
+ .sg:not([data-on]) svg {
185
178
  display: none;
186
179
  }
187
- .group:not([data-group='desc']) svg {
180
+ .sg:not([data-on='desc']) svg {
188
181
  transform: scaleY(-1);
189
182
  }
190
183
  `;
@@ -1,22 +1,21 @@
1
1
  import { useCallback, useMemo, useState } from 'haunted';
2
- import { props } from '@neovici/cosmoz-utils/lib/object';
3
-
4
- const storagePrefix = 'omnitable-';
5
-
2
+ import { normalizeStore } from './normalize';
3
+
4
+ const storagePrefix = 'omnitable-',
5
+ read = (settingsId) => {
6
+ if (!settingsId) {
7
+ return [];
8
+ }
9
+ try {
10
+ return JSON.parse(localStorage.getItem(storagePrefix + settingsId)) ?? [];
11
+ } catch (e) {
12
+ return [];
13
+ }
14
+ };
15
+ // eslint-disable-next-line max-lines-per-function
6
16
  export default (settingsId, settings, setSettings) => {
7
- const
8
- [counter, setCounter] = useState(0),
9
- savedSettings = useMemo(() => {
10
- if (!settingsId) {
11
- return [];
12
- }
13
-
14
- try {
15
- return JSON.parse(localStorage.getItem(storagePrefix + settingsId)) ?? [];
16
- } catch (e) {
17
- return [];
18
- }
19
- }, [settingsId, counter]);
17
+ const [counter, setCounter] = useState(0),
18
+ savedSettings = useMemo(() => read(settingsId), [settingsId, counter]);
20
19
 
21
20
  return {
22
21
  settingsId,
@@ -28,28 +27,32 @@ export default (settingsId, settings, setSettings) => {
28
27
  }
29
28
 
30
29
  try {
31
- localStorage.setItem(storagePrefix + settingsId, JSON.stringify(settings.map(props(['name', 'priority', 'width', 'flex', 'disabled']))));
32
- setSettings([]);
33
- setCounter(counter => counter + 1);
30
+ const current = read(settingsId);
31
+ localStorage.setItem(
32
+ storagePrefix + settingsId,
33
+ JSON.stringify(normalizeStore(settings, current))
34
+ );
35
+ setSettings();
36
+ setCounter((counter) => counter + 1);
34
37
  } catch (e) {
35
38
  // eslint-disable-next-line no-console
36
39
  console.error(e);
37
40
  }
38
41
  }, [settings]),
39
42
 
40
- onReset: e => {
41
- setSettings([]);
43
+ onReset: (e) => {
44
+ setSettings();
42
45
 
43
46
  if (e.shiftKey) {
44
47
  try {
45
48
  localStorage.removeItem(storagePrefix + settingsId);
46
- setCounter(counter => counter + 1);
49
+ setCounter((counter) => counter + 1);
47
50
  } catch (err) {
48
51
  // ignore error
49
52
  }
50
53
  }
51
54
  },
52
55
 
53
- hasChanges: settings.length !== 0
56
+ hasChanges: settings != null,
54
57
  };
55
58
  };
@@ -10,7 +10,17 @@ const parseIndex = (str) => {
10
10
  export default (host) => {
11
11
  const { config } = host,
12
12
  { settings, setSettings } = config,
13
- meta = useMeta({ settings, setSettings });
13
+ meta = useMeta({
14
+ settings: settings.columns,
15
+ setSettings: useCallback(
16
+ (columns) =>
17
+ setSettings((cfg) => ({
18
+ ...cfg,
19
+ columns,
20
+ })),
21
+ [setSettings]
22
+ ),
23
+ });
14
24
 
15
25
  return {
16
26
  ...config,
@@ -3,30 +3,38 @@ import { useMemo, useState } from 'haunted';
3
3
  import useSavedSettings from './use-saved-settings';
4
4
  import normalize from './normalize';
5
5
 
6
- export default ({ settingsId, columns }) => {
7
- const [settings, setSettings] = useState([]),
8
- [opened, setOpened] = useState({ columns: true, group: true }),
9
- { savedSettings, ...thru } = useSavedSettings(
6
+ export default ({ settingsId, ...thru }) => {
7
+ const [settings, setSettings] = useState(),
8
+ [opened, setOpened] = useState({ columns: true, sort: true }),
9
+ { savedSettings, ...rest } = useSavedSettings(
10
10
  settingsId,
11
11
  settings,
12
12
  setSettings
13
13
  ),
14
+ { columns } = thru,
14
15
  normalizedSettings = useMemo(
15
- () => normalize(columns, settings, savedSettings),
16
- [columns, settings, savedSettings]
16
+ () =>
17
+ normalize({
18
+ settings,
19
+ savedSettings,
20
+ ...thru,
21
+ }),
22
+ [settings, savedSettings, ...Object.values(thru)]
17
23
  ),
18
24
  normalizedColumns = useMemo(
19
- () => normalizedSettings.map(s => columns.find(c => c.name === s.name)),
20
- [columns, ...normalizedSettings.map(s => s.name)]
25
+ () =>
26
+ normalizedSettings.columns.map((s) =>
27
+ columns.find((c) => c.name === s.name)
28
+ ),
29
+ [columns, ...normalizedSettings.columns.map((s) => s.name)]
21
30
  );
22
31
 
23
32
  return {
24
- ...thru,
33
+ ...rest,
25
34
  opened,
26
35
  setOpened,
27
36
  settings: normalizedSettings,
28
- normalizedColumns,
37
+ columns: normalizedColumns,
29
38
  setSettings,
30
39
  };
31
40
  };
32
-
@@ -9,9 +9,9 @@ import { render } from 'lit-html';
9
9
  export const useFastLayout = ({ host, settings, setSettings, groupOnColumn, resizeSpeedFactor }) => {
10
10
  const
11
11
  canvasWidth = useCanvasWidth(host),
12
- layout = useLayout({ canvasWidth, groupOnColumn, config: settings }),
12
+ layout = useLayout({ canvasWidth, groupOnColumn, config: settings.columns }),
13
13
  tweenedlayout = useTweenArray(layout, resizeSpeedFactor),
14
- layoutCss = useMemo(() => toCss(tweenedlayout, settings), [tweenedlayout]);
14
+ layoutCss = useMemo(() => toCss(tweenedlayout, settings.columns), [tweenedlayout]);
15
15
 
16
16
  useResizableColumns({ host, canvasWidth, layout, setSettings: update => setSettings(update(settings)) });
17
17
 
@@ -8,12 +8,14 @@ import { onItemChange } from './utils-data';
8
8
 
9
9
  // eslint-disable-next-line max-lines-per-function
10
10
  export const useOmnitable = (host) => {
11
- const { enabledColumns, hashParam } = host,
12
- columns = useDOMColumns(host, { enabledColumns }),
13
- sortAndGroupOptions = useSortAndGroupOptions(columns, hashParam, host),
11
+ const { enabledColumns, hashParam, settingsId } = host,
12
+ _columns = useDOMColumns(host, { enabledColumns }),
13
+ settingS = useSettings({ columns: _columns, settingsId, initial: host }),
14
+ { settings, setSettings, columns } = settingS,
15
+ sortAndGroupOptions = useSortAndGroupOptions(columns, hashParam, settings, setSettings),
14
16
  { groupOnColumn, groupOnDescending, sortOnColumn, descending } =
15
17
  sortAndGroupOptions,
16
- { data, resizeSpeedFactor, settingsId } = host,
18
+ { data, resizeSpeedFactor } = host,
17
19
  // TODO: drop filterFunctions
18
20
  {
19
21
  processedItems,
@@ -31,8 +33,6 @@ export const useOmnitable = (host) => {
31
33
  descending,
32
34
  hashParam,
33
35
  }),
34
- settingsOpts = useSettings({ settingsId, columns }),
35
- { settings, setSettings, normalizedColumns } = settingsOpts,
36
36
  layout = useFastLayout({
37
37
  host,
38
38
  settings,
@@ -42,7 +42,7 @@ export const useOmnitable = (host) => {
42
42
  }),
43
43
  collapsedColumns = useMemo(
44
44
  () =>
45
- settings.reduce(
45
+ settings.columns.reduce(
46
46
  (acc, column, index) =>
47
47
  layout[index] != null ||
48
48
  column.name === groupOnColumn?.name ||
@@ -58,7 +58,7 @@ export const useOmnitable = (host) => {
58
58
  [
59
59
  groupOnColumn,
60
60
  ...collapsedColumns,
61
- ...settings.filter((s) => s.disabled),
61
+ ...settings.columns.filter((s) => s.disabled),
62
62
  ].some(
63
63
  (column) =>
64
64
  column && Object.keys(filterFunctions).includes(column.name)
@@ -67,12 +67,12 @@ export const useOmnitable = (host) => {
67
67
  ),
68
68
  settingsConfig = useMemo(
69
69
  () => ({
70
- ...settingsOpts,
70
+ ...settingS,
71
71
  collapsed: collapsedColumns,
72
72
  badge: hasHiddenFilter,
73
73
  filters,
74
74
  }),
75
- [settingsOpts, collapsedColumns, hasHiddenFilter, filters]
75
+ [settingS, collapsedColumns, hasHiddenFilter, filters]
76
76
  );
77
77
 
78
78
  useEffect(() => {
@@ -103,7 +103,6 @@ export const useOmnitable = (host) => {
103
103
  ...sortAndGroupOptions,
104
104
 
105
105
  columns,
106
- normalizedColumns,
107
106
  collapsedColumns,
108
107
 
109
108
  settingsConfig,
@@ -1,49 +1,62 @@
1
1
  import { useEffect, useRef } from 'haunted';
2
2
 
3
- export const useResizableColumns = ({ host, canvasWidth, layout, setSettings }) => {
3
+ export const useResizableColumns = ({
4
+ host,
5
+ canvasWidth,
6
+ layout,
7
+ setSettings,
8
+ }) => {
4
9
  const onColumnResizeRef = useRef();
5
10
 
6
- onColumnResizeRef.current = ev => setSettings(config => {
7
- const
8
- { detail: { newWidth, column }} = ev,
9
- columnIndex = config.findIndex(c => c.name === column.name),
10
- newConfig = [],
11
- maxPriority = config.reduce((p, c) => Math.max(p, c.priority), -Infinity);
12
-
13
- for (let i = 0; i < layout.length; i++) {
14
- newConfig[i] = { ...config[i] };
15
-
16
- // for visible columns to the left of the resized column
17
- if (i < columnIndex && layout[i]) {
18
- // save the current width
19
- newConfig[i].width = layout[i];
20
- // make them fixed width
21
- newConfig[i].flex = 0;
22
- // keep them visible
23
- newConfig[i].priority = maxPriority;
24
- }
25
-
26
- // update the width of the resized column
27
- if (i === columnIndex) {
28
- const
29
- maxNewSize = layout.reduce((acc, cur, i) => {
11
+ onColumnResizeRef.current = (ev) =>
12
+ setSettings((settings) => {
13
+ const config = settings.columns,
14
+ {
15
+ detail: { newWidth, column },
16
+ } = ev,
17
+ columnIndex = config.findIndex((c) => c.name === column.name),
18
+ newConfig = [],
19
+ maxPriority = config.reduce(
20
+ (p, c) => Math.max(p, c.priority),
21
+ -Infinity
22
+ );
23
+
24
+ for (let i = 0; i < layout.length; i++) {
25
+ newConfig[i] = { ...config[i] };
26
+
27
+ // for visible columns to the left of the resized column
28
+ if (i < columnIndex && layout[i]) {
29
+ // save the current width
30
+ newConfig[i].width = layout[i];
31
+ // make them fixed width
32
+ newConfig[i].flex = 0;
33
+ // keep them visible
34
+ newConfig[i].priority = maxPriority;
35
+ }
36
+
37
+ // update the width of the resized column
38
+ if (i === columnIndex) {
39
+ const maxNewSize = layout.reduce((acc, cur, i) => {
30
40
  if (i < columnIndex) {
31
41
  return cur ? acc - cur : acc;
32
42
  }
33
43
  return acc;
34
44
  }, canvasWidth);
35
45
 
36
- newConfig[i].width = Math.min(maxNewSize, Math.max(newWidth, config[i].minWidth));
37
- newConfig[i].flex = 0;
38
- newConfig[i].priority = maxPriority;
46
+ newConfig[i].width = Math.min(
47
+ maxNewSize,
48
+ Math.max(newWidth, config[i].minWidth)
49
+ );
50
+ newConfig[i].flex = 0;
51
+ newConfig[i].priority = maxPriority;
52
+ }
39
53
  }
40
- }
41
54
 
42
- return newConfig;
43
- });
55
+ return { ...settings, columns: newConfig };
56
+ });
44
57
 
45
58
  useEffect(() => {
46
- const handler = ev => onColumnResizeRef.current(ev);
59
+ const handler = (ev) => onColumnResizeRef.current(ev);
47
60
  host.addEventListener('column-resize', handler);
48
61
  return () => host.removeEventListener('column-resize', handler);
49
62
  }, []);
@@ -1,26 +1,46 @@
1
- import { useMemo, createContext, component, useContext } from 'haunted';
1
+ import {
2
+ useMemo,
3
+ createContext,
4
+ component,
5
+ useContext,
6
+ useCallback,
7
+ } from 'haunted';
2
8
  import { useHashState } from './use-hash-state';
3
9
 
4
10
  const parseBool = (bool) => [true, 'true', 1, 'yes', 'on'].includes(bool),
5
- boolParam = (p) => p === '' || (p == null ? undefined : parseBool(p));
11
+ boolParam = (p) => p === '' || (p == null ? undefined : parseBool(p)),
12
+ useSettingsState = (setter, name, setSettings) =>
13
+ useCallback(
14
+ (value) => {
15
+ setter(value);
16
+ setSettings((s) => ({ ...s, [name]: value }));
17
+ },
18
+ [setter, name, setSettings]
19
+ );
6
20
 
7
- export const useSortAndGroupOptions = (columns, hashParam, initial) => {
8
- const [sortOn, setSortOn] = useHashState(initial.sortOn, hashParam, {
21
+ // eslint-disable-next-line max-lines-per-function
22
+ export const useSortAndGroupOptions = (
23
+ columns,
24
+ hashParam,
25
+ settings,
26
+ setSettings
27
+ ) => {
28
+ const [sortOn, setSortOn] = useHashState(settings.sortOn, hashParam, {
9
29
  suffix: '-sortOn',
10
30
  }),
11
31
  [descending, setDescending] = useHashState(
12
- initial.descending,
32
+ settings.descending,
13
33
  hashParam,
14
34
  {
15
35
  suffix: '-descending',
16
36
  read: boolParam,
17
37
  }
18
38
  ),
19
- [groupOn, setGroupOn] = useHashState(initial.groupOn, hashParam, {
39
+ [groupOn, setGroupOn] = useHashState(settings.groupOn, hashParam, {
20
40
  suffix: '-groupOn',
21
41
  }),
22
42
  [groupOnDescending, setGroupOnDescending] = useHashState(
23
- initial.groupOnDescending,
43
+ settings.groupOnDescending,
24
44
  hashParam,
25
45
  { suffix: '-groupOnDescending', read: boolParam }
26
46
  ),
@@ -34,14 +54,22 @@ export const useSortAndGroupOptions = (columns, hashParam, initial) => {
34
54
  ),
35
55
  sortAndGroup_ = {
36
56
  groupOn,
37
- setGroupOn,
57
+ setGroupOn: useSettingsState(setGroupOn, 'groupOn', setSettings),
38
58
  groupOnDescending,
39
- setGroupOnDescending,
59
+ setGroupOnDescending: useSettingsState(
60
+ setGroupOnDescending,
61
+ 'groupOnDescending',
62
+ setSettings
63
+ ),
40
64
 
41
65
  sortOn,
42
- setSortOn,
66
+ setSortOn: useSettingsState(setSortOn, 'sortOn', setSettings),
43
67
  descending,
44
- setDescending,
68
+ setDescending: useSettingsState(
69
+ setDescending,
70
+ 'descending',
71
+ setSettings
72
+ ),
45
73
 
46
74
  columns,
47
75
  },
@@ -56,6 +84,7 @@ export const useSortAndGroupOptions = (columns, hashParam, initial) => {
56
84
  },
57
85
  SortAndGroupContext = createContext();
58
86
 
87
+
59
88
  customElements.define('sort-and-group-provider', SortAndGroupContext.Provider);
60
89
  customElements.define(
61
90
  'sort-and-group-consumer',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neovici/cosmoz-omnitable",
3
- "version": "8.11.0",
3
+ "version": "8.13.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"
@@ -1,37 +0,0 @@
1
- import { html } from 'haunted';
2
- import { ifDefined } from 'lit-html/directives/if-defined';
3
- import { triangle } from './icons';
4
-
5
- export default (column) => html`
6
- <sort-and-group-consumer
7
- style="display: contents"
8
- .render=${({
9
- sortOn,
10
- setSortOn,
11
- descending,
12
- setDescending,
13
- } = {}) => html` <button
14
- class="sort"
15
- data-sort=${ifDefined(
16
- (column === sortOn && (descending ? 'desc' : 'asc')) || undefined
17
- )}
18
- @click=${(e) => {
19
- const sort = e.currentTarget?.dataset.sort;
20
- if (!sort) {
21
- setSortOn(column);
22
- setDescending(false);
23
- }
24
- if (sort === 'asc') {
25
- setDescending(true);
26
- }
27
- if (sort === 'desc') {
28
- setSortOn();
29
- setDescending(false);
30
- }
31
- }}
32
- >
33
- ${triangle}
34
- </button>`}
35
- >
36
- </sort-and-group-consumer>
37
- `;