@neovici/cosmoz-omnitable 8.12.0 → 8.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,5 @@ cosmoz-omnitable
2
2
  =================
3
3
 
4
4
  [![Build Status](https://github.com/Neovici/cosmoz-omnitable/workflows/Github%20CI/badge.svg)](https://github.com/Neovici/cosmoz-omnitable/actions?workflow=Github+CI)
5
- [![Maintainability](https://api.codeclimate.com/v1/badges/6b16292868f47977eee2/maintainability)](https://codeclimate.com/github/Neovici/cosmoz-omnitable/maintainability)
6
5
  [![codecov](https://codecov.io/gh/Neovici/cosmoz-omnitable/branch/master/graph/badge.svg?token=j46iVMxjcs)](https://codecov.io/gh/Neovici/cosmoz-omnitable)
7
6
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
@@ -50,7 +50,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
50
50
  <input class="checkbox all" type="checkbox" checked="[[ _allSelected ]]" on-input="_onAllCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
51
51
  <cosmoz-omnitable-header-row
52
52
  data="[[ data ]]"
53
- columns="[[ normalizedColumns ]]"
53
+ columns="[[ columns ]]"
54
54
  filters="[[ filters ]]"
55
55
  group-on-column="[[ groupOnColumn ]]"
56
56
  set-filter-state="[[ setFilterState ]]"
@@ -98,7 +98,7 @@ class Omnitable extends hauntedPolymer(useOmnitable)(mixin({ isEmpty }, translat
98
98
  <div class="item-row-wrapper">
99
99
  <div selected$="[[ selected ]]" class="itemRow">
100
100
  <input class="checkbox" type="checkbox" checked="[[ selected ]]" on-input="_onCheckboxChange" disabled$="[[ !_dataIsValid ]]" />
101
- <cosmoz-omnitable-item-row columns="[[ normalizedColumns ]]"
101
+ <cosmoz-omnitable-item-row columns="[[ columns ]]"
102
102
  selected="[[ selected ]]" expanded="{{ expanded }}" item="[[ item ]]" group-on-column="[[ groupOnColumn ]]"
103
103
  on-item-change="[[ onItemChange ]]">
104
104
  </cosmoz-omnitable-item-row>
@@ -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.13.0",
4
4
  "description": "[![Build Status](https://travis-ci.org/Neovici/cosmoz-omnitable.svg?branch=master)](https://travis-ci.org/Neovici/cosmoz-omnitable)",
5
5
  "keywords": [
6
6
  "web-components"