wyser
Version:
JavaScript SPA (Single Page Application) Framework
163 lines (123 loc) • 5.17 kB
JavaScript
import { Next } from "../App";
import Router from "../Router";
class Components {
constructor () {
if (!Components.instance) {
this._components = {};
this._navComponents = {};
this._oldNavItems = {};
this._events = {
beforeLoad: [],
loaded: []
}
Components.instance = this;
}
return Components.instance;
}
add (name, component) {
if (component.nav)
this._navComponents[name] = component;
this._components[name] = component;
};
exists (name) {
return this._components[name] ? true : false;
}
use (name) {
if (!this._components[name])
throw `Could not find component (${name})`;
return this._components[name];
}
isInScope (scope, routeName) {
if (scope && scope.includes(routeName) || scope == 'all')
return true;
return false;
};
iterateOverComponents (callback) {
for (const component in this._navComponents) {
if (!this._navComponents.hasOwnProperty(component))
continue;
if (!this.isInScope(this._navComponents[component].scope, Router.currentRoute.name))
continue;
callback(this._navComponents[component]);
}
};
iterateOverNavItems (component, callback) {
let { parent, targets } = component.nav;
const parents = document.getElementsByClassName(parent);
Array.from(parents).forEach(parent => {
const _targets = Array.isArray(targets) ? targets : [targets];
_targets.forEach(target => {
const targetElements = parent.getElementsByClassName(target);
Array.from(targetElements).forEach(targetElement => {
callback(targetElement);
});
});
});
};
initHighlight (navItem, component) {
if (!navItem.dataset.linkaddress || !navItem.dataset.linkactive)
return;
// if current path matches nav item, activate the item, remember it and return;
if (navItem.dataset.linkaddress == location.pathname) {
this._oldNavItems[component.name] = navItem;
return navItem.classList.add(navItem.dataset.linkactive);
}
if (navItem.dataset.linkmultiple && component.nav && component.nav.linkmultiple) {
// Grouped routes, essentially same page with multiple tabs
const tabs = component.nav.linkmultiple[navItem.dataset.linkmultiple];
// if page (route) to render is a tab within @var tabs, make it active
if (this.isInScope(tabs, Router.currentRoute.name)) {
navItem.classList.add(navItem.dataset.linkactive);
this._oldNavItems[component.name] = navItem;
}
}
}
highlightNavs () {
this.iterateOverComponents((component) => {
this.iterateOverNavItems(component, (navItem) => {
this.initHighlight(navItem, component);
});
});
}
onClick () {
this.iterateOverComponents((component) => {
this.iterateOverNavItems(component, (navItem) => {
if (!navItem.dataset.linkaddress || !navItem.dataset.linkactive)
return;
// the cloning and replacing process, removes old events and prevents
// the same event being fired multiple times
const navItemClone = navItem.cloneNode(true);
navItem.replaceWith(navItemClone);
navItemClone.addEventListener('click', async (e) => {
// replace components with new ones, corresponding to the new page
Next(e.currentTarget.dataset.linkaddress);
// de-activate previosly activated navItems
if (this._oldNavItems[component.name])
this._oldNavItems[component.name].classList.remove(e.currentTarget.dataset.linkactive);
// if some nav components have been modified, re-set events
this.onClick();
// if there are new components that need highlighting, highlight them
this.highlightNavs();
});
});
});
}
initNavEvents () {
this.onClick();
};
initEvents (type) {
this._events[type].forEach(componentName => {
const component = this._components[componentName];
if (!this.isInScope(component.scope, Router.currentRoute.name))
return;
component.events[type](this._components[componentName]);
});
}
beforeLoad (name) {
this._events.beforeLoad.push(name);
}
loaded (name) {
this._events.loaded.push(name);
}
};
export default new Components;