stem-core
Version:
Frontend and core-library framework
414 lines (350 loc) • 12.2 kB
JSX
import {UI, changeParent, registerStyle} from "../UI";
import {NavStyle} from "./NavStyle";
import {Carousel, CarouselStyle} from "../Carousel";
import {LeftSideNavIcon, RightSideNavIcon, WrappedNavIcon} from "./NavIcon";
import {BasicOrientedElement, NavSection} from "./NavElement";
import {initializeSwipeEvents} from "./NavSwipeDetection";
import {SessionStorageMap} from "../../base/StorageMap";
import {Orientation, Direction} from "../Constants";
class SidePanelGroup extends UI.Element {
get styleSheet() {
return this.options.styleSheet || this.parent.styleSheet;
}
extraNodeAttributes(attr) {
attr.addClass(this.styleSheet.sidePanelGroup);
if (this.options.anchor === Direction.RIGHT) {
attr.setStyle("right", 0);
} else {
attr.setStyle("width", "250px");
}
}
getOrientation() {
return Orientation.VERTICAL;
}
}
class SidePanel extends UI.Element {
constructor(...args) {
super(...args);
this.initNode();
this.applyVisibility();
}
initNode() {
if (!this.node) {
this.mount(document.body);
}
}
applyVisibility() {
if (this.options.name) {
this.storageSerializer = new SessionStorageMap("sidePanel" + this.options.name);
this.visible = this.storageSerializer.get("visible");
}
if (this.visible) {
this.show();
} else {
this.hide();
}
}
extraNodeAttributes(attr) {
if (this.options.anchor === Direction.RIGHT) {
attr.addClass(this.styleSheet.rightSidePanel);
attr.setStyle("right", "0");
} else {
attr.addClass(this.styleSheet.leftSidePanel);
}
}
setVisible(value) {
this.visible = value;
if (this.storageSerializer) {
this.storageSerializer.set("visible", value);
}
}
show() {
if (this.options.anchor === Direction.RIGHT) {
this.removeClass(this.styleSheet.navVerticalRightHide);
} else {
this.removeClass(this.styleSheet.navVerticalLeftHide);
}
this.setVisible(true);
}
hide() {
if (this.options.anchor === Direction.RIGHT) {
this.addClass(this.styleSheet.navVerticalRightHide);
} else {
this.addClass(this.styleSheet.navVerticalLeftHide);
}
this.setVisible(false);
}
toggle() {
if (this.visible) {
this.hide();
} else {
this.show();
}
}
getChildrenToRender() {
return <SidePanelGroup ref="this.wrappedPanel" anchor={this.options.anchor}>
{this.render()}
</SidePanelGroup>;
}
onMount() {
this.addClickListener((event) => {
event.stopPropagation();
});
}
}
class NavCarouselStyle extends CarouselStyle {
hoverColor = () => NavStyle.getInstance().getColors().sidepanelHover;
textColor = () => NavStyle.getInstance().getColors().text;
navigatorTransitionTime = () => NavStyle.getInstance().dimensions.backgroundTransitionDuration;
}
class NavManager extends UI.Primitive("nav") {
getCarouselStyleSheet() {
return this.options.carouselStyleSheet || NavCarouselStyle.getInstance();
}
getDefaultOptions() {
return {
persistentLeftSidePanel: true,
persistentRightSidePanel: true,
};
}
initLeftSidePanel() {
this.leftSidePanel = <SidePanel anchor={Direction.LEFT} name="left" persistent={this.options.persistentLeftSidePanel}>
<Carousel ref={this.refLink("carousel")} styleSheet={this.getCarouselStyleSheet()}>
<BasicOrientedElement orientation={Orientation.VERTICAL} ref={this.refLink("navigationPanel")}
styleSheet={this.styleSheet}>
{this.getLeftSidePanelChildren()}
</BasicOrientedElement>
</Carousel>
</SidePanel>;
}
initRightSidePanel() {
this.rightSidePanel = <SidePanel anchor={Direction.RIGHT} name="right" persistent={this.options.persistentRightSidePanel}>
{this.getRightSidePanelChildren()}
</SidePanel>;
}
constructor(options) {
super(options);
this.initLeftSidePanel();
this.initRightSidePanel();
}
getLeftSidePanelChildren() {
return [];
}
getRightSidePanelChildren() {
return [];
}
getLeftConditionedChildren() {
return [];
}
getRightConditionedChildren() {
return [];
}
extraNodeAttributes(attr) {
attr.addClass(this.styleSheet.navManager);
}
getOrientation() {
return Orientation.HORIZONTAL;
}
leftSideIconAction() {
if (this.wrapped) {
if (this.carousel.getActive() === this.navigationPanel) {
this.toggleLeftSidePanel();
} else {
this.carousel.setActive(this.navigationPanel);
if (!this.leftSidePanel.visible) {
this.toggleLeftSidePanel();
}
}
} else {
this.toggleLeftSidePanel();
}
}
// TODO: lots of duplicate code here, with left/right stuff
getLeftSideIcon() {
if (!this.leftSidePanel) {
return null;
}
if (!this.leftPanelToggler) {
this.leftPanelToggler = <LeftSideNavIcon onClick={() => this.leftSideIconAction()} />;
}
return this.leftPanelToggler;
}
rightSideIconAction() {
this.toggleRightSidePanel();
}
getRightSideIcon() {
if (!this.rightSidePanel) {
return null;
}
if (!this.rightPanelToggler) {
this.rightPanelToggler = <RightSideNavIcon onClick={() => this.rightSideIconAction()} />;
}
return this.rightPanelToggler;
}
getFixedWidth() {
let width = 10;
for (let child of this.children) {
width += child.getWidth();
}
width -= this.getLeftConditioned().getWidth();
width -= this.getRightConditioned().getWidth();
return width;
}
wrappedIconAction() {
if (this.wrapped) {
if (this.carousel.getActive() === this.wrappedPanel) {
this.toggleLeftSidePanel();
} else {
this.carousel.setActive(this.wrappedPanel);
if (!this.leftSidePanel.visible) {
this.toggleLeftSidePanel();
}
}
} else {
this.toggleLeftSidePanel();
}
}
getWrappedIcon() {
if (!this.wrappedToggler) {
this.wrappedToggler = <WrappedNavIcon onClick={() => this.wrappedIconAction()}
className={this.wrapped ? "" : "hidden"} />;
}
return this.wrappedToggler;
}
getLeftFixed() {
return [];
}
getRightFixed() {
return [];
}
getLeftConditionedWrapper() {
if (!this.leftConditionedWrapper) {
this.leftConditionedWrapper = <NavSection anchor={Direction.LEFT}>
{this.getLeftConditioned()}
</NavSection>;
}
return this.leftConditionedWrapper;
}
getRightConditionedWrapper() {
if (!this.rightConditionedWrapper) {
this.rightConditionedWrapper = <NavSection anchor={Direction.RIGHT}>
{this.getRightConditioned()}
</NavSection>;
}
return this.rightConditionedWrapper;
}
getLeftConditioned() {
if (!this.leftConditioned) {
this.leftConditioned = <NavSection>{this.getLeftConditionedChildren()}</NavSection>;
}
return this.leftConditioned;
}
getRightConditioned() {
if (!this.rightConditioned) {
this.rightConditioned = <NavSection>{this.getRightConditionedChildren()}</NavSection>;
}
return this.rightConditioned;
}
toggleSidePanel(mainPanel, toggleEvent) {
let secondaryPanel = (mainPanel == this.leftSidePanel ? this.rightSidePanel : this.leftSidePanel);
mainPanel.toggle();
this.dispatch(toggleEvent, mainPanel.visible);
if (secondaryPanel && mainPanel.visible && secondaryPanel.visible) {
mainPanel.setStyle("z-index", 3001);
secondaryPanel.setStyle("z-index", 3000);
}
}
toggleLeftSidePanel() {
this.toggleSidePanel(this.leftSidePanel, "toggledLeftSide");
}
toggleRightSidePanel() {
this.toggleSidePanel(this.rightSidePanel, "toggledRightSide");
}
render() {
return [
this.getLeftSideIcon(),
this.getLeftFixed(),
this.getLeftConditionedWrapper(),
this.getWrappedIcon(),
<NavSection style={{marginLeft: "auto"}}>
{this.getRightConditionedWrapper()}
</NavSection>,
this.getRightFixed(),
this.getRightSideIcon(),
];
}
bindToNode() {
super.bindToNode(...arguments);
this.onMount();
}
// This method enforces the wrapping to be skipped. It is useful when navbar elements change.
skipWrap() {
this.wrapSkip = true;
this.wrapScheduled = false;
}
unskipWrap() {
this.wrapSkip = false;
if (this.wrapScheduled) {
this.checkForWrap();
}
}
checkForWrap() {
if (this.wrapSkip) {
this.wrapScheduled = true;
return;
}
const wrapNavElements = () => {
this.wrapped = true;
this.wrappedPanel = <BasicOrientedElement orientation={Orientation.VERTICAL} styleSheet={this.styleSheet}/>;
this.carousel.appendChild(this.wrappedPanel);
changeParent(this.getRightConditioned(), this.wrappedPanel);
changeParent(this.getLeftConditioned(), this.wrappedPanel);
this.getRightConditioned().redraw();
this.getLeftConditioned().redraw();
this.getWrappedIcon().removeClass("hidden");
};
const unwrapNavElements = () => {
this.wrapped = false;
this.getWrappedIcon().addClass("hidden");
changeParent(this.getLeftConditioned(), this.getLeftConditionedWrapper());
changeParent(this.getRightConditioned(), this.getRightConditionedWrapper());
this.carousel.eraseChild(this.wrappedPanel);
this.getLeftConditioned().redraw();
this.getRightConditioned().redraw();
};
if (this.getLeftConditioned().children.length || this.getRightConditioned().children.length) {
if (!this.wrapped) {
this.unwrappedTotalWidth = 10;
for (let child of this.children) {
this.unwrappedTotalWidth += child.getWidth();
}
}
if (window.innerWidth < this.unwrappedTotalWidth && !this.wrapped) {
wrapNavElements();
this.dispatch("wrapped", true);
} else if (window.innerWidth >= this.unwrappedTotalWidth && this.wrapped) {
unwrapNavElements();
this.dispatch("wrapped", false);
}
} else if (this.wrapped) {
unwrapNavElements();
}
}
onMount() {
NavManager.Global = this;
initializeSwipeEvents(this);
setTimeout(() => this.checkForWrap());
window.addEventListener("resize", () => this.checkForWrap());
this.addListener("maybeWrap", () => this.checkForWrap());
this.addClickListener((event) => {
event.stopPropagation();
});
}
}
let initializeNavbar = () => {
NavManager.Global = NavManager.Global || new NavManager();
return NavManager.Global;
};
export {NavManager, initializeNavbar, NavCarouselStyle, SidePanel}