@neovici/cosmoz-omnitable 8.8.0 → 8.10.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.
@@ -37,13 +37,15 @@ const /* eslint-disable-next-line max-lines-per-function */
37
37
  ></cosmoz-omnitable-resize-nub>`,
38
38
  ]
39
39
  ),
40
- HeaderRow = ({ content, columns, ...thru }) => [
40
+ HeaderRow = ({ columns, settingsConfig, ...thru }) => [
41
41
  columns &&
42
42
  renderHeaderRow({
43
43
  columns,
44
44
  ...thru,
45
45
  }),
46
- content,
46
+ html`<cosmoz-omnitable-settings
47
+ .config=${settingsConfig}
48
+ ></cosmoz-omnitable-settings>`,
47
49
  ];
48
50
 
49
51
  customElements.define(
@@ -24,7 +24,6 @@ import { translatable } from '@neovici/cosmoz-i18next';
24
24
  import { mixin, hauntedPolymer } from '@neovici/cosmoz-utils';
25
25
  import { isEmpty } from '@neovici/cosmoz-utils/lib/template.js';
26
26
  import { useOmnitable } from './lib/use-omnitable';
27
- import './lib/cosmoz-omnitable-settings';
28
27
  import { saveAsCsvAction } from './lib/save-as-csv-action';
29
28
  import { saveAsXlsxAction } from './lib/save-as-xlsx-action';
30
29
  import { defaultPlacement } from '@neovici/cosmoz-dropdown';
@@ -54,8 +53,8 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
54
53
  columns="[[ normalizedColumns ]]"
55
54
  filters="[[ filters ]]"
56
55
  group-on-column="[[ groupOnColumn ]]"
57
- content="[[ _renderSettings(normalizedSettings, collapsedColumns, settingsId, hasChangedSettings, hasHiddenFilter, filters) ]]"
58
56
  set-filter-state="[[ setFilterState ]]"
57
+ settings-config="[[ settingsConfig ]]"
59
58
  ></cosmoz-omnitable-header-row>
60
59
  </div>
61
60
  </sort-and-group-provider>
@@ -547,21 +546,6 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
547
546
  value && close(); /* eslint-disable-line no-unused-expressions */
548
547
  };
549
548
  }
550
-
551
- // eslint-disable-next-line max-params
552
- _renderSettings(normalizedSettings, collapsed, settingsId, hasChangedSettings, hasHiddenFilter, filters) {
553
- return litHtml`<cosmoz-omnitable-settings
554
- .settings=${ normalizedSettings }
555
- .onSettings=${ this.setSettings }
556
- .collapsed=${ collapsed?.map(c => c.name) }
557
- .settingsId=${ settingsId }
558
- .hasChanges=${ hasChangedSettings }
559
- .onSave=${ this.onSettingsSave }
560
- .onReset=${ this.onSettingsReset }
561
- .badge=${ hasHiddenFilter }
562
- .filters=${ filters }
563
- >`;
564
- }
565
549
  }
566
550
  customElements.define('cosmoz-omnitable', Omnitable);
567
551
 
@@ -1,16 +1,6 @@
1
1
  import { html } from 'haunted';
2
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> `;
3
+ import { triangle } from './icons';
14
4
 
15
5
  export default (column) => html`
16
6
  <sort-and-group-consumer
@@ -40,7 +30,7 @@ export default (column) => html`
40
30
  }
41
31
  }}
42
32
  >
