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

@ -104,8 +104,10 @@ export const demoFunc = () => {
<style> <style>
${css` ${css`
.demo-container { .demo-container {
position: relative; position: absolute;
height: 100vh; top: 0;
left: 0;
height: 100%;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
} }

View File

@ -1,21 +1,184 @@
import { html } from '@design.estate/dees-element'; import { html, DeesElement, customElement, css, cssManager } from '@design.estate/dees-element';
import type { IView } from './dees-simple-appdash.js'; import type { IView } from './dees-simple-appdash.js';
import './dees-form.js';
import './dees-input-text.js';
import './dees-input-checkbox.js';
import './dees-form-submit.js';
// Create demo view components
@customElement('demo-view-dashboard')
class DemoViewDashboard extends DeesElement {
static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
padding: 40px;
}
h1 {
margin: 0 0 20px 0;
color: ${cssManager.bdTheme('#000', '#fff')};
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 20px;
}
.stat-card {
background: ${cssManager.bdTheme('#fff', '#111')};
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#202020')};
border-radius: 8px;
padding: 20px;
text-align: center;
}
.stat-value {
font-size: 32px;
font-weight: bold;
color: ${cssManager.bdTheme('#26a69a', '#26a69a')};
}
.stat-label {
font-size: 14px;
color: ${cssManager.bdTheme('#666', '#999')};
margin-top: 8px;
}
`
];
render() {
return html`
<h1>Dashboard</h1>
<p>Welcome to your application dashboard. Here's an overview of your metrics:</p>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value">1,234</div>
<div class="stat-label">Active Users</div>
</div>
<div class="stat-card">
<div class="stat-value">56.7k</div>
<div class="stat-label">Page Views</div>
</div>
<div class="stat-card">
<div class="stat-value">89%</div>
<div class="stat-label">Uptime</div>
</div>
<div class="stat-card">
<div class="stat-value">3.2s</div>
<div class="stat-label">Avg Response</div>
</div>
</div>
`;
}
}
@customElement('demo-view-analytics')
class DemoViewAnalytics extends DeesElement {
static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
padding: 40px;
}
h1 {
margin: 0 0 20px 0;
color: ${cssManager.bdTheme('#000', '#fff')};
}
`
];
render() {
return html`
<h1>Analytics</h1>
<p>This is the analytics view. You can add charts and metrics here.</p>
`;
}
}
@customElement('demo-view-settings')
class DemoViewSettings extends DeesElement {
static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
padding: 40px;
}
h1 {
margin: 0 0 20px 0;
color: ${cssManager.bdTheme('#000', '#fff')};
}
.settings-section {
margin-top: 30px;
}
.settings-section h2 {
font-size: 18px;
margin: 0 0 15px 0;
color: ${cssManager.bdTheme('#333', '#ccc')};
}
`
];
render() {
return html`
<h1>Settings</h1>
<p>Configure your application settings below:</p>
<div class="settings-section">
<h2>General Settings</h2>
<dees-form>
<dees-input-text key="appName" label="Application Name" value="My App"></dees-input-text>
<dees-input-text key="apiEndpoint" label="API Endpoint" value="https://api.example.com"></dees-input-text>
<dees-input-checkbox key="enableNotifications" label="Enable Notifications" value="true"></dees-input-checkbox>
<dees-form-submit>Save Settings</dees-form-submit>
</dees-form>
</div>
`;
}
}
export const demoFunc = () => html` export const demoFunc = () => html`
<dees-simple-appdash <style>
.viewTabs=${[ body {
{ margin: 0;
name: 'View 1', padding: 0;
element: null, }
}, .demo-container {
{ width: 100%;
name: 'View 2', height: 100%;
element: null, position: absolute;
}, top: 0;
{ left: 0;
name: 'View 3', }
element: null, </style>
} <div class="demo-container">
] as IView[]} <dees-simple-appdash
>Hello there</dees-simple-appdash> name="My Application"
terminalSetupCommand="echo 'Welcome to the terminal!'"
.viewTabs=${[
{
name: 'Dashboard',
iconName: 'home',
element: DemoViewDashboard,
},
{
name: 'Analytics',
iconName: 'lineChart',
element: DemoViewAnalytics,
},
{
name: 'Settings',
iconName: 'settings',
element: DemoViewSettings,
}
] as IView[]}
@logout=${() => {
console.log('Logout event triggered');
alert('Logout clicked!');
}}
@view-select=${(e: CustomEvent) => {
console.log('View selected:', e.detail.view.name);
}}
></dees-simple-appdash>
</div>
`; `;

View File

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

View File

@ -1,3 +1,31 @@
import { html } from '@design.estate/dees-element'; import { html } from '@design.estate/dees-element';
export const demoFunc = () => html` <dees-simple-login name="someapp"> Hello there </dees-simple-login> `; export const demoFunc = () => html`
<style>
body {
margin: 0;
padding: 0;
}
.demo-container {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
<div class="demo-container">
<dees-simple-login
name="My Application"
@login=${(e: CustomEvent) => {
console.log('Login event received:', e.detail);
alert(`Login attempted with:\nUsername: ${e.detail.formData.username}\nPassword: ${e.detail.formData.password}`);
}}
>
<div style="padding: 40px; text-align: center;">
<h1>Welcome!</h1>
<p>This is the slotted content that appears after login.</p>
</div>
</dees-simple-login>
</div>
`;

View File

@ -26,51 +26,77 @@ export class DeesSimpleLogin extends DeesElement {
// INSTANCE // INSTANCE
@property() @property()
public name = 'Dees Simple Login'; public name: string = 'Application';
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
:host { :host {
color: ${cssManager.bdTheme('#333', '#fff')}; color: ${cssManager.bdTheme('#333', '#ccc')};
user-select: none; user-select: none;
display: block;
width: 100%;
height: 100%;
font-family: 'Geist Sans', sans-serif;
} }
.loginContainer { .loginContainer {
position: absolute; position: absolute;
display: flex; display: flex;
justify-content: center; /* aligns horizontally */ justify-content: center;
align-items: center; /* aligns vertically */ align-items: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
top: 0px; top: 0;
left: 0px; left: 0;
background: ${cssManager.bdTheme('#f5f5f5', '#000')};
} }
.slotContainer { .slotContainer {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
top: 0px; top: 0;
left: 0px; left: 0;
opacity: 0;
transition: opacity 0.3s, transform 0.3s;
pointer-events: none;
} }
.login { .login {
min-width: 320px; min-width: 320px;
min-height: 100px; background: ${cssManager.bdTheme('#ffffff', '#111')};
background: ${cssManager.bdTheme('#eeeeeb', '#111')}; box-shadow: ${cssManager.bdTheme(
box-shadow: ${cssManager.bdTheme('0px 1px 4px rgba(0,0,0,0.3)', 'none')}; '0 4px 12px rgba(0, 0, 0, 0.15)',
'0 4px 12px rgba(0, 0, 0, 0.3)'
)};
border-radius: 8px; border-radius: 8px;
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#202020')};
padding: 24px; padding: 24px;
transition: opacity 0.3s, transform 0.3s; transition: opacity 0.3s, transform 0.3s;
} }
.header { .header {
text-align: center; text-align: center;
font-size: 16px;
font-weight: 600;
margin-bottom: 20px;
color: ${cssManager.bdTheme('#000', '#fff')};
} }
.slotContainer {
opacity:0; .login dees-form {
transition: opacity 0.3s, transform 0.3s; display: flex;
pointer-events: none; flex-direction: column;
gap: 12px;
}
.login dees-input-text {
width: 100%;
}
.login dees-form-submit {
margin-top: 4px;
width: 100%;
} }
`, `,
]; ];
@ -79,11 +105,11 @@ export class DeesSimpleLogin extends DeesElement {
return html` return html`
<div class="loginContainer"> <div class="loginContainer">
<div class="login"> <div class="login">
<div class="header">Login to ${this.name}</div>
<dees-form> <dees-form>
<div class="header">Login to ${this.name}</div> <dees-input-text key="username" label="Username" required></dees-input-text>
<dees-input-text key="username" label="username" required></dees-input-text> <dees-input-text key="password" label="Password" isPasswordBool required></dees-input-text>
<dees-input-text key="password" label="password" isPasswordBool required></dees-input-text> <dees-form-submit>Login</dees-form-submit>
<dees-form-submit disabled>login</dees-form-submit>
</dees-form> </dees-form>
</div> </div>
</div> </div>
@ -102,7 +128,7 @@ export class DeesSimpleLogin extends DeesElement {
const password = this.shadowRoot.querySelector('dees-input-text[label="password"]'); const password = this.shadowRoot.querySelector('dees-input-text[label="password"]');
const submit = this.shadowRoot.querySelector('dees-form-submit'); const submit = this.shadowRoot.querySelector('dees-form-submit');
form.addEventListener('formData', (event: CustomEvent) => { form.addEventListener('formData', (event: CustomEvent) => {
this.dispatchEvent(new CustomEvent('login', { detail: event.detail })); this.dispatchEvent(new CustomEvent('login', { detail: event.detail, bubbles: true, composed: true }));
}); });
} }
@ -123,8 +149,5 @@ export class DeesSimpleLogin extends DeesElement {
slotContainerDiv.style.transform = 'translateY(0px)'; slotContainerDiv.style.transform = 'translateY(0px)';
await domtools.convenience.smartdelay.delayFor(300); await domtools.convenience.smartdelay.delayFor(300);
slotContainerDiv.style.pointerEvents = 'all'; slotContainerDiv.style.pointerEvents = 'all';
} }
} }