From 2c716ac565504efcdfa885db1bd897c4bb3e5eda Mon Sep 17 00:00:00 2001 From: Cristian Necula Date: Wed, 30 Sep 2020 13:20:54 +0300 Subject: [PATCH 1/2] chore: update cosmoz-utils --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d894c8..658f976 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1276,9 +1276,9 @@ } }, "@neovici/cosmoz-utils": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@neovici/cosmoz-utils/-/cosmoz-utils-3.8.0.tgz", - "integrity": "sha512-VagVI8zRGh6bwGhLksrbSamgAHWKHIPhCOVMMn64hVKZvHZZl+tDxK/XskIkzNJXgvC1QCpqCkfed92mvKDN5A==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@neovici/cosmoz-utils/-/cosmoz-utils-3.13.0.tgz", + "integrity": "sha512-Ui/6/8PNFDlMl4BXjPMsi3ysU/+Ll7SGM+DFAdGZ2b6PsS6k7GUcdfmkW5D6zFcWws0e9aFBWlwgZlPzy/Dk9Q==", "requires": { "haunted": "^4.0.0" } @@ -9244,7 +9244,7 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true } @@ -9614,7 +9614,7 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true } diff --git a/package.json b/package.json index e71cbe0..dc60bdb 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@neovici/cosmoz-bottom-bar": "^3.0.10", "@neovici/cosmoz-i18next": "^3.2.2", "@neovici/cosmoz-page-router": "^6.0.5", - "@neovici/cosmoz-utils": "^3.8.0", + "@neovici/cosmoz-utils": "^3.13.0", "@polymer/iron-resizable-behavior": "^3.0.1", "@polymer/paper-icon-button": "^3.0.0", "@polymer/paper-spinner": "^3.0.0", From d5856526ab4d89d04b78f3fc6b5f973e44b9fcf9 Mon Sep 17 00:00:00 2001 From: Cristian Necula Date: Wed, 30 Sep 2020 13:54:26 +0300 Subject: [PATCH 2/2] feat(cosmoz-data-nav-pure): haunted implementation I decided to have a whack at a more declarative data-nav. The result is a very reliable data-nav that does away with all of the complexity of keeping an item cache and mutable data. It relies on immutable data being passed from the component above. It keeps the maintain selection and need-data behaviour. It's missing the transition animation and the incomplete template, which I will add in a follow-up PR. This is meant to be used only with renderItem functions that render haunted views. This is not tested with polymer views and will likely cause bugs, due to polymer's use of mutable state. --- cosmoz-data-nav-pure.js | 40 +++++++++++++++++++++++++++++++++++ lib/use-maintain-selection.js | 31 +++++++++++++++++++++++++++ lib/use-trigger-preload.js | 14 ++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 cosmoz-data-nav-pure.js create mode 100644 lib/use-maintain-selection.js create mode 100644 lib/use-trigger-preload.js diff --git a/cosmoz-data-nav-pure.js b/cosmoz-data-nav-pure.js new file mode 100644 index 0000000..dbbe746 --- /dev/null +++ b/cosmoz-data-nav-pure.js @@ -0,0 +1,40 @@ +import { + component, useState, useCallback +} from 'haunted'; + +import { useNotifyProperty } from '@neovici/cosmoz-utils/lib/hooks/use-notify-property'; +import { useMaintainSelection } from './lib/use-maintain-selection'; +import { useTriggerPreload } from './lib/use-trigger-preload'; + +const + useCosmozDataNav = host => { + const + { + items, + renderItem, + maintainSelection + } = host, + [selected, setSelected] = useState(0); + + useTriggerPreload(host, items, selected); + useMaintainSelection(items, selected, maintainSelection, setSelected); + useNotifyProperty('selected', selected); + + return { + items, + renderItem, + selected, + advance: useCallback(n => setSelected(selected => selected + n), []) + }; + }, + + renderCosmozDataNav = ({ + items, + renderItem, + selected, + advance + }) => items[selected] && renderItem(items[selected], selected, items, advance), + + CosmozDataNavPure = host => renderCosmozDataNav(useCosmozDataNav(host)); + +customElements.define('cosmoz-data-nav-pure', component(CosmozDataNavPure, { useShadowDOM: false })); diff --git a/lib/use-maintain-selection.js b/lib/use-maintain-selection.js new file mode 100644 index 0000000..6c2d845 --- /dev/null +++ b/lib/use-maintain-selection.js @@ -0,0 +1,31 @@ +import { + useState, useEffect +} from 'haunted'; + +export const useMaintainSelection = (items, selected, maintainSelection, setSelected) => { + const [selectedItem, setSelectedItem] = useState(items?.[selected]); + + useEffect(() => { + setSelectedItem(items?.[selected]); + }, [items, selected]); + + useEffect(() => { + if (maintainSelection === false) { + setSelected(0); + return; + } + + const index = items.findIndex(item => (item?.id || item) === (selectedItem?.id || selectedItem)); + + if (index === -1) { + setSelected(0); + return; + } + + if (index === selected) { + return; + } + + setSelected(index); + }, [items]); // We intentionally only run this code when `items` changes. +}; diff --git a/lib/use-trigger-preload.js b/lib/use-trigger-preload.js new file mode 100644 index 0000000..f44ac4c --- /dev/null +++ b/lib/use-trigger-preload.js @@ -0,0 +1,14 @@ +import { useEffect } from 'haunted'; + +const needData = (host, item) => item != null && typeof item !== 'object' && host.dispatchEvent(new CustomEvent('need-data', { detail: { id: item }})); + +export const useTriggerPreload = (host, items, selected) => + useEffect(() => { + if (items.length === 0) { + return; + } + + needData(host, items[selected - 1]); + needData(host, items[selected]); + needData(host, items[selected + 1]); + }, [items, selected]);