Browse Source

New menu

qdn-metadata
AlphaX-Projects 3 years ago committed by GitHub
parent
commit
2d28420c8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 153
      qortal-ui-core/src/functional-components/side-menu-item-style.js
  2. 210
      qortal-ui-core/src/functional-components/side-menu-item.js
  3. 78
      qortal-ui-core/src/functional-components/side-menu.js

153
qortal-ui-core/src/functional-components/side-menu-item-style.js

@ -0,0 +1,153 @@
import { css } from 'lit'
export const sideMenuItemStyle = css`
:host {
--font-family: "Roboto", sans-serif;
--item-font-size: 1rem;
--sub-item-font-size: 0.85rem;
--item-padding: 1rem;
--item-content-padding: 1rem;
--icon-height: 1.25rem;
--icon-width: 1.25rem;
--item-border-radius: 5px;
--item-selected-color: #dddddd;
--item-selected-color-text: #333333;
--item-color-active: #d1d1d1;
--item-color-hover: #eeeeee;
--item-text-color: #080808;
--item-icon-color: #080808;
--item-border-color: #eeeeee;
--item-border-selected-color: #333333;
--overlay-box-shadow: 0 2px 4px -1px hsla(214, 53%, 23%, 0.16), 0 3px 12px -1px hsla(214, 50%, 22%, 0.26);
--overlay-background-color: #ffffff;
--spacing: 4px;
font-family: var(--font-family);
display: flex;
overflow: hidden;
flex-direction: column;
border-radius: var(--item-border-radius);
}
#itemLink {
align-items: center;
font-size: var(--item-font-size);
font-weight: 400;
height: var(--icon-height);
transition: background-color 200ms;
padding: var(--item-padding);
cursor: pointer;
display: inline-flex;
flex-grow: 1;
align-items: center;
overflow: hidden;
text-decoration: none;
border-bottom: 1px solid var(--item-border-color);
}
#itemLink:hover {
background-color: var(--item-color-hover);
}
#itemLink:active {
background-color: var(--item-color-active);
}
#content {
padding-left: var(--item-content-padding);
flex: 1;
}
:host([compact]) #content {
padding-left: 0;
display: none;
}
:host([selected]) #itemLink {
background-color: var(--item-selected-color);
color: var(--item-selected-color-text);
border-left: 3px solid var(--item-border-selected-color);
}
:host([selected]) slot[name="icon"]::slotted(*) {
color: var(--item-selected-color-text);
}
:host(:not([selected])) #itemLink{
color: var(--item-text-color);
}
:host([expanded]){
background-color: var(--item-selected-color);
}
:host([hasSelectedChild]){
background-color: var(--item-selected-color);
}
:host span {
cursor: inherit;
overflow: hidden;
text-overflow: ellipsis;
user-select: none;
-webkit-user-select: none;
white-space: nowrap;
}
slot[name="icon"]::slotted(*) {
flex-shrink: 0;
color: var(--item-icon-color);
height: var(--icon-height);
width: var(--icon-width);
pointer-events: none;
}
#collapse-button {
float: right;
}
:host([compact]) #itemLink[level]:not([level="0"]) {
padding: calc( var(--item-padding) / 2);
}
:host(:not([compact])) #itemLink[level]:not([level="0"]) {
padding-left: calc(var(--icon-width) + var(--item-content-padding));
}
#itemLink[level]:not([level="0"]) #content {
display: block;
visibility: visible;
width: auto;
font-weight: 400;
font-size: var(--sub-item-font-size)
}
#overlay {
display: block;
left: 101%;
min-width: 200px;
padding: 4px 2px;
background-color: var(--overlay-background-color);
background-image: var(--overlay-background-image, none);
box-shadow: var(--overlay-box-shadow);
border: 1px solid var(--overlay-background-color);
border-left: 0;
border-radius: 0 3px 3px 0;
position: absolute;
z-index: 1;
animation: pop 200ms forwards;
}
@keyframes pop{
0% {
transform: translateX(-5px);
opacity: 0.5;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
`;

210
qortal-ui-core/src/functional-components/side-menu-item.js

