js/template/NavigationTemplate.js
import Template from '~/template/Template';
import SidebarNavigationViewController, { ACTIVE_LINK_OPEN } from '~/controllers/SidebarNavigationViewController';
import { fromEvent, BehaviorSubject } from 'rxjs';
import { tap, distinctUntilChanged, map, filter, mapTo } from 'rxjs/operators';
/**
* Navigation template for friendly nav.
*/
export default class NavigationTemplate extends Template {
/**
* Creates a nav from an object
* @param {Object} object
* @return {Array<Array<string, string>>}
*/
static navFromObject(object) {
return Object
.keys(object)
.map(sectionName => [
sectionName,
Object
.keys(object[sectionName])
.map(itemName => [itemName])
])
}
/**
* Takes dyadic-tuple in form of array. Followed by elements and if the
* second element is href, this is used when emitting clicks.
* @param {Array<Array<string, string>>} nav Navigation data
* @param {Object} opts initialization options
* @param {?Array<?string, ?string>} firstItem - Array in form of section name
* and item name.
*/
constructor(nav, {
firstItem: [
firstSectionName = nav[0][0],
firstItemName = nav[0][1][0][0]
] = []
} = {}) {
const root = <nav class="sidebar-navigation"/>;
super(root);
let navigationController;
const navigationSubject = new BehaviorSubject(null)
.pipe(
filter(value => value),
distinctUntilChanged());
const sections = nav.map(([sectionName, items]) => {
const sectionItems = items.map(([itemName]) => {
const link = <a class="sidebar-navigation__link">{ itemName }</a>;
fromEvent(link, 'click')
.pipe(
mapTo([ sectionName, itemName ]))
.subscribe(navigationSubject);
navigationSubject
.pipe(
map(([section, item]) => section === sectionName && item === itemName),
distinctUntilChanged())
.subscribe(isCurrentLinkSelected => {
link.classList.toggle(ACTIVE_LINK_OPEN, isCurrentLinkSelected);
});
return (
<li>
{ link }
</li>
);
});
const sublist = <ul>{ sectionItems }</ul>;
const section = <a class="sidebar-navigation__title">{ sectionName }</a>;
navigationSubject
.pipe(
filter(([section]) => section === sectionName),
distinctUntilChanged())
.subscribe(() => {
const openSections = [...navigationController.getOpenSections()];
// If we navigate to a non-open link, then we'll still open
if (!openSections.includes(section)) {
navigationController.beginQueue();
navigationController.toggleSection(sublist, true);
navigationController.finishQueue();
}
});
return (
<li>
{ section }
{ sublist }
</li>
);
});
root.appendChild(
<ul>
{ sections }
</ul>
);
navigationController = new SidebarNavigationViewController(root);
/** @private */
this.firstItem = [firstSectionName, firstItemName];
/** @private */
this.navigationSubject = navigationSubject;
}
/**
* Called on didLoad
*/
didLoad() {
super.didLoad();
this.navigationSubject.next(this.firstItem);
}
/**
* Returns navigation subject. Emits non-duplicates in form of
* like [section, item].
* @return {BehaviorSubject<Array<string, string>>}
*/
observeNavigation() {
return this.navigationSubject;
}
}