web/admin: rewrite sidebar to use full components, switch to categories
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		
							
								
								
									
										167
									
								
								web/src/elements/sidebar/SidebarItem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								web/src/elements/sidebar/SidebarItem.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,167 @@ | ||||
| import { css, CSSResult, customElement, LitElement, property } from "lit-element"; | ||||
| import PFPage from "@patternfly/patternfly/components/Page/page.css"; | ||||
| import PFNav from "@patternfly/patternfly/components/Nav/nav.css"; | ||||
| import PFBase from "@patternfly/patternfly/patternfly-base.css"; | ||||
| import AKGlobal from "../../authentik.css"; | ||||
| import { TemplateResult, html } from "lit-html"; | ||||
| import { until } from "lit-html/directives/until"; | ||||
| import { ROUTE_SEPARATOR } from "../../constants"; | ||||
|  | ||||
| @customElement("ak-sidebar-item") | ||||
| export class SidebarItem extends LitElement { | ||||
|  | ||||
|     static get styles(): CSSResult[] { | ||||
|         return [ | ||||
|             PFBase, | ||||
|             PFPage, | ||||
|             PFNav, | ||||
|             AKGlobal, | ||||
|             css` | ||||
|                 :host { | ||||
|                     z-index: 100; | ||||
|                     box-shadow: none !important; | ||||
|                 } | ||||
|                 .pf-c-nav__link.pf-m-current::after, | ||||
|                 .pf-c-nav__link.pf-m-current:hover::after, | ||||
|                 .pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link::after { | ||||
|                     --pf-c-nav__link--m-current--after--BorderColor: #fd4b2d; | ||||
|                 } | ||||
|  | ||||
|                 .pf-c-nav__section + .pf-c-nav__section { | ||||
|                     --pf-c-nav__section--section--MarginTop: var(--pf-global--spacer--sm); | ||||
|                 } | ||||
|                 .pf-c-nav__list .sidebar-brand { | ||||
|                     max-height: 82px; | ||||
|                     margin-bottom: -0.5rem; | ||||
|                 } | ||||
|                 nav { | ||||
|                     display: flex; | ||||
|                     flex-direction: column; | ||||
|                     max-height: 100vh; | ||||
|                     height: 100%; | ||||
|                     overflow-y: hidden; | ||||
|                 } | ||||
|                 .pf-c-nav__list { | ||||
|                     flex-grow: 1; | ||||
|                     overflow-y: auto; | ||||
|                 } | ||||
|  | ||||
|                 .pf-c-nav__link { | ||||
|                     --pf-c-nav__link--PaddingTop: 0.5rem; | ||||
|                     --pf-c-nav__link--PaddingRight: 0.5rem; | ||||
|                     --pf-c-nav__link--PaddingBottom: 0.5rem; | ||||
|                 } | ||||
|                 .pf-c-nav__section-title { | ||||
|                     font-size: 12px; | ||||
|                 } | ||||
|                 .pf-c-nav__item { | ||||
|                     --pf-c-nav__item--MarginTop: 0px; | ||||
|                 } | ||||
|             `, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     @property() | ||||
|     path?: string; | ||||
|  | ||||
|     @property({ attribute: false }) | ||||
|     condition: () => Promise<boolean> = async () => true; | ||||
|  | ||||
|     activeMatchers: RegExp[] = []; | ||||
|  | ||||
|     @property({ type: Boolean }) | ||||
|     expanded = false; | ||||
|  | ||||
|     @property({ type: Boolean }) | ||||
|     isActive = false; | ||||
|  | ||||
|     parent?: SidebarItem; | ||||
|  | ||||
|     get childItems(): SidebarItem[] { | ||||
|         const children = Array.from(this.querySelectorAll<SidebarItem>("ak-sidebar-item") || []); | ||||
|         children.forEach(child => child.parent = this); | ||||
|         return children; | ||||
|     } | ||||
|  | ||||
|     @property({attribute: false}) | ||||
|     set activeWhen(regexp: string[]) { | ||||
|         regexp.forEach(r => { | ||||
|             this.activeMatchers.push(new RegExp(r)); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         this.onHashChange(); | ||||
|         window.addEventListener("hashchange", () => this.onHashChange()); | ||||
|     } | ||||
|  | ||||
|     onHashChange(): void { | ||||
|         const activePath = window.location.hash.slice(1, Infinity).split(ROUTE_SEPARATOR)[0]; | ||||
|         this.childItems.forEach(item => { | ||||
|             this.expandParentRecursive(activePath, item); | ||||
|         }); | ||||
|         this.isActive = this.matchesPath(activePath); | ||||
|     } | ||||
|  | ||||
|     private matchesPath(path: string): boolean { | ||||
|         if (!this.path) { | ||||
|             return false; | ||||
|         } | ||||
|         if (this.path) { | ||||
|             if (new RegExp(`^${this.path}$`).exec(path)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return this.activeMatchers.some(v => { | ||||
|             const match = v.exec(path); | ||||
|             if (match !== null) { | ||||
|                 return true; | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     expandParentRecursive(activePath: string, item: SidebarItem): void { | ||||
|         if (item.matchesPath(activePath) && item.parent) { | ||||
|             item.parent.expanded = true; | ||||
|             this.requestUpdate(); | ||||
|         } | ||||
|         item.childItems.forEach(i => this.expandParentRecursive(activePath, i)); | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         return html`${until(this.renderInner())}`; | ||||
|     } | ||||
|  | ||||
|     async renderInner(): Promise<TemplateResult> { | ||||
|         if (this.condition) { | ||||
|             const result = await this.condition(); | ||||
|             if (!result) { | ||||
|                 return html``; | ||||
|             } | ||||
|         } | ||||
|         if (this.childItems.length > 0) { | ||||
|             return html`<li class="pf-c-nav__item ${this.expanded ? "pf-m-expandable pf-m-expanded" : ""}"> | ||||
|                 <button class="pf-c-nav__link" aria-expanded="true" @click=${() => { | ||||
|                     this.expanded = !this.expanded; | ||||
|                 }}> | ||||
|                     <slot name="label"></slot> | ||||
|                     <span class="pf-c-nav__toggle"> | ||||
|                         <span class="pf-c-nav__toggle-icon"> | ||||
|                             <i class="fas fa-angle-right" aria-hidden="true"></i> | ||||
|                         </span> | ||||
|                     </span> | ||||
|                 </button> | ||||
|                 <section class="pf-c-nav__subnav" ?hidden=${!this.expanded}> | ||||
|                     <ul class="pf-c-nav__list"> | ||||
|                         <slot></slot> | ||||
|                     </ul> | ||||
|                 </section> | ||||
|             </li>`; | ||||
|         } | ||||
|         return html`<li class="pf-c-nav__item"> | ||||
|             <a href="#${this.path}" class="pf-c-nav__link ${this.isActive ? "pf-m-current" : ""}"> | ||||
|                 <slot name="label"></slot> | ||||
|             </a> | ||||
|         </li>`; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer