@neovici/cosmoz-omnitable 8.5.4 → 8.7.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.
@@ -1,12 +1,12 @@
1
1
  import { component } from 'haunted';
2
2
  import { nothing } from 'lit-html';
3
3
 
4
- const GroupRow = ({ column, item, selected, folded, group }) => {
4
+ const GroupRow = ({ column, item, selected, folded }) => {
5
5
  if (!column) {
6
6
  return nothing;
7
7
  }
8
8
 
9
- return (column.renderGroup ?? column.renderCell)(column, { item, selected, folded, group });
9
+ return (column.renderGroup ?? column.renderCell)(column, { item, selected, folded });
10
10
  };
11
11
 
12
12
  customElements.define('cosmoz-omnitable-group-row', component(GroupRow, { useShadowDOM: false }));
@@ -1,8 +1,10 @@
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';
4
5
 
5
- const renderHeaderRow = ({
6
+ const /* eslint-disable-next-line max-lines-per-function */
7
+ renderHeaderRow = ({
6
8
  data,
7
9
  columns,
8
10
  groupOnColumn,
@@ -19,12 +21,15 @@ const renderHeaderRow = ({
19
21
  title=${column.title}
20
22
  name=${column.name}
21
23
  >
22
- ${column.renderHeader(
23
- column,
24
- filters[column.name] ?? {},
25
- (state) => setFilterState(column.name, state),
26
- column.source(column, data)
27
- )}
24
+ ${[
25
+ column.renderHeader(
26
+ column,
27
+ filters[column.name] ?? {},
28
+ (state) => setFilterState(column.name, state),
29
+ column.source(column, data)
30
+ ),
31
+ sort(column.name),
32
+ ]}
28
33
  </div>`,
29
34
  html`<cosmoz-omnitable-resize-nub
30
35
  .column=${column}
@@ -1,73 +1,120 @@
1
1
  /* eslint-disable max-lines */
2
- const checkbox = `
3
- .checkbox {
4
- box-sizing: border-box;
5
- width: 18px;
6
- height: 18px;
7
- background: transparent;
8
- border-radius: 4px;
9
- appearance: none;
10
- -webkit-appearance: none;
11
- outline: none;
12
- position: relative;
13
- user-select: none;
14
- cursor: pointer;
15
- display: inline-block;
16
- box-shadow: 0 0 0 2px rgba(0,0,0, 0.16) inset;
17
- -webkit-tap-highlight-color: rgba(0,0,0,0);
18
- vertical-align: middle;
19
- transition: background-color 140ms;
20
- margin: 1px 12px;
21
- flex: none;
22
- }
23
-
24
- .checkbox:checked {
25
- background-color: var(--cosmoz-omnitable-checkbox-checked-color, var(--primary-color));
26
- box-shadow: none;
27
- }
2
+ import { tagged as css } from '@neovici/cosmoz-utils';
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[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 };
28
117
 
29
- .checkbox:checked::before {
30
- content: "";
31
- position: absolute;
32
- box-sizing: content-box;
33
- width: 5px;
34
- height: 10px;
35
- border: 2.4px solid #fff;
36
- border-top: none;
37
- border-left: none;
38
- transform-origin: 5px 10px;
39
- transform: translate(3px) rotate(45deg);
40
- }
41
-
42
- .checkbox::after {
43
- content: '';
44
- display: block;
45
- bottom: -5px;
46
- left: -5px;
47
- right: -5px;
48
- top: -5px;
49
- }
50
-
51
- .checkbox:hover {
52
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 1) inset, 0 0 2px 6px #2021240f;
53
- }
54
-
55
- .checkbox:checked:hover {
56
- box-shadow: 0 0 2px 6px #2021240f;
57
- }
58
-
59
- .checkbox:indeterminate::before {
60
- content: "";
61
- position: absolute;
62
- width: 10px;
63
- height: 2px;
64
- left: 4px;
65
- top: 8px;
66
- background-color: var(--cosmoz-omnitable-checkbox-checked-color, var(--primary-color));
67
- }
68
- `;
69
-
70
- export { checkbox };
71
118
  export default `<style>
72
119
  :host {
73
120
  display: flex;
@@ -115,14 +162,12 @@ export default `<style>
115
162
  }
116
163
  .header-cell {
117
164
  --paper-input-container: {
118
- padding-top: 0;
165
+ padding-top: 0;
166
+ padding-bottom: 0;
167
+ --paper-font-caption_-_line-height: 18px;
119
168
  }
120
169
  }
121
170
 
122
- .header-cell > * {
123
- width: 100%;
124
- }
125
-
126
171
  cosmoz-omnitable-header-row {
127
172
  white-space: nowrap;
128
173
  }
@@ -455,10 +500,10 @@ export default `<style>
455
500
  color: rgba(0, 0, 0, 0.54);
456
501
  }
457
502
 
458
- ${ checkbox }
503
+ ${checkbox}
459
504
 
460
505
  .all {
461
- margin-bottom: 14px;
506
+ margin-bottom: 6px;
462
507
  }
463
508
 
464
509
  .expand {
@@ -475,4 +520,5 @@ export default `<style>
475
520
  .expand:hover, .fold:hover {
476
521
  color: #000;
477
522
  }
523
+ ${sort}
478
524
  </style>`;
@@ -46,6 +46,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
46
46
  <div id="layoutStyle"></div>
47
47
 
48
48
  <div class="mainContainer">
49
+ <sort-and-group-provider value="[[ sortAndGroup ]]">
49
50
  <div class="header" id="header">
50
51
  <input class="checkbox all" type="checkbox" checked="[[ _allSelected ]]" on-input="_onAllCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
51
52
  <cosmoz-omnitable-header-row
@@ -57,6 +58,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
57
58
  set-filter-state="[[ setFilterState ]]"
58
59
  ></cosmoz-omnitable-header-row>
59
60
  </div>
61
+ </sort-and-group-provider>
60
62
  <div class="tableContent" id="tableContent">
61
63
  <template is="dom-if" if="[[ !_dataIsValid ]]">
62
64
  <div class="tableContent-empty">
@@ -119,9 +121,8 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
119
121
  <input class="checkbox" type="checkbox" checked="[[ selected ]]" on-input="_onCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
120
122
  <h3 class="groupRow-label">
121
123
  <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>
124
+ <cosmoz-omnitable-group-row column="[[ groupOnColumn ]]" item="[[ item.items.0 ]]" selected="[[ selected ]]" folded="[[ folded ]]">
125
+ </cosmoz-omnitable-group-row>
125
126
  </h3>
126
127
  <div class="groupRow-badge">[[ item.items.length ]]</div>
127
128
  <paper-icon-button class="fold" icon="[[ _getFoldIcon(folded) ]]" on-tap="_toggleGroup"></paper-icon-button>
@@ -1,24 +1,26 @@
1
1
  import { html, component, useCallback } from 'haunted';
2
2
  import { useMeta } from '@neovici/cosmoz-utils/lib/hooks/use-meta';
3
- import { checkbox } from '../cosmoz-omnitable-styles';
3
+ import { checkbox, sort as sortStyle } from '../cosmoz-omnitable-styles';
4
4
  import { nothing } from 'lit-html';
5
5
  import { isEmpty } from '@neovici/cosmoz-utils/lib/template';
6
6
  import { defaultPlacement } from '@neovici/cosmoz-dropdown';
7
+ import sort from './cosmoz-omnitable-sort';
8
+
7
9
  const placement = ['bottom-right', ...defaultPlacement],
8
10
  settingsStyles = `
9
11
  :host {
10
12
  box-sizing: border-box;
11
- padding: 12px;
12
13
  display: flex;
13
14
  flex-direction: column;
14
15
  max-height: 42vh;
15
16
  outline: none;
17
+ padding-top: 14px;
16
18
  }
17
19
  .list {
18
20
  overflow-y: auto;
19
21
  flex: 1;
20
- padding-top: 2px;
21
- min-width: 225px;
22
+ padding: 2px 14px;
23
+ min-width: 232px;
22
24
  }
23
25
  .item {
24
26
  display: flex;
@@ -32,7 +34,7 @@ const placement = ['bottom-right', ...defaultPlacement],
32
34
  .item.dragover {
33
35
  box-shadow: 0 -2px 0 0 currentColor;
34
36
  }
35
- .sort {
37
+ .pull {
36
38
  border: none;
37
39
  padding: 0;
38
40
  font-size: 0;
@@ -41,6 +43,7 @@ const placement = ['bottom-right', ...defaultPlacement],
41
43
  background: transparent;
42
44
  cursor: move;
43
45
  margin-right: 12px;
46
+ color: #c4c4c4;
44
47
  }
45
48
  .title {
46
49
  flex: 1;
@@ -48,12 +51,25 @@ const placement = ['bottom-right', ...defaultPlacement],
48
51
  .title[has-filter] {
49
52
  font-weight: bold;
50
53
  }
51
- ${ checkbox }
54
+ ${checkbox}
55
+ .checkbox {
56
+ margin: 4px 0;
57
+ }
58
+
59
+ ${sortStyle}
60
+ .sort {
61
+ order: initial;
62
+ margin: 0;
63
+ width: auto;
64
+ padding: 8px;
65
+ }
52
66
 
53
67
  .buttons {
54
68
  display: flex;
69
+ gap: 8px;
55
70
  margin-top: 10px;
56
- gap: 7px;
71
+ padding: 12px 14px;
72
+ box-shadow: inset 0px 1px 0px rgba(0, 0, 0, 0.15);
57
73
  }
58
74
  .button {
59
75
  border: none;
@@ -62,32 +78,32 @@ const placement = ['bottom-right', ...defaultPlacement],
62
78
  color: var(--cosmoz-button-color, #fff);
63
79
  font-family: inherit;
64
80
  font-size: 13px;
65
- font-weight: 500;
81
+ font-weight: 600;
66
82
  line-height: 26px;
67
- height: 26px;
68
83
  border-radius: 4px;
69
- padding: 0 8px;
84
+ padding: 8px;
70
85
  flex: 1;
71
86
  }
72
- .button:hover {
73
- background: var(--cosmoz-button-hover-bg-color, #3A3F44);
87
+ .button:not(.reset):hover {
88
+ background: var(--cosmoz-button-hover-bg-color, #3A3F44);
74
89
  }
75
90
  .button[disabled] {
76
- opacity: 0.7;
91
+ opacity: 0.2;
77
92
  pointer-events: none;
78
93
  }
94
+ .reset {
95
+ background: transparent;
96
+ color: inherit;
97
+ text-decoration: underline;
98
+ }
79
99
  `,
80
-
81
- parseIndex = str => {
100
+ parseIndex = (str) => {
82
101
  const idx = parseInt(str, 10);
83
102
  return isFinite(idx) ? idx : undefined;
84
103
  },
85
-
86
104
  // eslint-disable-next-line max-lines-per-function
87
- useSettings = host => {
88
-
89
- const
90
- { settings, onSettings } = host,
105
+ useSettings = (host) => {
106
+ const { settings, onSettings } = host,
91
107
  meta = useMeta({ settings, onSettings });
92
108
 
93
109
  return {
@@ -101,30 +117,40 @@ const placement = ['bottom-right', ...defaultPlacement],
101
117
 
102
118
  filters: host.filters,
103
119
 
104
- onDown: useCallback(e => {
105
- if (!e.target.closest('.sort')) {
106
- return;
107
- }
120
+ onDown: useCallback(
121
+ (e) => {
122
+ if (!e.target.closest('.pull')) {
123
+ return;
124
+ }
108
125
 
109
- meta.handle = e.currentTarget;
110
- }, [meta]),
126
+ meta.handle = e.currentTarget;
127
+ },
128
+ [meta]
129
+ ),
111
130
 
112
- onDragStart: useCallback(e => {
113
- const { target } = e,
114
- index = parseIndex(target.dataset.index);
131
+ onDragStart: useCallback(
132
+ (e) => {
133
+ const { target } = e,
134
+ index = parseIndex(target.dataset.index);
115
135
 
116
- if (!meta.handle?.contains(target) || index == null) {
117
- return e.preventDefault();
118
- }
136
+ if (!meta.handle?.contains(target) || index == null) {
137
+ return e.preventDefault();
138
+ }
119
139
 
120
- meta.handle = null;
121
- e.dataTransfer.effectAllowed = 'move';
122
- e.dataTransfer.setData('omnitable/sort-index', index);
123
- setTimeout(() => target.classList.add('drag'), 0);
124
- target.addEventListener('dragend', e => e.target.classList.remove('drag'), { once: true });
125
- }, [meta]),
140
+ meta.handle = null;
141
+ e.dataTransfer.effectAllowed = 'move';
142
+ e.dataTransfer.setData('omnitable/sort-index', index);
143
+ setTimeout(() => target.classList.add('drag'), 0);
144
+ target.addEventListener(
145
+ 'dragend',
146
+ (e) => e.target.classList.remove('drag'),
147
+ { once: true }
148
+ );
149
+ },
150
+ [meta]
151
+ ),
126
152
 
127
- onDragEnter: useCallback(e => {
153
+ onDragEnter: useCallback((e) => {
128
154
  const ctg = e.currentTarget;
129
155
  if (ctg !== e.target) {
130
156
  return;
@@ -135,12 +161,12 @@ const placement = ['bottom-right', ...defaultPlacement],
135
161
  ctg.classList.add('dragover');
136
162
  }, []),
137
163
 
138
- onDragOver: useCallback(e => {
164
+ onDragOver: useCallback((e) => {
139
165
  e.preventDefault();
140
166
  e.currentTarget.classList.add('dragover');
141
167
  }, []),
142
168
 
143
- onDragLeave: useCallback(e => {
169
+ onDragLeave: useCallback((e) => {
144
170
  const ctg = e.currentTarget;
145
171
  if (ctg.contains(e.relatedTarget)) {
146
172
  return;
@@ -149,77 +175,153 @@ const placement = ['bottom-right', ...defaultPlacement],
149
175
  ctg.classList.remove('dragover');
150
176
  }, []),
151
177
 
152
- onDrop: useCallback(e => {
153
- const from = parseIndex(e.dataTransfer.getData('omnitable/sort-index')),
154
- to = parseIndex(e.currentTarget.dataset.index),
155
- { settings, onSettings } = meta;
178
+ onDrop: useCallback(
179
+ (e) => {
180
+ const from = parseIndex(
181
+ e.dataTransfer.getData('omnitable/sort-index')
182
+ ),
183
+ to = parseIndex(e.currentTarget.dataset.index),
184
+ { settings, onSettings } = meta;
156
185
 
157
- e.currentTarget.classList.remove('dragover');
158
- e.preventDefault();
186
+ e.currentTarget.classList.remove('dragover');
187
+ e.preventDefault();
159
188
 
160
- const newSettings = settings.slice();
161
- newSettings.splice(to + (from >= to ? 0 : -1), 0, newSettings.splice(from, 1)[0]);
162
- onSettings(newSettings);
163
- }, [meta]),
164
-
165
- onToggle: useCallback(e => {
166
- const checked = e.target.checked,
167
- idx = parseIndex(e.target.closest('[data-index]')?.dataset.index),
168
- { settings, onSettings } = meta,
169
- newSettings = settings.slice(),
170
- setting = settings[idx],
171
- priority = e.target.windeterminate ? settings.reduce((acc, s) => Math.max(acc, s.priority), 0) + 1 : setting.priority;
172
-
173
- newSettings.splice(idx, 1, { ...settings[idx], disabled: !checked, priority });
174
- onSettings(newSettings);
175
- }, [meta])
176
- };
177
- },
189
+ const newSettings = settings.slice();
190
+ newSettings.splice(
191
+ to + (from >= to ? 0 : -1),
192
+ 0,
193
+ newSettings.splice(from, 1)[0]
194
+ );
195
+ onSettings(newSettings);
196
+ },
197
+ [meta]
198
+ ),
178
199
 
179
- renderItem = ({ onDragStart, onDragEnter, onDragOver, onDragLeave, onDrop, onDown, onToggle, collapsed, filters }) => (column, i) => {
180
- const indeterminate = collapsed?.includes(column.name),
181
- checked = !column.disabled && !indeterminate;
182
- return html`
183
- <div class="item" data-index=${ i } @mousedown=${ onDown } draggable="true"
184
- @dragstart=${ onDragStart } @dragenter=${ onDragEnter } @dragover=${ onDragOver } @dragleave=${ onDragLeave }
185
- @drop=${ onDrop }
186
- >
187
- <button class="sort" mousedown="">
188
- <svg viewBox="0 0 24 24" width="24"><path d="M20 9H4v2h16V9zM4 15h16v-2H4v2z"></path></svg>
189
- </button>
190
- <label class="title" ?has-filter=${ !isEmpty(filters[column.name]?.filter) }>${ column.title }</label>
191
- <input class="checkbox" type="checkbox" .checked=${ checked } @click=${ onToggle }
192
- .indeterminate=${ indeterminate } .windeterminate=${ indeterminate }
193
- >
194
- </div>`;
195
- },
200
+ onToggle: useCallback(
201
+ (e) => {
202
+ const checked = e.target.checked,
203
+ idx = parseIndex(e.target.closest('[data-index]')?.dataset.index),
204
+ { settings, onSettings } = meta,
205
+ newSettings = settings.slice(),
206
+ setting = settings[idx],
207
+ priority = e.target.windeterminate
208
+ ? settings.reduce((acc, s) => Math.max(acc, s.priority), 0) + 1
209
+ : setting.priority;
196
210
 
197
- SettingsUI = host => {
198
- const { settings, settingsId, onSave, onReset, hasChanges, ...thru } = useSettings(host);
211
+ newSettings.splice(idx, 1, {
212
+ ...settings[idx],
213
+ disabled: !checked,
214
+ priority,
215
+ });
216
+ onSettings(newSettings);
217
+ },
218
+ [meta]
219
+ ),
220
+ };
221
+ },
222
+ renderItem =
223
+ // eslint-disable-next-line max-lines-per-function
224
+ ({
225
+ onDragStart,
226
+ onDragEnter,
227
+ onDragOver,
228
+ onDragLeave,
229
+ onDrop,
230
+ onDown,
231
+ onToggle,
232
+ collapsed,
233
+ filters,
234
+ }) =>
235
+ (column, i) => {
236
+ const indeterminate = collapsed?.includes(column.name),
237
+ checked = !column.disabled && !indeterminate;
238
+ return html` <div
239
+ class="item"
240
+ data-index=${i}
241
+ @mousedown=${onDown}
242
+ draggable="true"
243
+ @dragstart=${onDragStart}
244
+ @dragenter=${onDragEnter}
245
+ @dragover=${onDragOver}
246
+ @dragleave=${onDragLeave}
247
+ @drop=${onDrop}
248
+ >
249
+ <button class="pull">
250
+ <svg
251
+ width="16"
252
+ height="6"
253
+ viewBox="0 0 16 6"
254
+ fill="currentColor"
255
+ xmlns="http://www.w3.org/2000/svg"
256
+ >
257
+ <rect width="16" height="2" />
258
+ <rect y="4" width="16" height="2" />
259
+ </svg>
260
+ </button>
261
+ <label
262
+ class="title"
263
+ ?has-filter=${!isEmpty(filters[column.name]?.filter)}
264
+ >${column.title}</label
265
+ >
266
+ ${sort(column.name)}
267
+ <input
268
+ class="checkbox"
269
+ type="checkbox"
270
+ .checked=${checked}
271
+ @click=${onToggle}
272
+ .indeterminate=${indeterminate}
273
+ .windeterminate=${indeterminate}
274
+ />
275
+ </div>`;
276
+ },
277
+ SettingsUI = (host) => {
278
+ const { settings, settingsId, onSave, onReset, hasChanges, ...thru } =
279
+ useSettings(host);
199
280
  return [
200
281
  html`
201
- <style>${ settingsStyles }</style>
202
- <div class="list">${ settings?.map(renderItem(thru)) }</div>
282
+ <style>
283
+ ${settingsStyles}
284
+ </style>
285
+ <div class="list">${settings?.map(renderItem(thru))}</div>
203
286
  `,
204
287
  settingsId
205
288
  ? html`<div class="buttons">
206
- <button class="button" @click=${ onSave } ?disabled=${ !hasChanges }>Save</button>
207
- <button class="button" @click=${ onReset } ?disabled=${ !hasChanges }>Reset</button>
208
- </div>`
209
- : nothing
289
+ <button
290
+ class="button reset"
291
+ @click=${onReset}
292
+ ?disabled=${!hasChanges}
293
+ >
294
+ Reset
295
+ </button>
296
+ <button class="button" @click=${onSave} ?disabled=${!hasChanges}>
297
+ Save
298
+ </button>
299
+ </div>`
300
+ : nothing,
210
301
  ];
211
302
  },
212
-
213
- Settings = ({ anchor, settings, onSettings, collapsed, settingsId, hasChanges, onSave, onReset, filters, badge }) => html`
303
+ // eslint-disable-next-line max-lines-per-function
304
+ Settings = ({
305
+ anchor,
306
+ settings,
307
+ onSettings,
308
+ collapsed,
309
+ settingsId,
310
+ hasChanges,
311
+ onSave,
312
+ onReset,
313
+ filters,
314
+ badge,
315
+ }) => html`
214
316
  <style>
215
317
  :host {
216
318
  display: contents;
217
319
  color: var(--cosmoz-omnitable-settings-color, #101010);
218
- --cosmoz-dropdown-box-shadow: 0 3px 4px 0 rgb(0 0 0 / 14%), 0 1px 8px 0 rgb(0 0 0 / 12%), 0 3px 3px -2px rgb(0 0 0 / 40%);
320
+ --cosmoz-dropdown-box-shadow: 0 3px 4px 0 rgb(0 0 0 / 14%),
321
+ 0 1px 8px 0 rgb(0 0 0 / 12%), 0 3px 3px -2px rgb(0 0 0 / 40%);
219
322
  }
220
323
  cosmoz-dropdown::part(button) {
221
324
  --cosmoz-dropdown-button-size: 24px;
222
- margin-bottom: 10px;
223
325
  padding: 0;
224
326
  background: transparent;
225
327
  color: inherit;
@@ -231,19 +333,34 @@ const placement = ['bottom-right', ...defaultPlacement],
231
333
  position: absolute;
232
334
  top: 1px;
233
335
  right: -4px;
234
- background-color: var(--cosmoz-omnitable-checkbox-checked-color, var(--primary-color));
336
+ background-color: var(
337
+ --cosmoz-omnitable-checkbox-checked-color,
338
+ var(--primary-color)
339
+ );
235
340
  width: 8px;
236
341
  height: 8px;
237
342
  border-radius: 100%;
238
343
  }
239
344
  </style>
240
- <cosmoz-dropdown .render=${ () => html`<cosmoz-omnitable-settings-ui
241
- .anchor=${ anchor } .settings=${ settings } .onSettings=${ onSettings } .collapsed=${ collapsed }
242
- .settingsId=${ settingsId } .hasChanges=${ hasChanges } .onSave=${ onSave } .onReset=${ onReset }
243
- .filters=${ filters }></cosmoz-omnitable-settings-ui>` } .placement=${ placement }>
244
- <svg viewBox="0 0 24 24" width="24" slot="button" fill="currentColor"><path d="M10 18h5V5h-5v13zm-6 0h5V5H4v13zM16 5v13h5V5h-5z"></path></svg>
245
- ${ badge ? html`<div class="badge" slot="button"></div>` : nothing }
345
+ <cosmoz-dropdown
346
+ .render=${() => html`<cosmoz-omnitable-settings-ui
347
+ .anchor=${anchor}
348
+ .settings=${settings}
349
+ .onSettings=${onSettings}
350
+ .collapsed=${collapsed}
351
+ .settingsId=${settingsId}
352
+ .hasChanges=${hasChanges}
353
+ .onSave=${onSave}
354
+ .onReset=${onReset}
355
+ .filters=${filters}
356
+ ></cosmoz-omnitable-settings-ui>`}
357
+ .placement=${placement}
358
+ >
359
+ <svg viewBox="0 0 24 24" width="24" slot="button" fill="currentColor">
360
+ <path d="M10 18h5V5h-5v13zm-6 0h5V5H4v13zM16 5v13h5V5h-5z"></path>
361
+ </svg>
362
+ ${badge ? html`<div class="badge" slot="button"></div>` : nothing}
246
363
  </cosmoz-dropdown>
247
- `;
364
+ `;
248
365
  customElements.define('cosmoz-omnitable-settings', component(Settings));
249
366
  customElements.define('cosmoz-omnitable-settings-ui', component(SettingsUI));
@@ -0,0 +1,47 @@
1
+ import { html } from 'haunted';
2
+ import { ifDefined } from 'lit-html/directives/if-defined';
3
+
4
+ const sortIcon = html`<svg
5
+ width="8"
6
+ height="6"
7
+ viewBox="0 0 8 6"
8
+ fill="currentColor"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ >
11
+ <path d="M0.5,0.5h7c0.4,0,0.6,0.4,0.4,0.7L4.4,5.3c-0.2,0.2-0.5,0.2-0.7,0L0.1,1.2
12
+ C-0.1,0.9,0.1,0.5,0.5,0.5z" />
13
+ </svg> `;
14
+
15
+ export default (column) => html`
16
+ <sort-and-group-consumer
17
+ style="display: contents"
18
+ .render=${({
19
+ sortOn,
20
+ setSortOn,
21
+ descending,
22
+ setDescending,
23
+ } = {}) => html` <button
24
+ class="sort"
25
+ data-sort=${ifDefined(
26
+ (column === sortOn && (descending ? 'desc' : 'asc')) || undefined
27
+ )}
28
+ @click=${(e) => {
29
+ const sort = e.currentTarget?.dataset.sort;
30
+ if (!sort) {
31
+ setSortOn(column);
32
+ setDescending(false);
33
+ }
34
+ if (sort === 'asc') {
35
+ setDescending(true);
36
+ }
37
+ if (sort === 'desc') {
38
+ setSortOn();
39
+ setDescending(false);
40
+ }
41
+ }}
42
+ >
43
+ ${sortIcon}
44
+ </button>`}
45
+ >
46
+ </sort-and-group-consumer>
47
+ `;
@@ -1,45 +1,63 @@
1
- import { useMemo } from 'haunted';
1
+ import { useMemo, createContext, component, useContext } from 'haunted';
2
2
  import { useHashState } from './use-hash-state';
3
3
 
4
4
  const parseBool = (bool) => [true, 'true', 1, 'yes', 'on'].includes(bool),
5
5
  boolParam = (p) => p === '' || (p == null ? undefined : parseBool(p));
6
6
 
7
7
  export const useSortAndGroupOptions = (columns, hashParam, initial) => {
8
- const [sortOn, setSortOn] = useHashState(initial.sortOn, hashParam, {
9
- suffix: '-sortOn',
10
- }),
11
- [descending, setDescending] = useHashState(initial.descending, hashParam, {
12
- suffix: '-descending',
13
- read: boolParam,
14
- }),
15
- [groupOn, setGroupOn] = useHashState(initial.groupOn, hashParam, {
16
- suffix: '-groupOn',
17
- }),
18
- [groupOnDescending, setGroupOnDescending] = useHashState(
19
- initial.groupOnDescending,
20
- hashParam,
21
- { suffix: '-groupOnDescending', read: boolParam }
22
- ),
23
- sortOnColumn = useMemo(
24
- () => columns.find((column) => column.name === sortOn),
25
- [columns, sortOn]
26
- ),
27
- groupOnColumn = useMemo(
28
- () => columns.find((column) => column.name === groupOn),
29
- [columns, groupOn]
30
- );
8
+ const [sortOn, setSortOn] = useHashState(initial.sortOn, hashParam, {
9
+ suffix: '-sortOn',
10
+ }),
11
+ [descending, setDescending] = useHashState(
12
+ initial.descending,
13
+ hashParam,
14
+ {
15
+ suffix: '-descending',
16
+ read: boolParam,
17
+ }
18
+ ),
19
+ [groupOn, setGroupOn] = useHashState(initial.groupOn, hashParam, {
20
+ suffix: '-groupOn',
21
+ }),
22
+ [groupOnDescending, setGroupOnDescending] = useHashState(
23
+ initial.groupOnDescending,
24
+ hashParam,
25
+ { suffix: '-groupOnDescending', read: boolParam }
26
+ ),
27
+ sortOnColumn = useMemo(
28
+ () => columns.find((column) => column.name === sortOn),
29
+ [columns, sortOn]
30
+ ),
31
+ groupOnColumn = useMemo(
32
+ () => columns.find((column) => column.name === groupOn),
33
+ [columns, groupOn]
34
+ ),
35
+ sortAndGroup_ = {
36
+ groupOn,
37
+ setGroupOn,
38
+ groupOnDescending,
39
+ setGroupOnDescending,
31
40
 
32
- return {
33
- groupOn,
34
- setGroupOn,
35
- groupOnColumn,
36
- groupOnDescending,
37
- setGroupOnDescending,
41
+ sortOn,
42
+ setSortOn,
43
+ descending,
44
+ setDescending,
45
+ },
46
+ sortAndGroup = useMemo(() => sortAndGroup_, Object.values(sortAndGroup_));
38
47
 
39
- sortOn,
40
- setSortOn,
41
- sortOnColumn,
42
- descending,
43
- setDescending,
44
- };
45
- };
48
+ return {
49
+ ...sortAndGroup,
50
+ sortAndGroup,
51
+ groupOnColumn,
52
+ sortOnColumn,
53
+ };
54
+ },
55
+ SortAndGroupContext = createContext();
56
+
57
+ customElements.define('sort-and-group-provider', SortAndGroupContext.Provider);
58
+ customElements.define(
59
+ 'sort-and-group-consumer',
60
+ component(({ render }) => render(useContext(SortAndGroupContext)), {
61
+ useShadowDOM: false,
62
+ })
63
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neovici/cosmoz-omnitable",
3
- "version": "8.5.4",
3
+ "version": "8.7.1",
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"