@maksu/side-menu
Version:
Side menu for navigating in web applications
228 lines (193 loc) • 5.83 kB
JavaScript
import { LitElement, html, css } from "lit";
import { sideMenuItemStyle } from "./side-menu-item-style.js";
import "@polymer/iron-icon/iron-icon.js";
import "@polymer/iron-icons/iron-icons.js";
import "@polymer/paper-tooltip";
export class SideMenuItem extends LitElement {
render() {
return html`
${this._itemLinkTemplate()} ${this._tooltipTemplate()}
${this._childrenTemplate()}
`;
}
_itemLinkTemplate() {
return html`
<a id="itemLink"
level=${this._getLevel}
href=${this.href || '#!'}
="${(e) => this._onClick(e)}"
?target=${this.target}>
<slot class="icon" name="icon"></slot>
<div id ="content"
<span>${this.label}</span>
</div>
</a>
`;
}
_tooltipTemplate() {
return html`
${this._getLevel === 0 && this.compact
? html`
<paper-tooltip for="itemLink" position="right" animation-delay="0">
${this.label}
</paper-tooltip>
`
: undefined}
`;
}
_childrenTemplate() {
return html`
${this.expanded
? html`
${this.compact
? html`
<div id="overlay"><slot></slot></div>
`
: html`
<slot></slot>
`}
`
: undefined}
`;
}
static get properties() {
return {
selected: { type: Boolean, reflect: true },
label: { type: String, reflect: true },
expanded: { type: Boolean, reflect: true },
compact: { type: Boolean, reflect: true },
href: { type: String, reflect: true },
target: { type: String, reflect: true }
};
}
constructor() {
super();
this.selected = false;
this.expanded = false;
}
firstUpdated(changedProperties) {
if (!this.hasChildren()) {
return;
}
this.collapseExpandIcon = document.createElement("iron-icon");
this.collapseExpandIcon.id = "collapse-button";
this.shadowRoot
.getElementById("content")
.appendChild(this.collapseExpandIcon);
this._boundOutsideClickListener = this._outsideClickListener.bind(this);
}
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
if (propName === "compact") {
this._onCompactChanged();
}
if (propName === "expanded") {
this._onExpandedChanged();
}
if (propName === "selected"){
if (oldValue === this.selected){
return;
}
if (this.selected) {
this._changeSelectedState(true);
this._markParentWithSelectedChild();
}
}
});
}
_onCompactChanged() {
this.expanded = false;
if (this.collapseExpandIcon == null) {
return;
}
if (!this.compact) {
this.collapseExpandIcon["icon"] = "expand-more";
} else {
this.collapseExpandIcon["icon"] = "chevron-right";
}
}
_onExpandedChanged() {
if (this.collapseExpandIcon == null) {
return;
}
if (this.expanded) {
this._onHandleExpanded();
} else {
this._onHandleCollapsed();
}
}
_onHandleExpanded() {
if (!this.compact) {
this.collapseExpandIcon["icon"] = "expand-less";
} else {
this.collapseExpandIcon["icon"] = "chevron-left";
document.addEventListener("click", this._boundOutsideClickListener, true);
}
}
_onHandleCollapsed() {
if (!this.compact) {
this.collapseExpandIcon["icon"] = "expand-more";
} else {
this.collapseExpandIcon["icon"] = "chevron-right";
document.removeEventListener(
"click",
this._boundOutsideClickListener,
true
);
}
}
_onClick(e) {
if (!this.hasChildren()) {
this.selected = true;
} else {
this.expanded = !this.expanded;
//Don't go anywhere
e.preventDefault();
}
}
_outsideClickListener(event) {
const eventPath = event.composedPath();
if (eventPath.indexOf(this) < 0) {
this.expanded = false;
}
}
_changeSelectedState(selected, sourceEvent) {
this.selected = selected;
//Fire event to the side-menu, that an item is selected
let evt = new CustomEvent("side-menu-item-select", {
bubbles: true,
cancelable: true,
detail: { sourceEvent: sourceEvent }
});
this.dispatchEvent(evt);
}
hasChildren() {
return !!this.querySelector("side-menu-item");
}
/**
* Finds all parent items and adds an attribute that it has a child which is selected
*
*/
_markParentWithSelectedChild() {
let element = this.parentElement;
while (element instanceof SideMenuItem) {
element.setAttribute('hasSelectedChild', true);
element = element.parentElement;
}
}
get _getLevel() {
let level = 0;
let element = this.parentElement;
while (element instanceof SideMenuItem) {
level++;
element = element.parentElement;
}
return level;
}
static get styles() {
return css`
${sideMenuItemStyle}
`;
}
}
customElements.define("side-menu-item", SideMenuItem);