@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.
- package/cosmoz-omnitable-group-row.js +2 -2
- package/cosmoz-omnitable-header-row.js +12 -7
- package/cosmoz-omnitable-styles.js +121 -75
- package/cosmoz-omnitable.js +4 -3
- package/lib/cosmoz-omnitable-settings.js +219 -102
- package/lib/cosmoz-omnitable-sort.js +47 -0
- package/lib/use-sort-and-group-options.js +55 -37
- package/package.json +1 -1
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
${
|
|
23
|
-
column
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
${
|
|
503
|
+
${checkbox}
|
|
459
504
|
|
|
460
505
|
.all {
|
|
461
|
-
margin-bottom:
|
|
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>`;
|
package/cosmoz-omnitable.js
CHANGED
|
@@ -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>: </div>
|
|
122
|
-
<cosmoz-omnitable-group-row column="[[ groupOnColumn ]]" item="[[ item.items.0 ]]" selected="[[ selected ]]" folded="[[ folded ]]"
|
|
123
|
-
|
|
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
|
|
21
|
-
min-width:
|
|
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
|
-
.
|
|
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
|
-
${
|
|
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
|
-
|
|
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:
|
|
81
|
+
font-weight: 600;
|
|
66
82
|
line-height: 26px;
|
|
67
|
-
height: 26px;
|
|
68
83
|
border-radius: 4px;
|
|
69
|
-
padding:
|
|
84
|
+
padding: 8px;
|
|
70
85
|
flex: 1;
|
|
71
86
|
}
|
|
72
|
-
.button:hover {
|
|
73
|
-
|
|
87
|
+
.button:not(.reset):hover {
|
|
88
|
+
background: var(--cosmoz-button-hover-bg-color, #3A3F44);
|
|
74
89
|
}
|
|
75
90
|
.button[disabled] {
|
|
76
|
-
opacity: 0.
|
|
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(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
onDown: useCallback(
|
|
121
|
+
(e) => {
|
|
122
|
+
if (!e.target.closest('.pull')) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
108
125
|
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
meta.handle = e.currentTarget;
|
|
127
|
+
},
|
|
128
|
+
[meta]
|
|
129
|
+
),
|
|
111
130
|
|
|
112
|
-
onDragStart: useCallback(
|
|
113
|
-
|
|
114
|
-
|
|
131
|
+
onDragStart: useCallback(
|
|
132
|
+
(e) => {
|
|
133
|
+
const { target } = e,
|
|
134
|
+
index = parseIndex(target.dataset.index);
|
|
115
135
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
if (!meta.handle?.contains(target) || index == null) {
|
|
137
|
+
return e.preventDefault();
|
|
138
|
+
}
|
|
119
139
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
158
|
-
|
|
186
|
+
e.currentTarget.classList.remove('dragover');
|
|
187
|
+
e.preventDefault();
|
|
159
188
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
198
|
-
|
|
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
|
|
202
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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 = ({
|
|
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%),
|
|
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(
|
|
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
|
|
241
|
-
.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
initial.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
sortOn,
|
|
42
|
+
setSortOn,
|
|
43
|
+
descending,
|
|
44
|
+
setDescending,
|
|
45
|
+
},
|
|
46
|
+
sortAndGroup = useMemo(() => sortAndGroup_, Object.values(sortAndGroup_));
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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.
|
|
3
|
+
"version": "8.7.1",
|
|
4
4
|
"description": "[](https://travis-ci.org/Neovici/cosmoz-omnitable)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web-components"
|