feat: Enhance demo components with improved layout, styling, and functionality for login and dashboard views

This commit is contained in:
Juergen Kunz
2025-06-17 11:45:25 +00:00
parent 8a1d830376
commit bdb666cbe2
5 changed files with 445 additions and 101 deletions

View File

@ -1,5 +1,4 @@
import { demoFunc } from './dees-simple-appdash.demo.js';
import * as colors from './00colors.js';
import {
customElement,
@ -14,7 +13,8 @@ import {
state,
domtools,
} from '@design.estate/dees-element';
import { DeesTerminal } from './dees-terminal.js';
import './dees-icon.js';
import type { DeesTerminal } from './dees-terminal.js';
declare global {
interface HTMLElementTagNameMap {
@ -24,6 +24,7 @@ declare global {
export interface IView {
name: string;
iconName?: string;
element: DeesElement['constructor']['prototype'];
}
@ -34,13 +35,17 @@ export class DeesSimpleAppDash extends DeesElement {
// INSTANCE
@property()
public name = 'Dees Simple Login';
public name: string = 'Application Dashboard';
@property()
@property({ type: Array })
public viewTabs: IView[] = [];
@property({ type: String })
public terminalSetupCommand: string = `echo "Terminal ready"`;
@state()
private selectedView: IView;
@property()
public terminalSetupCommand: string = `pnpm install @serve.zone/cli && clear && servezone info`;
public static styles = [
cssManager.defaultStyles,
@ -69,54 +74,110 @@ export class DeesSimpleAppDash extends DeesElement {
top: 0px;
left: 0px;
height: calc(100% - 24px);
width: 200px;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
border-right: 1px solid ${cssManager.bdTheme('#ccc', '#ffffff20')};
font-size: 14px;
line-height: 32px;
width: 240px;
background: ${cssManager.bdTheme('#fafafa', '#000')};
border-right: 1px solid ${cssManager.bdTheme('#e0e0e0', '#202020')};
font-size: 12px;
font-family: 'Geist Sans', sans-serif;
padding: 0px 16px;
z-index: 2;
box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.8);
display: grid;
grid-template-rows: min-content auto min-content;
grid-template-rows: auto min-content;
overflow: hidden;
}
.appbar .viewTabs {
margin-left: -8px;
margin-right: -8px;
.sidebar-header {
padding: 16px 12px;
border-bottom: 1px solid ${cssManager.bdTheme('#e0e0e0', '#202020')};
display: flex;
align-items: center;
gap: 8px;
}
.appName {
font-size: 14px;
font-weight: 600;
color: ${cssManager.bdTheme('#000', '#fff')};
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.viewTabs-container {
flex: 1;
overflow-y: auto;
padding: 4px 0;
}
.viewTabs {
display: flex;
flex-direction: column;
align-items: top;
}
.viewTab {
padding: 0px 8px;
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
cursor: default;
transition: background 0.1s;
color: ${cssManager.bdTheme('#333', '#ccc')};
user-select: none;
position: relative;
}
.viewTab:hover {
background: ${cssManager.bdTheme('#ccc', '#ffffff10')};
color: ${cssManager.bdTheme('#000', '#fff')};
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.04)', 'rgba(255, 255, 255, 0.08)')};
}
.viewTab:active {
background: ${cssManager.bdTheme('#aaa', '#ffffff20')};
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.08)', 'rgba(255, 255, 255, 0.12)')};
}
.viewTab.selected {
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.08)', 'rgba(255, 255, 255, 0.12)')};
color: ${cssManager.bdTheme('#000', '#fff')};
font-weight: 500;
}
.viewTab.selected::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: ${cssManager.bdTheme('#26a69a', '#26a69a')};
}
.viewTab dees-icon {
font-size: 14px;
opacity: 0.7;
}
.appName {
white-space: nowrap;
color: ${cssManager.bdTheme('#666', '#999')};
}
.appActions {
display: flex;
padding: 12px;
border-top: 1px solid ${cssManager.bdTheme('#e0e0e0', '#202020')};
}
.appActions .action {
.action {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
border-radius: 4px;
cursor: default;
transition: background 0.1s;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
.appActions .action:hover {
color: ${cssManager.bdTheme('#000', '#fff')};
.action:hover {
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.04)', 'rgba(255, 255, 255, 0.08)')};
}
.action dees-icon {
font-size: 14px;
opacity: 0.7;
}
.appcontent {
@ -124,37 +185,50 @@ export class DeesSimpleAppDash extends DeesElement {
position: absolute;
top: 0px;
right: 0px;
height: calc(100vh - 24px);
height: calc(100% - 24px);
bottom: 24px;
width: calc(100vw - 200px);
width: calc(100% - 240px);
overflow: auto;
background: ${cssManager.bdTheme('#eeeeeb', '#000')};
background: ${cssManager.bdTheme('#f5f5f5', '#000')};
overscroll-behavior: contain;
}
.controlbar {
color: #fff;
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
border-top: 1px solid #44444480;
border-top: 1px solid ${cssManager.bdTheme('#00000020', '#ffffff20')};
height: 24px;
background: ${cssManager.bdTheme(colors.bright.blueMuted, colors.dark.blueMuted)};
background: ${cssManager.bdTheme('#2196f3', '#1565c0')};
z-index: 2;
display: flex;
justify-content: flex-end;
align-items: center;
flex-direction: row;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.8);
font-size: 12px;
}
.control {
width: min-content;
display: flex;
align-items: center;
gap: 4px;
margin-right: 16px;
font-size: 12px;
white-space: nowrap;
cursor: default;
opacity: 0.8;
transition: opacity 0.2s;
}
.control:hover {
opacity: 1;
}
.control dees-icon {
font-size: 14px;
}
`,
];
@ -162,31 +236,49 @@ export class DeesSimpleAppDash extends DeesElement {
return html`
<div class="maincontainer">
<div class="appbar">
<div class="appName">${this.name}</div>
<div class="viewTabs">
${this.viewTabs.map(
(view) => html`
<div class="viewTab" @click=${() => {
this.loadView(view);
}}>${view.name}</div>
`
)}
<div>
<div class="sidebar-header">
<dees-icon .icon="lucide:grid3x3" style="font-size: 18px;"></dees-icon>
<div class="appName">${this.name}</div>
</div>
<div class="viewTabs-container">
<div class="viewTabs">
${this.viewTabs.map(
(view) => html`
<div
class="viewTab ${this.selectedView === view ? 'selected' : ''}"
@click=${() => this.loadView(view)}
>
${view.iconName ? html`
<dees-icon .icon="${`lucide:${view.iconName}`}"></dees-icon>
` : ''}
<span style="flex: 1;">${view.name}</span>
</div>
`
)}
</div>
</div>
</div>
<div class="appActions">
<div class="action" @click=${() => {
this.dispatchEvent(new CustomEvent('logout'));
}}>Logout</div>
this.dispatchEvent(new CustomEvent('logout', { bubbles: true, composed: true }));
}}>
<dees-icon .icon="lucide:logOut"></dees-icon>
<span>Logout</span>
</div>
</div>
</div>
<div class="appcontent">
<!-- Content goes here -->
</div>
<div class="controlbar">
<div class="control">
<dees-icon .iconFA=${'networkWired'}></dees-icon>
<dees-icon .icon="lucide:wifi"></dees-icon>
<span>Connected</span>
</div>
<div class="control" @click=${this.launchTerminal}>
<dees-icon .iconFA=${'terminal'}></dees-icon>
<dees-icon .icon="lucide:terminal"></dees-icon>
<span>Terminal</span>
</div>
</div>
</div>
@ -196,30 +288,58 @@ export class DeesSimpleAppDash extends DeesElement {
public async firstUpdated(_changedProperties): Promise<void> {
const domtools = await this.domtoolsPromise;
super.firstUpdated(_changedProperties);
await this.loadView(this.viewTabs[0]);
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.js');
const terminal = new DeesTerminal();
terminal.setupCommand = this.terminalSetupCommand;
this.currentTerminal = terminal;
maincontainer.appendChild(terminal);
terminal.style.position = 'absolute';
terminal.style.zIndex = '1';
terminal.style.zIndex = '10';
terminal.style.top = '0px';
terminal.style.left = '200px';
terminal.style.left = '240px';
terminal.style.right = '0px';
terminal.style.bottom = '24px';
terminal.style.opacity = '0';
terminal.style.transform = 'translateY(20px)';
terminal.style.transition = 'all 0.2s';
await domtools.plugins.smartdelay.delayFor(0);
terminal.style.background = '#000';
terminal.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.3)';
// Add close button to terminal
terminal.addEventListener('close', () => this.closeTerminal());
await domtools.convenience.smartdelay.delayFor(0);
terminal.style.opacity = '1';
terminal.style.transform = 'translateY(0px)';
return terminal;
}
private async closeTerminal() {
const domtools = await this.domtoolsPromise;
if (this.currentTerminal) {
this.currentTerminal.style.opacity = '0';
this.currentTerminal.style.transform = 'translateY(20px)';
await domtools.convenience.smartdelay.delayFor(200);
this.currentTerminal.remove();
this.currentTerminal = null;
}
}
private currentView: DeesElement;
public async loadView(viewArg: IView) {
@ -230,5 +350,13 @@ export class DeesSimpleAppDash extends DeesElement {
}
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
}));
}
}