Skip to content

Commit 10f4b21

Browse files
committed
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. The only thing missing is the transition animation, 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.
1 parent 2c716ac commit 10f4b21

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

cosmoz-data-nav-pure.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {
2+
component, useState, useCallback
3+
} from 'haunted';
4+
5+
import { useNotifyProperty } from '@neovici/cosmoz-utils/lib/hooks/use-notify-property';
6+
import { useMaintainSelection } from './lib/use-maintain-selection';
7+
import { useTriggerPreload } from './lib/use-trigger-preload';
8+
9+
const
10+
useCosmozDataNav = host => {
11+
const
12+
{
13+
items,
14+
renderItem,
15+
maintainSelection
16+
} = host,
17+
[selected, setSelected] = useState(0);
18+
19+
useTriggerPreload(host, items, selected);
20+
useMaintainSelection(items, selected, maintainSelection, setSelected);
21+
useNotifyProperty('selected', selected);
22+
23+
return {
24+
items,
25+
renderItem,
26+
selected,
27+
advance: useCallback(n => setSelected(selected => selected + n), [])
28+
};
29+
},
30+
31+
renderCosmozDataNav = ({
32+
items,
33+
renderItem,
34+
selected,
35+
advance
36+
}) => items[selected] && renderItem(items[selected], selected, items, advance),
37+
38+
CosmozDataNavPure = host => renderCosmozDataNav(useCosmozDataNav(host));
39+
40+
customElements.define('cosmoz-data-nav-pure', component(CosmozDataNavPure, { useShadowDOM: false }));

lib/use-maintain-selection.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {
2+
useState, useEffect
3+
} from 'haunted';
4+
5+
export const useMaintainSelection = (items, selected, maintainSelection, setSelected) => {
6+
const [selectedItem, setSelectedItem] = useState(items?.[selected]);
7+
8+
useEffect(() => {
9+
setSelectedItem(items?.[selected]);
10+
}, [items, selected]);
11+
12+
useEffect(() => {
13+
if (maintainSelection === false) {
14+
setSelected(0);
15+
return;
16+
}
17+
18+
const index = items.findIndex(item => (item?.id || item) === (selectedItem?.id || selectedItem));
19+
20+
if (index === -1) {
21+
setSelected(0);
22+
return;
23+
}
24+
25+
if (index === selected) {
26+
return;
27+
}
28+
29+
setSelected(index);
30+
}, [items]); // We intentionally only run this code when `items` changes.
31+
};

lib/use-trigger-preload.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useEffect } from 'haunted';
2+
3+
const needData = (host, item) => item != null && typeof item !== 'object' && host.dispatchEvent(new CustomEvent('need-data', { detail: { id: item }}));
4+
5+
export const useTriggerPreload = (host, items, selected) =>
6+
useEffect(() => {
7+
if (items.length === 0) {
8+
return;
9+
}
10+
11+
needData(host, items[selected - 1]);
12+
needData(host, items[selected]);
13+
needData(host, items[selected + 1]);
14+
}, [items, selected]);

0 commit comments

Comments
 (0)