@neovici/cosmoz-omnitable 8.12.0 → 8.14.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/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)
@@ -348,7 +348,7 @@ export default `<style>
348
348
  }
349
349
 
350
350
  .itemRow[selected] {
351
- background-color: var(--cosmoz-omnitable-selection-color, rgb(195, 212, 248)) !important;
351
+ background-color: var(--cosmoz-omnitable-selection-color, rgb(195, 212, 248));
352
352
  @apply --cosmoz-omnitable-selected-row;
353
353
  }
354
354
 
@@ -15,7 +15,6 @@ import './cosmoz-omnitable-group-row';
15
15
  import './cosmoz-omnitable-columns';
16
16
  import styles from './cosmoz-omnitable-styles';
17
17
 
18
-
19
18
  import { PolymerElement } from '@polymer/polymer/polymer-element';
20
19
  import { html } from '@polymer/polymer/lib/utils/html-tag';
21
20
  import { html as litHtml } from 'lit-html';
@@ -50,7 +49,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
50
49
  <input class="checkbox all" type="checkbox" checked="[[ _allSelected ]]" on-input="_onAllCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
51
50
  <cosmoz-omnitable-header-row
52
51
  data="[[ data ]]"
53
- columns="[[ normalizedColumns ]]"
52
+ columns="[[ columns ]]"
54
53
  filters="[[ filters ]]"
55
54
  group-on-column="[[ groupOnColumn ]]"
56
55
  set-filter-state="[[ setFilterState ]]"
@@ -96,11 +95,11 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
96
95
  >
97
96
  <template slot="templates" data-type="item">
98
97
  <div class="item-row-wrapper">
99
- <div selected$="[[ selected ]]" class="itemRow">
98
+ <div selected$="[[ selected ]]" part="itemRow itemRow-[[ index ]]" data-index$="[[ index ]]" class="itemRow">
100
99
  <input class="checkbox" type="checkbox" checked="[[ selected ]]" on-input="_onCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
101
- <cosmoz-omnitable-item-row columns="[[ normalizedColumns ]]"
100
+ <cosmoz-omnitable-item-row columns="[[ columns ]]"
102
101
  selected="[[ selected ]]" expanded="{{ expanded }}" item="[[ item ]]" group-on-column="[[ groupOnColumn ]]"
103
- on-item-change="[[ onItemChange ]]">
102
+ on-item-change="[[ onItemChange ]]" on-click="onItemClick">
104
103
  </cosmoz-omnitable-item-row>
105
104
  <paper-icon-button
106
105
  class="expand"
@@ -132,7 +131,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
132
131
  </div>
133
132
  </div>
134
133
  <div class="footer">
135
- <div class="footer-controls">
134
+ <div class="footer-controls" part="footer-controls">
136
135
  <cosmoz-autocomplete
137
136
  label="[[ _('Group on', t) ]] [[ _computeSortDirection(groupOnDescending, t) ]]" placeholder="[[ _('No grouping', t) ]]"
138
137
  source="[[ _onCompleteValues(columns, 'groupOn', groupOnColumn) ]]" value="[[ groupOnColumn ]]" limit="1" text-property="title"
@@ -546,6 +545,27 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
546
545
  value && close(); /* eslint-disable-line no-unused-expressions */
547
546
  };
548
547
  }
548
+
549
+ onItemClick(e) {
550
+ const composedPath = e.composedPath(),
551
+ path = composedPath.slice(0, composedPath.indexOf(e.currentTarget));
552
+
553
+ if (path.find((e) => e.matches?.('a, .checkbox, .expand'))) {
554
+ return;
555
+ }
556
+
557
+
558
+ this.dispatchEvent(
559
+ new window.CustomEvent('omnitable-item-click', {
560
+ bubbles: true,
561
+ composed: true,
562
+ detail: {
563
+ item: e.model.item,
564
+ index: e.model.index,
565
+ },
566
+ })
567
+ );
568
+ }
549
569
  }
550
570
  customElements.define('cosmoz-omnitable', Omnitable);
551
571
 
@@ -85,7 +85,7 @@ const placement = ['bottom-right', ...defaultPlacement],
85
85
  ${_('Columns')} ${arrow}
86
86
  </div>
87
87
  <cosmoz-collapse ?opened=${opened.columns}>
88
- <div class="list">${settings?.map(renderItem(thru))}</div>
88
+ <div class="list">${settings.columns?.map(renderItem(thru))}</div>
89
89
  </cosmoz-collapse>
90
90
 
91
91
  <div
@@ -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,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([]),
6
+ export default ({ settingsId, ...thru }) => {
7
+ const [settings, setSettings] = useState(),
8
8
  [opened, setOpened] = useState({ columns: true, sort: true }),
9
- { savedSettings, ...thru } = useSavedSettings(
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.12.0",
3
+ "version": "8.14.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"