43
- ${sortIcon}
33
+ ${triangle}
44
34
  </button>`}
45
35
  >
46
36
  </sort-and-group-consumer>
package/lib/icons.js ADDED
@@ -0,0 +1,66 @@
1
+ /* eslint-disable import/group-exports */
2
+ import { html } from 'lit-html';
3
+
4
+ export const close = html`<svg
5
+ width="10"
6
+ height="9"
7
+ viewBox="0 0 10 9"
8
+ stroke="currentColor"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ >
11
+ <line
12
+ x1="8.53033"
13
+ y1="0.53033"
14
+ x2="1.53033"
15
+ y2="7.53033"
16
+ stroke-width="1.5"
17
+ />
18
+ <line
19
+ x1="8.46967"
20
+ y1="7.53033"
21
+ x2="1.46967"
22
+ y2="0.530331"
23
+ stroke-width="1.5"
24
+ />
25
+ </svg>`;
26
+
27
+ export const pull = html`
28
+ <svg
29
+ width="16"
30
+ height="6"
31
+ viewBox="0 0 16 6"
32
+ fill="currentColor"
33
+ xmlns="http://www.w3.org/2000/svg"
34
+ >
35
+ <rect width="16" height="2" />
36
+ <rect y="4" width="16" height="2" />
37
+ </svg>
38
+ `;
39
+
40
+ export const arrow = html` <svg
41
+ width="12"
42
+ height="7"
43
+ viewBox="0 0 12 7"
44
+ fill="none"
45
+ stroke="currentColor"
46
+ xmlns="http://www.w3.org/2000/svg"
47
+ >
48
+ <path
49
+ d="M0.999998 0.999999L6 6L11 1"
50
+ stroke-width="1.5"
51
+ stroke-linejoin="round"
52
+ />
53
+ </svg>`;
54
+
55
+ export const triangle = html`<svg
56
+ width="8"
57
+ height="6"
58
+ viewBox="0 0 8 6"
59
+ fill="currentColor"
60
+ xmlns="http://www.w3.org/2000/svg"
61
+ >
62
+ <path
63
+ 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
64
+ C-0.1,0.9,0.1,0.5,0.5,0.5z"
65
+ />
66
+ </svg> `;
@@ -0,0 +1,44 @@
1
+ import { html } from 'haunted';
2
+ import { ifDefined } from 'lit-html/directives/if-defined';
3
+ import { triangle } from '../icons';
4
+
5
+ export default () => html`
6
+ <sort-and-group-consumer
7
+ class="groups"
8
+ .render=${({
9
+ columns,
10
+ 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
+ }}
42
+ >
43
+ </sort-and-group-consumer>
44
+ `;
@@ -0,0 +1,135 @@
1
+ import { html, component } from 'haunted';
2
+ import { nothing } from 'lit-html';
3
+ import { _ } from '@neovici/cosmoz-i18next';
4
+ import { isEmpty } from '@neovici/cosmoz-utils/lib/template';
5
+ import { defaultPlacement } from '@neovici/cosmoz-dropdown';
6
+ import '@neovici/cosmoz-collapse';
7
+ import sort from '../cosmoz-omnitable-sort';
8
+ import group from './cosmoz-omnitable-group';
9
+ import style, { dropdown as dropdownStyle } from './style.css';
10
+ import useSettingsUi from './use-settings-ui';
11
+ import { close, pull, arrow } from '../icons';
12
+
13
+ /* eslint-disable max-lines-per-function */
14
+ const placement = ['bottom-right', ...defaultPlacement],
15
+ renderItem =
16
+ ({
17
+ onDragStart,
18
+ onDragEnter,
19
+ onDragOver,
20
+ onDragLeave,
21
+ onDrop,
22
+ onDown,
23
+ onToggle,
24
+ collapsed,
25
+ filters,
26
+ }) =>
27
+ (column, i) => {
28
+ const indeterminate = !!collapsed?.find((c) => c.name === column.name),
29
+ checked = !column.disabled && !indeterminate;
30
+ return html` <div
31
+ class="item"
32
+ data-index=${i}
33
+ @mousedown=${onDown}
34
+ draggable="true"
35
+ @dragstart=${onDragStart}
36
+ @dragenter=${onDragEnter}
37
+ @dragover=${onDragOver}
38
+ @dragleave=${onDragLeave}
39
+ @drop=${onDrop}
40
+ >
41
+ <button class="pull">${pull}</button>
42
+ <label
43
+ class="title"
44
+ ?has-filter=${!isEmpty(filters[column.name]?.filter)}
45
+ >${column.title}</label
46
+ >
47
+ ${sort(column.name)}
48
+ <input
49
+ class="checkbox"
50
+ type="checkbox"
51
+ .checked=${checked}
52
+ @click=${onToggle}
53
+ .indeterminate=${indeterminate}
54
+ .windeterminate=${indeterminate}
55
+ />
56
+ </div>`;
57
+ },
58
+ SettingsUI = (host) => {
59
+ const {
60
+ settings,
61
+ settingsId,
62
+ onSave,
63
+ onReset,
64
+ hasChanges,
65
+ opened,
66
+ setOpened,
67
+ ...thru
68
+ } = useSettingsUi(host);
69
+ return [
70
+ html`
71
+ <style>
72
+ ${style}
73
+ </style>
74
+ <div class="headline">
75
+ ${_('Sort and filter')}
76
+ <button class="close" @click=${(e) => e.currentTarget?.blur()}>
77
+ ${close}
78
+ </button>
79
+ </div>
80
+ <div class="contents">
81
+ <div
82
+ class="heading"
83
+ ?data-opened=${opened.columns}
84
+ @click=${() => setOpened((c) => ({ ...c, columns: !c.columns }))}
85
+ >
86
+ ${_('Columns')} ${arrow}
87
+ </div>
88
+ <cosmoz-collapse ?opened=${opened.columns}>
89
+ <div class="list">${settings?.map(renderItem(thru))}</div>
90
+ </cosmoz-collapse>
91
+ <div
92
+ class="heading"
93
+ ?data-opened=${opened.group}
94
+ @click=${() => setOpened((c) => ({ ...c, group: !c.group }))}
95
+ >
96
+ ${_('Group on')} ${arrow}
97
+ </div>
98
+ <cosmoz-collapse ?opened=${opened.group}>
99
+ ${group()}
100
+ </cosmoz-collapse>
101
+ </div>
102
+ `,
103
+ settingsId &&
104
+ html`<div class="buttons">
105
+ <button
106
+ class="button reset"
107
+ @click=${onReset}
108
+ ?disabled=${!hasChanges}
109
+ >
110
+ ${_('Reset')}
111
+ </button>
112
+ <button class="button" @click=${onSave} ?disabled=${!hasChanges}>
113
+ ${_('Save')}
114
+ </button>
115
+ </div>`,
116
+ ].filter(Boolean);
117
+ },
118
+ Settings = ({ config }) => html`
119
+ <style>
120
+ ${dropdownStyle}
121
+ </style>
122
+ <cosmoz-dropdown
123
+ .render=${() => html`<cosmoz-omnitable-settings-ui
124
+ .config=${config}
125
+ ></cosmoz-omnitable-settings-ui>`}
126
+ .placement=${placement}
127
+ >
128
+ <svg viewBox="0 0 24 24" width="24" slot="button" fill="currentColor">
129
+ <path d="M10 18h5V5h-5v13zm-6 0h5V5H4v13zM16 5v13h5V5h-5z"></path>
130
+ </svg>
131
+ ${config?.badge ? html`<div class="badge" slot="button"></div>` : nothing}
132
+ </cosmoz-dropdown>
133
+ `;
134
+ customElements.define('cosmoz-omnitable-settings', component(Settings));
135
+ customElements.define('cosmoz-omnitable-settings-ui', component(SettingsUI));
@@ -0,0 +1,3 @@
1
+ import './cosmoz-omnitable-settings';
2
+
3
+ export { default as useSettings } from './use-settings';
@@ -0,0 +1,40 @@
1
+ const byName = (name) => (item) => item.name === name;
2
+
3
+ export default (columns = [], settings = [], savedSettings = []) => {
4
+ const cols = columns.filter(
5
+ (column) =>
6
+ !settings.some(byName(column.name)) &&
7
+ !savedSettings.some(byName(column.name))
8
+ ),
9
+ _savedSettings = savedSettings.filter(
10
+ (column) => !settings.some(byName(column.name))
11
+ );
12
+
13
+ return [
14
+ ...settings,
15
+ ..._savedSettings.flatMap((setting) => {
16
+ const column = columns.find((c) => c.name === setting.name);
17
+
18
+ if (!column) {
19
+ return [];
20
+ }
21
+
22
+ return {
23
+ ...setting,
24
+ title: column.title,
25
+ minWidth: parseInt(column.minWidth, 10),
26
+ };
27
+ }),
28
+ ...cols.map((column) => {
29
+ const { name, title, priority, minWidth, width, flex } = column;
30
+ return {
31
+ name,
32
+ title,
33
+ priority,
34
+ minWidth: parseInt(minWidth, 10),
35
+ width: parseInt(width, 10),
36
+ flex: parseInt(flex, 10),
37
+ };
38
+ }),
39
+ ];
40
+ };
@@ -0,0 +1,223 @@
1
+ import { tagged as css } from '@neovici/cosmoz-utils';
2
+ import { checkbox, sort as sortStyle } from '../../cosmoz-omnitable-styles';
3
+
4
+ export default css`
5
+ :host {
6
+ box-sizing: border-box;
7
+ display: flex;
8
+ flex-direction: column;
9
+ max-height: var(--ot-height, 60vh);
10
+ outline: none;
11
+ min-width: 270px;
12
+ }
13
+
14
+ .headline {
15
+ box-shadow: inset 0px -1px 0px rgba(0, 0, 0, 0.15),
16
+ inset 0px 1px 0px rgba(0, 0, 0, 0.15);
17
+ font-weight: 500;
18
+ font-size: 16px;
19
+ line-height: 0.95;
20
+ padding: 12px 14px 11px 14px;
21
+ display: flex;
22
+ }
23
+ .close {
24
+ border: none;
25
+ background: none;
26
+ cursor: pointer;
27
+ padding: 0;
28
+ margin-left: auto;
29
+ }
30
+
31
+ .contents {
32
+ overflow-y: auto;
33
+ scrollbar-width: 2px;
34
+ scrollbar-gutter: stable;
35
+ }
36
+ .contents::-webkit-scrollbar {
37
+ width: 2px;
38
+ }
39
+ .contents::-webkit-scrollbar-thumb {
40
+ background: rgba(0, 0, 0, 0.5);
41
+ }
42
+ .contents::-webkit-scrollbar-track-piece:start,
43
+ .contents::-webkit-scrollbar-track-piece:end {
44
+ background: transparent;
45
+ }
46
+
47
+ .heading {
48
+ box-shadow: inset 0px -1px 0px rgba(0, 0, 0, 0.15);
49
+ font-weight: 500;
50
+ font-size: 14px;
51
+ line-height: 0.95;
52
+ padding: 14px;
53
+ display: flex;
54
+ cursor: pointer;
55
+ align-items: center;
56
+ }
57
+ .heading svg {
58
+ margin-left: auto;
59
+ }
60
+ .heading[data-opened] svg {
61
+ transform: scaleY(-1);
62
+ }
63
+ cosmoz-collapse[opened] + .heading {
64
+ box-shadow: inset 0px -1px 0px rgba(0, 0, 0, 0.15),
65
+ inset 0px 1px 0px rgba(0, 0, 0, 0.15);
66
+ }
67
+
68
+ .list {
69
+ flex: 1;
70
+ padding: 2px 14px;
71
+ min-width: 232px;
72
+ }
73
+ .item {
74
+ display: flex;
75
+ align-items: center;
76
+ background: white;
77
+ }
78
+ .item.drag {
79
+ opacity: 0.6;
80
+ pointer-events: none;
81
+ }
82
+ .item.dragover {
83
+ box-shadow: 0 -2px 0 0 currentColor;
84
+ }
85
+ .pull {
86
+ border: none;
87
+ padding: 0;
88
+ font-size: 0;
89
+ vertical-align: bottom;
90
+ outline: none;
91
+ background: transparent;
92
+ cursor: move;
93
+ margin-right: 12px;
94
+ color: #c4c4c4;
95
+ }
96
+ .title {
97
+ flex: auto;
98
+ overflow: hidden;
99
+ text-overflow: ellipsis;
100
+ }
101
+ .title[has-filter] {
102
+ font-weight: bold;
103
+ }
104
+ ${checkbox}
105
+ .checkbox {
106
+ margin: 4px 0;
107
+ }
108
+
109
+ ${sortStyle}
110
+ .sort {
111
+ order: initial;
112
+ margin: 0;
113
+ width: auto;
114
+ padding: 8px;
115
+ }
116
+
117
+ .buttons {
118
+ display: flex;
119
+ gap: 8px;
120
+ padding: 12px 14px;
121
+ box-shadow: inset 0px 1px 0px rgba(0, 0, 0, 0.15);
122
+ }
123
+ .button {
124
+ border: none;
125
+ cursor: pointer;
126
+ background: var(--cosmoz-button-bg-color, #101010);
127
+ color: var(--cosmoz-button-color, #fff);
128
+ font-family: inherit;
129
+ font-size: 13px;
130
+ font-weight: 600;
131
+ line-height: 26px;
132
+ border-radius: 4px;
133
+ padding: 8px;
134
+ flex: 1;
135
+ }
136
+ .button:not(.reset):hover {
137
+ background: var(--cosmoz-button-hover-bg-color, #3a3f44);
138
+ }
139
+ .button[disabled] {
140
+ opacity: 0.2;
141
+ pointer-events: none;
142
+ }
143
+ .reset {
144
+ background: transparent;
145
+ color: inherit;
146
+ text-decoration: underline;
147
+ }
148
+ .groups {
149
+ display: grid;
150
+ column-gap: 7px;
151
+ row-gap: 8px;
152
+ grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
153
+ grid-template-rows: auto;
154
+ padding: 14px;
155
+ }
156
+ .group {
157
+ border: 1px solid rgba(0, 0, 0, 0.2);
158
+ border-radius: 2px;
159
+ font-size: 13px;
160
+ line-height: 1;
161
+ text-align: center;
162
+ padding: 6px 12px;
163
+ background: transparent;
164
+ cursor: pointer;
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ }
169
+ .group span {
170
+ overflow: hidden;
171
+ text-overflow: ellipsis;
172
+ }
173
+ .group[data-group] {
174
+ background: var(
175
+ --cosmoz-omnitable-checkbox-checked-color,
176
+ var(--primary-color)
177
+ );
178
+ }
179
+ .group svg {
180
+ margin-left: 4px;
181
+ flex: none;
182
+ vertical-align: middle;
183
+ }
184
+ .group:not([data-group]) svg {
185
+ display: none;
186
+ }
187
+ .group:not([data-group='desc']) svg {
188
+ transform: scaleY(-1);
189
+ }
190
+ `;
191
+
192
+ export const dropdown = css`
193
+ :host {
194
+ display: contents;
195
+ color: var(--cosmoz-omnitable-settings-color, #101010);
196
+ --cosmoz-dropdown-box-shadow: 0 3px 4px 0 rgb(0 0 0 / 14%),
197
+ 0 1px 8px 0 rgb(0 0 0 / 12%), 0 3px 3px -2px rgb(0 0 0 / 40%);
198
+ }
199
+ cosmoz-dropdown {
200
+ outline: none;
201
+ }
202
+ cosmoz-dropdown::part(button) {
203
+ --cosmoz-dropdown-button-size: 24px;
204
+ padding: 0;
205
+ background: transparent;
206
+ color: inherit;
207
+ }
208
+ cosmoz-dropdown::part(anchor) {
209
+ display: inline-block;
210
+ }
211
+ .badge {
212
+ position: absolute;
213
+ top: 1px;
214
+ right: -4px;
215
+ background-color: var(
216
+ --cosmoz-omnitable-checkbox-checked-color,
217
+ var(--primary-color)
218
+ );
219
+ width: 8px;
220
+ height: 8px;
221
+ border-radius: 100%;
222
+ }
223
+ `;
@@ -3,7 +3,7 @@ import { props } from '@neovici/cosmoz-utils/lib/object';
3
3
 
4
4
  const storagePrefix = 'omnitable-';
5
5
 
6
- export const useSavedSettings = (settingsId, settings, setSettings) => {
6
+ export default (settingsId, settings, setSettings) => {
7
7
  const
8
8
  [counter, setCounter] = useState(0),
9
9
  savedSettings = useMemo(() => {
@@ -19,9 +19,10 @@ export const useSavedSettings = (settingsId, settings, setSettings) => {
19
19
  }, [settingsId, counter]);
20
20
 
21
21
  return {
22
+ settingsId,
22
23
  savedSettings,
23
24
 
24
- onSettingsSave: useCallback(() => {
25
+ onSave: useCallback(() => {
25
26
  if (!settingsId) {
26
27
  return;
27
28
  }
@@ -36,7 +37,7 @@ export const useSavedSettings = (settingsId, settings, setSettings) => {
36
37
  }
37
38
  }, [settings]),
38
39
 
39
- onSettingsReset: e => {
40
+ onReset: e => {
40
41
  setSettings([]);
41
42
 
42
43
  if (e.shiftKey) {
@@ -49,6 +50,6 @@ export const useSavedSettings = (settingsId, settings, setSettings) => {
49
50
  }
50
51
  },
51
52
 
52
- hasChangedSettings: settings.length !== 0
53
+ hasChanges: settings.length !== 0
53
54
  };
54
55
  };