import { demoFunc } from './dees-simple-appdash.demo.js'; import { customElement, html, DeesElement, property, type TemplateResult, cssManager, css, unsafeCSS, type CSSResult, state, domtools, } from '@design.estate/dees-element'; import '../../dees-icon/dees-icon.js'; import type { DeesTerminal } from '../../dees-terminal/dees-terminal.js'; declare global { interface HTMLElementTagNameMap { 'dees-simple-appdash': DeesSimpleAppDash; } } export interface IView { name: string; iconName?: string; element: DeesElement['constructor']['prototype']; } @customElement('dees-simple-appdash') export class DeesSimpleAppDash extends DeesElement { // STATIC public static demo = demoFunc; // INSTANCE @property() accessor name: string = 'Application Dashboard'; @property({ type: Array }) accessor viewTabs: IView[] = []; @property({ type: String }) accessor terminalSetupCommand: string = `echo "Terminal ready"`; @state() accessor selectedView: IView; public static styles = [ cssManager.defaultStyles, css` :host { color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')}; user-select: none; display: block; overflow: hidden; position: relative; height: 100%; width: 100%; } .maincontainer { position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; overflow: hidden; } .appbar { position: absolute; top: 0px; left: 0px; height: calc(100% - 24px); width: 240px; background: ${cssManager.bdTheme('hsl(0 0% 99%)', 'hsl(0 0% 7%)')}; border-right: 1px solid ${cssManager.bdTheme('hsl(0 0% 91%)', 'hsl(0 0% 13%)')}; font-size: 13px; font-family: 'Geist Sans', sans-serif; z-index: 2; display: grid; grid-template-rows: auto 1fr min-content; overflow: hidden; } .sidebar-header { padding: 20px 16px; border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 91%)', 'hsl(0 0% 13%)')}; display: flex; align-items: center; gap: 12px; } .header-icon-wrapper { display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 10px; background: ${cssManager.bdTheme( 'linear-gradient(135deg, hsl(215 20% 95%) 0%, hsl(215 20% 90%) 100%)', 'linear-gradient(135deg, hsl(215 20% 18%) 0%, hsl(215 20% 14%) 100%)' )}; box-shadow: ${cssManager.bdTheme( '0 1px 2px rgb(0 0 0 / 0.05), inset 0 1px 0 rgb(255 255 255 / 0.5)', '0 1px 2px rgb(0 0 0 / 0.2), inset 0 1px 0 rgb(255 255 255 / 0.05)' )}; } .header-icon-wrapper dees-icon { font-size: 18px; color: ${cssManager.bdTheme('hsl(215 20% 40%)', 'hsl(215 20% 70%)')}; } .appName { font-size: 15px; font-weight: 600; color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 98%)')}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: -0.02em; } .viewTabs-container { overflow-y: auto; padding: 12px 8px; scrollbar-width: thin; scrollbar-color: ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')} transparent; } .viewTabs-container::-webkit-scrollbar { width: 6px; } .viewTabs-container::-webkit-scrollbar-track { background: transparent; } .viewTabs-container::-webkit-scrollbar-thumb { background: ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')}; border-radius: 3px; } .viewTabs-container::-webkit-scrollbar-thumb:hover { background: ${cssManager.bdTheme('hsl(0 0% 75%)', 'hsl(0 0% 30%)')}; } .section-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 50%)')}; padding: 8px 12px 8px; margin-bottom: 4px; } .viewTabs { display: flex; flex-direction: column; gap: 2px; } .viewTab { display: flex; align-items: center; gap: 10px; padding: 10px 12px; cursor: default; transition: all 0.15s ease; color: ${cssManager.bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 65%)')}; user-select: none; position: relative; border-radius: 8px; } .viewTab:hover { background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.04)', 'hsl(0 0% 100% / 0.05)')}; color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')}; } .viewTab:active { background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.06)', 'hsl(0 0% 100% / 0.07)')}; transform: scale(0.99); } .viewTab.selected { background: ${cssManager.bdTheme('hsl(215 25% 95%)', 'hsl(215 20% 15%)')}; color: ${cssManager.bdTheme('hsl(215 25% 30%)', 'hsl(215 25% 85%)')}; font-weight: 500; } .viewTab.selected::before { content: ''; position: absolute; left: 0; top: 8px; bottom: 8px; width: 3px; border-radius: 0 2px 2px 0; background: ${cssManager.bdTheme('hsl(215 70% 50%)', 'hsl(215 70% 60%)')}; } .viewTab dees-icon { font-size: 16px; opacity: 0.55; transition: all 0.15s ease; } .viewTab:hover dees-icon { opacity: 0.75; } .viewTab.selected dees-icon { opacity: 0.9; color: ${cssManager.bdTheme('hsl(215 70% 45%)', 'hsl(215 70% 65%)')}; } .viewTab span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .appActions { padding: 12px 8px; border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 91%)', 'hsl(0 0% 13%)')}; } .action { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 8px; cursor: default; transition: all 0.15s ease; color: ${cssManager.bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 65%)')}; } .action:hover { background: ${cssManager.bdTheme('hsl(0 84% 60% / 0.08)', 'hsl(0 84% 60% / 0.12)')}; color: ${cssManager.bdTheme('hsl(0 84% 45%)', 'hsl(0 84% 65%)')}; } .action dees-icon { font-size: 16px; opacity: 0.6; transition: all 0.15s ease; } .action:hover dees-icon { opacity: 0.9; color: ${cssManager.bdTheme('hsl(0 84% 45%)', 'hsl(0 84% 65%)')}; } .appcontent { z-index: 1; position: absolute; top: 0px; right: 0px; height: calc(100% - 24px); bottom: 24px; width: calc(100% - 240px); overflow: auto; background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(0 0% 5%)')}; overscroll-behavior: contain; } .controlbar { color: #fff; position: absolute; bottom: 0px; left: 0px; width: 100%; height: 24px; background: ${cssManager.bdTheme('hsl(220 13% 18%)', 'hsl(220 13% 12%)')}; z-index: 2; display: flex; justify-content: flex-end; align-items: center; flex-direction: row; font-size: 11px; font-weight: 500; letter-spacing: 0.01em; } .control { display: flex; align-items: center; gap: 6px; padding: 0 12px; height: 100%; white-space: nowrap; cursor: default; color: hsl(0 0% 70%); transition: all 0.15s ease; border-left: 1px solid hsl(0 0% 100% / 0.08); } .control:first-child { border-left: none; } .control:hover { background: hsl(0 0% 100% / 0.06); color: hsl(0 0% 95%); } .control dees-icon { font-size: 13px; } .control.status-connected dees-icon { color: hsl(142 70% 50%); } .control.status-terminal dees-icon { color: hsl(45 90% 55%); } `, ]; public render(): TemplateResult { return html`
${this.viewTabs.map( (view) => html`
this.loadView(view)} > ${view.iconName ? html` ` : html` `} ${view.name}
` )}
{ this.dispatchEvent(new CustomEvent('logout', { bubbles: true, composed: true })); }}> Sign out
Connected
Terminal
`; } public async firstUpdated(_changedProperties): Promise { const domtools = await this.domtoolsPromise; super.firstUpdated(_changedProperties); if (this.viewTabs && this.viewTabs.length > 0) { await this.loadView(this.viewTabs[0]); } } public currentTerminal: DeesTerminal; public async launchTerminal() { const domtools = await this.domtoolsPromise; if (this.currentTerminal) { // If terminal already exists, remove it await this.closeTerminal(); return; } const maincontainer = this.shadowRoot.querySelector('.maincontainer'); const { DeesTerminal } = await import('../../dees-terminal/dees-terminal.js'); const terminal = new DeesTerminal(); terminal.setupCommand = this.terminalSetupCommand; this.currentTerminal = terminal; maincontainer.appendChild(terminal); terminal.style.position = 'absolute'; terminal.style.zIndex = '10'; terminal.style.top = '0px'; terminal.style.left = '240px'; terminal.style.right = '0px'; terminal.style.bottom = '24px'; terminal.style.opacity = '0'; terminal.style.transform = 'translateY(8px) scale(0.99)'; terminal.style.transition = 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)'; terminal.background = 'hsl(220 13% 8%)'; terminal.style.boxShadow = '0 25px 50px -12px rgb(0 0 0 / 0.5), 0 0 0 1px rgb(255 255 255 / 0.05)'; terminal.style.maxWidth = `calc(${maincontainer.clientWidth}px -240px)`; terminal.style.maxHeight = `calc(${maincontainer.clientHeight}px - 24px)`; // Add close button to terminal terminal.addEventListener('close', () => this.closeTerminal()); await domtools.convenience.smartdelay.delayFor(0); terminal.style.opacity = '1'; terminal.style.transform = 'translateY(0) scale(1)'; return terminal; } private async closeTerminal() { const domtools = await this.domtoolsPromise; if (this.currentTerminal) { this.currentTerminal.style.opacity = '0'; this.currentTerminal.style.transform = 'translateY(8px) scale(0.99)'; await domtools.convenience.smartdelay.delayFor(250); this.currentTerminal.remove(); this.currentTerminal = null; } } private currentView: DeesElement; public async loadView(viewArg: IView) { const appcontent = this.shadowRoot.querySelector('.appcontent'); const view = new viewArg.element(); if (this.currentView) { this.currentView.remove(); } appcontent.appendChild(view); this.currentView = view; this.selectedView = viewArg; // Emit view-select event this.dispatchEvent(new CustomEvent('view-select', { detail: { view: viewArg }, bubbles: true, composed: true })); } }