import { DeesElement, customElement, html, css, cssManager, property, state, type TemplateResult, } from '@design.estate/dees-element'; export interface IConfigField { key: string; value: string | number | boolean | string[] | null; type?: 'text' | 'boolean' | 'badge' | 'pills' | 'code' | 'link'; description?: string; linkTo?: string; } declare global { interface HTMLElementTagNameMap { 'sz-config-section': SzConfigSection; } } @customElement('sz-config-section') export class SzConfigSection extends DeesElement { public static demo = () => html` `; public static demoGroups = ['Configuration']; @property({ type: String }) public accessor title: string = ''; @property({ type: String }) public accessor subtitle: string = ''; @property({ type: String }) public accessor icon: string = ''; @property({ type: String }) public accessor status: 'enabled' | 'disabled' | 'not-configured' | 'warning' = 'enabled'; @property({ type: Array }) public accessor fields: IConfigField[] = []; @property({ type: Boolean }) public accessor collapsible: boolean = false; @property({ type: Boolean }) public accessor collapsed: boolean = false; @state() accessor isCollapsed: boolean = false; public static styles = [ cssManager.defaultStyles, css` :host { display: block; margin-bottom: 16px; } .section { background: ${cssManager.bdTheme('#ffffff', '#09090b')}; border: 1px solid ${cssManager.bdTheme('#e4e4e7', '#27272a')}; border-radius: 8px; overflow: hidden; } .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')}; cursor: default; user-select: none; } :host([collapsible]) .section-header { cursor: pointer; } :host([collapsible]) .section-header:hover { background: ${cssManager.bdTheme('#ebebed', '#1c1c1f')}; } .header-left { display: flex; align-items: center; gap: 12px; min-width: 0; } .header-icon { display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; background: ${cssManager.bdTheme('#e4e4e7', '#27272a')}; border-radius: 8px; flex-shrink: 0; } .header-icon dees-icon { font-size: 18px; color: ${cssManager.bdTheme('#52525b', '#a1a1aa')}; } .header-text { min-width: 0; } .header-title { font-size: 15px; font-weight: 600; color: ${cssManager.bdTheme('#18181b', '#fafafa')}; line-height: 1.3; } .header-subtitle { font-size: 12px; color: ${cssManager.bdTheme('#71717a', '#a1a1aa')}; line-height: 1.3; margin-top: 1px; } .header-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; } /* Status badge */ .status-badge { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 9999px; font-size: 12px; font-weight: 500; white-space: nowrap; } .status-badge.enabled { background: ${cssManager.bdTheme('#dcfce7', 'rgba(34,197,94,0.2)')}; color: ${cssManager.bdTheme('#16a34a', '#22c55e')}; } .status-badge.disabled { background: ${cssManager.bdTheme('#fee2e2', 'rgba(239,68,68,0.2)')}; color: ${cssManager.bdTheme('#dc2626', '#ef4444')}; } .status-badge.not-configured { background: ${cssManager.bdTheme('#f4f4f5', 'rgba(113,113,122,0.2)')}; color: ${cssManager.bdTheme('#71717a', '#a1a1aa')}; } .status-badge.warning { background: ${cssManager.bdTheme('#fef3c7', 'rgba(245,158,11,0.15)')}; color: ${cssManager.bdTheme('#92400e', '#fbbf24')}; } .status-dot { width: 7px; height: 7px; border-radius: 50%; } .status-badge.enabled .status-dot { background: ${cssManager.bdTheme('#16a34a', '#22c55e')}; } .status-badge.disabled .status-dot { background: ${cssManager.bdTheme('#dc2626', '#ef4444')}; } .status-badge.not-configured .status-dot { background: ${cssManager.bdTheme('#a1a1aa', '#52525b')}; } .status-badge.warning .status-dot { background: ${cssManager.bdTheme('#f59e0b', '#fbbf24')}; } /* Chevron */ .chevron { display: flex; align-items: center; transition: transform 200ms ease; } .chevron.collapsed { transform: rotate(-90deg); } .chevron dees-icon { font-size: 16px; color: ${cssManager.bdTheme('#a1a1aa', '#52525b')}; } /* Content */ .section-content { padding: 0; } .section-content.collapsed { display: none; } /* Field rows */ .field-row { display: flex; align-items: flex-start; justify-content: space-between; padding: 10px 20px; border-bottom: 1px solid ${cssManager.bdTheme('#f4f4f5', '#1a1a1e')}; gap: 16px; } .field-row:last-child { border-bottom: none; } .field-key { font-size: 13px; font-weight: 500; color: ${cssManager.bdTheme('#71717a', '#a1a1aa')}; flex-shrink: 0; min-width: 140px; padding-top: 1px; } .field-value { font-size: 13px; font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; color: ${cssManager.bdTheme('#18181b', '#fafafa')}; text-align: right; word-break: break-all; } .field-value.null-value { color: ${cssManager.bdTheme('#a1a1aa', '#52525b')}; font-style: italic; font-family: inherit; } /* Boolean display */ .bool-value { display: inline-flex; align-items: center; gap: 5px; font-family: inherit; font-size: 13px; font-weight: 500; } .bool-value.true { color: ${cssManager.bdTheme('#16a34a', '#22c55e')}; } .bool-value.false { color: ${cssManager.bdTheme('#dc2626', '#ef4444')}; } .bool-dot { width: 6px; height: 6px; border-radius: 50%; } .bool-value.true .bool-dot { background: ${cssManager.bdTheme('#16a34a', '#22c55e')}; } .bool-value.false .bool-dot { background: ${cssManager.bdTheme('#dc2626', '#ef4444')}; } /* Pills */ .pills { display: flex; flex-wrap: wrap; gap: 5px; justify-content: flex-end; } .pill { display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 9999px; font-size: 12px; font-weight: 500; font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; background: ${cssManager.bdTheme('#eff6ff', 'rgba(59,130,246,0.1)')}; color: ${cssManager.bdTheme('#2563eb', '#60a5fa')}; } /* Code value */ .code-value { font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 12px; background: ${cssManager.bdTheme('#f4f4f5', '#18181b')}; padding: 2px 8px; border-radius: 4px; color: ${cssManager.bdTheme('#18181b', '#fafafa')}; } /* Link value */ .link-value { color: ${cssManager.bdTheme('#2563eb', '#60a5fa')}; cursor: pointer; text-decoration: none; font-family: inherit; font-size: 13px; } .link-value:hover { text-decoration: underline; } /* Description hint */ .field-description { font-size: 11px; color: ${cssManager.bdTheme('#a1a1aa', '#52525b')}; margin-top: 3px; text-align: right; } /* Slot for custom content */ .slot-content { border-top: 1px solid ${cssManager.bdTheme('#f4f4f5', '#1a1a1e')}; } .slot-content:empty { display: none; border-top: none; } /* Badge type */ .badge-value { display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 9999px; font-size: 12px; font-weight: 500; background: ${cssManager.bdTheme('#f4f4f5', '#27272a')}; color: ${cssManager.bdTheme('#52525b', '#a1a1aa')}; } `, ]; async connectedCallback() { await super.connectedCallback(); this.isCollapsed = this.collapsed; if (this.collapsible) { this.setAttribute('collapsible', ''); } } public render(): TemplateResult { const statusLabels: Record = { 'enabled': 'Enabled', 'disabled': 'Disabled', 'not-configured': 'Not Configured', 'warning': 'Warning', }; return html` { if (this.collapsible) { this.isCollapsed = !this.isCollapsed; } }} > ${this.icon ? html` ` : ''} ${this.title} ${this.subtitle ? html`${this.subtitle}` : ''} ${this.status ? html` ${statusLabels[this.status] || this.status} ` : ''} ${this.collapsible ? html` ` : ''} ${this.fields.map(field => this.renderField(field))} `; } private renderField(field: IConfigField): TemplateResult { return html` ${field.key} ${this.renderFieldValue(field)} ${field.description ? html`${field.description}` : ''} `; } private renderFieldValue(field: IConfigField): TemplateResult { const value = field.value; const type = field.type || this.inferType(value); // Null / undefined if (value === null || value === undefined) { return html`Not configured`; } switch (type) { case 'boolean': return html` ${value ? 'Enabled' : 'Disabled'} `; case 'pills': if (Array.isArray(value) && value.length === 0) { return html`None`; } return html` ${(value as string[]).map(v => html`${v}`)} `; case 'code': return html`${String(value)}`; case 'badge': return html`${String(value)}`; case 'link': return html` { if (field.linkTo) { this.dispatchEvent(new CustomEvent('navigate', { detail: { target: field.linkTo }, bubbles: true, composed: true, })); } }} >${String(value)} `; default: return html`${String(value)}`; } } private inferType(value: unknown): string { if (typeof value === 'boolean') return 'boolean'; if (Array.isArray(value)) return 'pills'; return 'text'; } }