stem-core
Version:
Frontend and core-library framework
286 lines (244 loc) • 8.79 kB
JSX
import {UI} from "../UI";
import {Switcher} from "../Switcher";
import {Link} from "../UIPrimitives";
import {FACollapseIcon} from "../FontAwesome"; //TODO: more flexibility, do not require FAIcons in NavElements
import {SessionStorageMap} from "../../base/StorageMap";
import {unwrapArray} from "../../base/Utils";
import {Orientation, Direction} from "../Constants";
let navSessionManager = new SessionStorageMap("navManager");
const BasicOrientedElementInterface = (BaseClass) => class BasicOrientedElement extends BaseClass {
get styleSheet() {
return this.options.styleSheet || this.parent.styleSheet;
}
getOrientation() {
if (this.options.orientation) {
return this.options.orientation;
}
if (this.parent && typeof this.parent.getOrientation === "function") {
return this.parent.getOrientation();
}
return Orientation.HORIZONTAL;
}
};
const BasicOrientedElement = BasicOrientedElementInterface(UI.Element);
const BasicOrientedLinkElement = BasicOrientedElementInterface(Link);
// NavElements should know if they are in vertical or horizontal mode, so they can behave differently
const NavElementInterface = (BaseClass) => class NavElement extends BaseClass {
constructor(...args) {
super(...args);
this.isToggled = this.getToggledState();
}
extraNodeAttributes(attr) {
super.extraNodeAttributes(attr);
if (this.getOrientation() === Orientation.HORIZONTAL) {
// it is in the navbar
attr.addClass(this.styleSheet.navElementHorizontal);
if (this.parent instanceof NavSection) {
attr.setStyle("float", "left");
} else {
// it is an element in a dropdown
attr.addClass(this.styleSheet.navCollapseElement);
}
} else {
// it is in the sidebar
attr.addClass(this.styleSheet.navElementVertical);
}
}
getSelf() {
const style = (this.getOrientation() === Orientation.HORIZONTAL ?
this.styleSheet.navElementValueHorizontal : this.styleSheet.navElementValueVertical);
const marginLeft = this.getOrientation() === Orientation.VERTICAL && unwrapArray(this.render()).length ? "-20px" : "0";
return <BasicOrientedElement className={style} style={{marginLeft: marginLeft}}>
{this.getValue()}
</BasicOrientedElement>;
}
getSubElements() {
let childrenToRender = unwrapArray(this.render());
if (childrenToRender.length) {
let subElementsClass;
if (!this.isToggled) {
subElementsClass = "hidden";
}
return <BasicOrientedElement ref="contentArea" className={subElementsClass}>
{childrenToRender}
</BasicOrientedElement>;
}
return null;
}
getValue() {
let result;
if (unwrapArray(this.render()).length) {
if (this.getOrientation() === Orientation.VERTICAL) {
// is in the sidebar
result = [
<FACollapseIcon ref="collapseIcon" collapsed={!this.isToggled} className={this.styleSheet.navElementVerticalArrow} />,
this.options.value
];
} else if (this.getOrientation() === Orientation.HORIZONTAL) {
// is in the navbar
result = [
this.options.value,
<FACollapseIcon collapsed={false} className={this.styleSheet.navElementHorizontalArrow} />,
];
}
} else {
result = this.options.value;
}
return result;
}
getChildrenToRender() {
return [
this.getSelf(),
this.getSubElements(),
];
}
showChildren() {
this.contentArea.removeClass("hidden");
}
hideChildren() {
this.contentArea.addClass("hidden");
}
toggleChildren() {
if (!unwrapArray(this.render()).length) {
return;
}
if (!this.isToggled) {
this.showChildren();
} else {
this.hideChildren();
}
if (this.collapseIcon) {
this.collapseIcon.setCollapsed(this.isToggled);
}
this.isToggled = !this.isToggled;
this.saveToggledState();
}
getSessionKeyName() {
let sessionKeyName = this.options.sessionKey || this.options.href;
if (!sessionKeyName) {
throw Error("Persistent nav element needs a unique session key!");
}
return sessionKeyName;
}
getLocalToggledState() {
if (this.hasOwnProperty("isToggled")) {
return !!this.isToggled;
}
return !!this.options.defaultToggled;
}
getToggledState() {
if (!this.options.persistent) {
return this.getLocalToggledState();
}
let sessionKeyName = this.getSessionKeyName();
return navSessionManager.get(sessionKeyName, this.getLocalToggledState());
}
saveToggledState() {
if (!this.options.persistent) {
return;
}
let sessionKeyName = this.getSessionKeyName();
navSessionManager.set(sessionKeyName, this.getLocalToggledState());
}
onMount() {
super.onMount();
this.addNodeListener("mouseenter", () => {
if (this.getOrientation() === Orientation.HORIZONTAL && unwrapArray(this.render()).length) {
this.showChildren();
}
});
this.addNodeListener("mouseleave", () => {
if (this.getOrientation() === Orientation.HORIZONTAL && unwrapArray(this.render()).length) {
this.hideChildren();
}
});
this.addClickListener((event) => {
if (this.getOrientation() === Orientation.VERTICAL) {
event.stopPropagation();
this.toggleChildren();
}
});
}
};
const NavElement = NavElementInterface(UI.Primitive(BasicOrientedElement, "li"));
class NavLinkElement extends NavElementInterface(BasicOrientedLinkElement) {
extraNodeAttributes(attr) {
super.extraNodeAttributes(attr);
attr.addClass(this.styleSheet.navLinkElement);
}
render() {
return this.options.children;
}
}
class NavSection extends UI.Primitive("ul") {
get styleSheet() {
return this.options.styleSheet || this.parent.styleSheet;
}
extraNodeAttributes(attr) {
if (this.getOrientation() === Orientation.HORIZONTAL) {
// it is in the navbar
attr.addClass(this.styleSheet.navSectionHorizontal);
// this is functionality, should be isolated from the actual design
attr.setStyle("float", this.getAnchor());
} else {
// it is in the sidebar
attr.addClass(this.styleSheet.navSectionVertical);
}
}
getAnchor() {
return this.options.anchor || Direction.LEFT;
}
getOrientation() {
return this.parent.getOrientation();
}
}
class NavAnchoredNotifications extends NavSection {
extraNodeAttributes(attr) {
super.extraNodeAttributes(attr);
attr.setStyle({
position: "relative",
});
}
getSwitcherStyle() {
return {
position: "absolute",
maxWidth: "calc(100vw - 76px)",
top: "50px",
right: "0",
height: "300px",
width: "400px",
boxShadow: "0px 0px 10px #666",
zIndex: "-1",
};
}
render() {
return [
this.options.children,
<Switcher ref="switcher" style={this.getSwitcherStyle()} className="hidden" />
];
}
show(content, child) {
this.activeChild = child;
this.switcher.removeClass("hidden");
this.switcher.setActive(content, child);
this.bodyListener = document.body.addEventListener("click", () => this.hide());
}
hide() {
this.switcher.addClass("hidden");
this.activeChild = null;
document.body.removeEventListener("click", this.bodyListener);
}
onMount() {
this.addListener("changeSwitcher", (content, child) => {
if (this.activeChild == child) {
this.hide();
} else {
this.show(content, child);
}
});
this.switcher.addClickListener((event) => {
event.stopPropagation();
});
}
}
export {BasicOrientedElement, NavElement, NavLinkElement, NavSection, NavAnchoredNotifications, navSessionManager};