feat(elements): standardize dashboard and detail views on dees tile and stats grid components
This commit is contained in:
@@ -24,6 +24,13 @@ export interface IConfigSectionAction {
|
||||
detail?: any;
|
||||
}
|
||||
|
||||
export interface IConfigSectionLink {
|
||||
label: string;
|
||||
href: string;
|
||||
icon?: string;
|
||||
external?: boolean;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'sz-config-section': SzConfigSection;
|
||||
@@ -46,6 +53,12 @@ export class SzConfigSection extends DeesElement {
|
||||
{ key: 'Auto Renew', value: true, type: 'boolean' },
|
||||
{ key: 'Renew Threshold', value: '30 days' },
|
||||
] as IConfigField[]}
|
||||
.links=${[
|
||||
{ label: 'Docs', href: 'https://code.foss.global/serve.zone/smartproxy', icon: 'lucide:bookOpen', external: true },
|
||||
] as IConfigSectionLink[]}
|
||||
.actions=${[
|
||||
{ label: 'Configure', icon: 'lucide:settings', event: 'configure' },
|
||||
] as IConfigSectionAction[]}
|
||||
></sz-config-section>
|
||||
<sz-config-section
|
||||
title="Email Server"
|
||||
@@ -57,6 +70,13 @@ export class SzConfigSection extends DeesElement {
|
||||
{ key: 'Hostname', value: null },
|
||||
{ key: 'Domains', value: ['example.com', 'mail.example.com'], type: 'pills' },
|
||||
] as IConfigField[]}
|
||||
.links=${[
|
||||
{ label: 'Docs', href: 'https://code.foss.global/serve.zone/smartmta', icon: 'lucide:bookOpen', external: true },
|
||||
{ label: 'Source', href: 'https://code.foss.global/serve.zone/smartmta', icon: 'lucide:github', external: true },
|
||||
] as IConfigSectionLink[]}
|
||||
.actions=${[
|
||||
{ label: 'Enable', icon: 'lucide:power', event: 'enable' },
|
||||
] as IConfigSectionAction[]}
|
||||
></sz-config-section>
|
||||
<sz-config-section
|
||||
title="DNS Server"
|
||||
@@ -68,6 +88,9 @@ export class SzConfigSection extends DeesElement {
|
||||
{ key: 'Port', value: 53 },
|
||||
{ key: 'NS Domains', value: ['ns1.example.com', 'ns2.example.com'], type: 'pills' },
|
||||
] as IConfigField[]}
|
||||
.links=${[
|
||||
{ label: 'Getting Started', href: 'https://docs.example.com/dns', icon: 'lucide:bookOpen', external: true },
|
||||
] as IConfigSectionLink[]}
|
||||
></sz-config-section>
|
||||
`;
|
||||
|
||||
@@ -91,6 +114,9 @@ export class SzConfigSection extends DeesElement {
|
||||
@property({ type: Array })
|
||||
public accessor actions: IConfigSectionAction[] = [];
|
||||
|
||||
@property({ type: Array })
|
||||
public accessor links: IConfigSectionLink[] = [];
|
||||
|
||||
@property({ type: Boolean })
|
||||
public accessor collapsible: boolean = false;
|
||||
|
||||
@@ -108,20 +134,25 @@ export class SzConfigSection extends DeesElement {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.section {
|
||||
background: ${cssManager.bdTheme('#ffffff', '#09090b')};
|
||||
border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
dees-tile {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host([collapsed]) dees-tile::part(content) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host([collapsed]) dees-tile::part(footer) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 20px;
|
||||
background: ${cssManager.bdTheme('#f4f4f5', '#18181b')};
|
||||
border-bottom: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')};
|
||||
padding: 10px 16px;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -131,30 +162,31 @@ export class SzConfigSection extends DeesElement {
|
||||
}
|
||||
|
||||
:host([collapsible]) .section-header:hover {
|
||||
background: ${cssManager.bdTheme('#ebebed', '#1c1c1f')};
|
||||
background: var(--dees-color-hover);
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: ${cssManager.bdTheme('#e4e4e7', '#27272a')};
|
||||
border-radius: 8px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: var(--dees-color-border-default);
|
||||
border-radius: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-icon dees-icon {
|
||||
font-size: 18px;
|
||||
color: ${cssManager.bdTheme('#52525b', '#a1a1aa')};
|
||||
font-size: 14px;
|
||||
color: var(--dees-color-text-muted);
|
||||
}
|
||||
|
||||
.header-text {
|
||||
@@ -162,15 +194,17 @@ export class SzConfigSection extends DeesElement {
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: ${cssManager.bdTheme('#18181b', '#fafafa')};
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--dees-color-text-secondary);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
font-size: 12px;
|
||||
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
|
||||
font-size: 11px;
|
||||
color: var(--dees-color-text-muted);
|
||||
letter-spacing: -0.01em;
|
||||
line-height: 1.3;
|
||||
margin-top: 1px;
|
||||
}
|
||||
@@ -236,30 +270,60 @@ export class SzConfigSection extends DeesElement {
|
||||
background: ${cssManager.bdTheme('#f59e0b', '#fbbf24')};
|
||||
}
|
||||
|
||||
/* Action buttons */
|
||||
.header-action {
|
||||
display: inline-flex;
|
||||
/* Footer action buttons — canonical dees-modal / dees-tile pattern */
|
||||
.section-footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
gap: 0;
|
||||
height: 36px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tile-button {
|
||||
padding: 0 16px;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: ${cssManager.bdTheme('#2563eb', '#60a5fa')};
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all 0.15s ease;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background 150ms ease;
|
||||
border-left: 1px solid var(--dees-color-border-subtle);
|
||||
color: var(--dees-color-text-muted);
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-family: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.header-action:hover {
|
||||
background: ${cssManager.bdTheme('rgba(37,99,235,0.08)', 'rgba(96,165,250,0.1)')};
|
||||
.tile-button:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.header-action dees-icon {
|
||||
font-size: 14px;
|
||||
.tile-button:hover {
|
||||
background: var(--dees-color-hover);
|
||||
color: var(--dees-color-text-primary);
|
||||
}
|
||||
|
||||
.tile-button.primary {
|
||||
color: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')};
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tile-button.primary:hover {
|
||||
background: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8% / 0.08)', 'hsl(213.1 93.9% 67.8% / 0.08)')};
|
||||
color: ${cssManager.bdTheme('hsl(217.2 91.2% 50%)', 'hsl(213.1 93.9% 75%)')};
|
||||
}
|
||||
|
||||
.tile-button dees-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Chevron */
|
||||
@@ -274,8 +338,8 @@ export class SzConfigSection extends DeesElement {
|
||||
}
|
||||
|
||||
.chevron dees-icon {
|
||||
font-size: 16px;
|
||||
color: ${cssManager.bdTheme('#a1a1aa', '#52525b')};
|
||||
font-size: 14px;
|
||||
color: var(--dees-color-text-muted);
|
||||
}
|
||||
|
||||
/* Content */
|
||||
@@ -283,10 +347,6 @@ export class SzConfigSection extends DeesElement {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.section-content.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Field rows */
|
||||
.field-row {
|
||||
display: flex;
|
||||
@@ -439,6 +499,13 @@ export class SzConfigSection extends DeesElement {
|
||||
}
|
||||
}
|
||||
|
||||
updated(changedProperties: Map<string, any>) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has('isCollapsed')) {
|
||||
this.toggleAttribute('collapsed', this.isCollapsed);
|
||||
}
|
||||
}
|
||||
|
||||
public render(): TemplateResult {
|
||||
const statusLabels: Record<string, string> = {
|
||||
'enabled': 'Enabled',
|
||||
@@ -448,8 +515,9 @@ export class SzConfigSection extends DeesElement {
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="section">
|
||||
<dees-tile>
|
||||
<div
|
||||
slot="header"
|
||||
class="section-header"
|
||||
@click=${() => {
|
||||
if (this.collapsible) {
|
||||
@@ -475,8 +543,36 @@ export class SzConfigSection extends DeesElement {
|
||||
${statusLabels[this.status] || this.status}
|
||||
</span>
|
||||
` : ''}
|
||||
${this.collapsible ? html`
|
||||
<span class="chevron ${this.isCollapsed ? 'collapsed' : ''}">
|
||||
<dees-icon .icon=${'lucide:chevronDown'}></dees-icon>
|
||||
</span>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-content">
|
||||
${this.fields.map(field => this.renderField(field))}
|
||||
<div class="slot-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
${this.links.length > 0 || this.actions.length > 0 ? html`
|
||||
<div slot="footer" class="section-footer">
|
||||
${this.links.map(link => html`
|
||||
<a
|
||||
class="tile-button"
|
||||
href=${link.href}
|
||||
target=${link.external ? '_blank' : '_self'}
|
||||
rel=${link.external ? 'noopener noreferrer' : ''}
|
||||
@click=${(e: Event) => e.stopPropagation()}
|
||||
>
|
||||
${link.icon ? html`<dees-icon .icon=${link.icon}></dees-icon>` : ''}
|
||||
${link.label}
|
||||
${link.external ? html`<dees-icon .icon=${'lucide:externalLink'}></dees-icon>` : ''}
|
||||
</a>
|
||||
`)}
|
||||
${this.actions.map(action => html`
|
||||
<button class="header-action" @click=${(e: Event) => {
|
||||
<button class="tile-button primary" @click=${(e: Event) => {
|
||||
e.stopPropagation();
|
||||
this.dispatchEvent(new CustomEvent(action.event || 'action', {
|
||||
detail: action.detail || { label: action.label },
|
||||
@@ -488,20 +584,9 @@ export class SzConfigSection extends DeesElement {
|
||||
${action.label}
|
||||
</button>
|
||||
`)}
|
||||
${this.collapsible ? html`
|
||||
<span class="chevron ${this.isCollapsed ? 'collapsed' : ''}">
|
||||
<dees-icon .icon=${'lucide:chevronDown'}></dees-icon>
|
||||
</span>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-content ${this.isCollapsed ? 'collapsed' : ''}">
|
||||
${this.fields.map(field => this.renderField(field))}
|
||||
<div class="slot-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</dees-tile>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user