import { AnyVirtualDOM, ChildrenLike, VirtualDOM } from '@youwol/rx-vdom' import { BehaviorSubject } from 'rxjs' import { layoutStyleBase } from './common' /** * Type definition for an element in the side navigation. */ export type SideNavElement = { /** * Icon in the side-bar. */ icon: string /** * Content when the side-bar is expanded. */ content: AnyVirtualDOM } /** * Type definition for the arguments to create a {@link SideNavLayout}. */ export type SideNavArguments = { /** * An object mapping keys to side navigation elements. */ sideNavElements: { [k: string]: SideNavElement } /** * The main content of the layout. */ content: AnyVirtualDOM } /** * Function to create a {@link SideNavLayout}. * * @param params Arguments */ export function sideNav(params: SideNavArguments) { return new SideNavLayout(params) } /** * Class representing a side navigation layout. * * It features: * - A main content area. * - A side navigation bar on the right: * - When collapsed, it includes the icons of the multiple elements declared in the side-bar in a vertical layout. * - When an element is expanded, it displays the associated content. * * * const sideNavClass = 'h-100 bg-light p-2 px-5' * const sideNavHome = { * icon: 'fas fa-home', * content: { tag: 'div', innerText: 'Home', class: sideNavClass }, * } * const sideNavAbout = { * icon: 'fas fa-info', * content: { tag: 'div', innerText: 'About', class: sideNavClass }, * } * const sideNavContact = { * icon: 'fas fa-envelope', * content: { tag: 'div', innerText: 'Contact', class: sideNavClass }, * } * const sideNav = { * home: sideNavHome, * about: sideNavAbout, * contact: sideNavContact, * } * const sideNavLayout = Views.Layouts.sideNav({ * sideNavElements: sideNav, * content: { tag: 'div', innerText: 'Main content', class:'p-2' }, * }) * display(sideNavLayout) * * */ export class SideNavLayout implements VirtualDOM<'div'> { public readonly tag = 'div' public readonly class = 'd-flex' public readonly style = layoutStyleBase public readonly children: ChildrenLike public readonly sideNavElements: SideNavElement[] public readonly content: AnyVirtualDOM public readonly selected$ = new BehaviorSubject( undefined, ) /** * Creates an instance of SideNavLayout. * * @param params Arguments */ constructor(params: SideNavArguments) { Object.assign(this, params) this.children = [ this.menuBar(), { tag: 'div', class: 'h-100 flex-grow-1 d-flex', style: { minWidth: '0px', position: 'relative', }, children: [ { tag: 'div', class: 'mkdocs-bg-0 h-100', style: { position: 'absolute', top: '0px', left: '0px', zIndex: 1, opacity: 0.9, }, children: Object.entries(this.sideNavElements).map( ([k, elem]) => { return { tag: 'div', class: { source$: this.selected$, vdomMap: (selected) => selected === k ? 'd-block h-100 overflow-auto' : 'd-none', }, children: [elem.content], } }, ), }, { tag: 'div', class: 'h-100 flex-grow-1', style: { minWidth: '0px', }, children: [this.content], }, ], }, ] } private menuBar(): VirtualDOM<'div'> { return { tag: 'div', class: 'h-100 overflow-y-auto px-1 border-right d-flex flex-column', children: Object.entries(this.sideNavElements).map( ([key, elem]) => { return { tag: 'button', class: { source$: this.selected$, vdomMap: (selected: string) => selected === key ? `btn btn-primary ${elem.icon} p-1 border rounded` : `btn btn-light ${elem.icon} p-1 border rounded`, }, onclick: () => { this.selected$.value === key ? this.selected$.next('') : this.selected$.next(key) }, } }, ), } } }