@ -0,0 +1,210 @@
import { LitElement, html, css } from 'lit'
import { ifDefined } from 'lit/directives/if-defined.js'
import { sideMenuItemStyle } from './side-menu-item-style.js'
import '@vaadin/icon'
import '@vaadin/icons'
import '@polymer/paper-tooltip'
export class SideMenuItem extends LitElement {
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 }
}
}
static get styles() {
return css`
${sideMenuItemStyle}
`
}
constructor() {
super()
this.selected = false
this.expanded = false
}
render() {
return html`
${this._itemLinkTemplate()} ${this._tooltipTemplate()}
${this._childrenTemplate()}
`
}
firstUpdated(changedProperties) {
if (!this.hasChildren()) {
return
}
this.collapseExpandIcon = document.createElement("vaadin-icon")
this.collapseExpandIcon.id = "collapse-button"
this.shadowRoot.getElementById("content").appendChild(this.collapseExpandIcon)
this._boundOutsideClickListener = this._outsideClickListener.bind(this)
}
_itemLinkTemplate() {
return html`
<a id="itemLink"
level=${this._getLevel}
href=${this.href || '#!'}
@click="${(e) => this._onClick(e)}"
target=${ifDefined(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}
`
}
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"] = "vaadin:chevron-down-small"
} else {
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
}
}
_onExpandedChanged() {
if (this.collapseExpandIcon == null) {
return;
}
if (this.expanded) {
this._onHandleExpanded();
} else {
this._onHandleCollapsed();
}
}
_onHandleExpanded() {
if (!this.compact) {
this.collapseExpandIcon["icon"] = "vaadin:chevron-up-small"
} else {
this.collapseExpandIcon["icon"] = "vaadin:chevron-left-small"
document.addEventListener("click", this._boundOutsideClickListener, true)
}
}
_onHandleCollapsed() {
if (!this.compact) {
this.collapseExpandIcon["icon"] = "vaadin:chevron-down-small"
} else {
this.collapseExpandIcon["icon"] = "vaadin:chevron-right-small"
document.removeEventListener(
"click",
this._boundOutsideClickListener,
true
)
}
}
_onClick(e) {
if (!this.hasChildren()) {
this.selected = true
} else {
this.expanded = !this.expanded
e.preventDefault()
}
}
_outsideClickListener(event) {
const eventPath = event.composedPath()
if (eventPath.indexOf(this) < 0) {
this.expanded = false
}
}
_changeSelectedState(selected, sourceEvent) {
this.selected = 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")
}
_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
}
}
window.customElements.define("side-menu-item", SideMenuItem);

78
qortal-ui-core/src/functional-components/side-menu.js

@ -0,0 +1,78 @@
import {LitElement, html, css} from 'lit'
class SideMenu extends LitElement {
static get properties() {
return {
items: {type: Array},
selectedValue: {type: String, reflect: true},
compact: {type: Boolean, reflect: true}
}
}
static get styles() {
return css`
nav {
padding: 0;
}
:host {
list-style: none;
width: 100%;
position: relative;
}
:host([compact]) {
width: auto;
}
`
}
constructor() {
super()
this.compact = false
}
render() {
return html`
<nav @side-menu-item-select=${this._handleSelect}>
<slot></slot>
</nav>
`
}
firstUpdated(_changedProperties) {
this.items = [...this.querySelectorAll("side-menu-item")]
}
_handleSelect(event) {
let targetItem = event.target
this._deselectAllItems()
targetItem.selected = true
this.selectedValue = targetItem.label
}
_deselectAllItems() {
this.items.forEach(element => {
if (this.compact) {
element.expanded = false
}
element.selected = false
element.hasChildren() ? element.removeAttribute('hasSelectedChild') : undefined
});
}
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
if (propName === "compact") {
this.items.forEach(item => (item.compact = this.compact))
let evt = new CustomEvent("side-menu-compact-change", {
bubbles: true,
cancelable: true
})
this.dispatchEvent(evt)
}
})
}
}
window.customElements.define("side-menu", SideMenu);
Loading…
Cancel
Save