feat(12-02): make config sections collapsible with chevron dropdown

Sections start collapsed showing only heading, description, and a
chevron icon. Click to expand and reveal the fields. The open property
allows sections to start expanded if needed.
This commit is contained in:
2026-03-14 12:57:35 -04:00
parent 649e516aa3
commit 12c678284c
@@ -1,9 +1,10 @@
import { LitElement, html, css, nothing } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { customElement, property, state } from 'lit/decorators.js';
/**
* A visual grouping wrapper for config fields.
* Renders a heading, optional description, and a slot for fields.
* Renders a collapsible heading with optional description,
* and a slot for fields. Starts collapsed by default.
*/
@customElement('config-section')
export class ConfigSection extends LitElement {
@@ -17,11 +18,42 @@ export class ConfigSection extends LitElement {
background: var(--yj-bg-surface, #212529);
border: 1px solid var(--yj-border-subtle, #333);
border-radius: 6px;
}
.header {
display: flex;
align-items: flex-start;
gap: 0.5em;
padding: 1.25em;
cursor: pointer;
user-select: none;
}
.header:hover {
background: var(--yj-bg-elevated, #343a40);
border-radius: 6px;
}
.chevron {
flex-shrink: 0;
width: 16px;
height: 16px;
margin-top: 0.15em;
transition: transform 150ms ease;
color: var(--yj-text-tertiary, #888);
}
.chevron.open {
transform: rotate(90deg);
}
.header-text {
flex: 1;
min-width: 0;
}
h3 {
margin: 0 0 0.25em;
margin: 0;
font-size: 1em;
font-weight: 700;
color: var(--yj-text-primary, #fff);
@@ -30,7 +62,11 @@ export class ConfigSection extends LitElement {
.description {
font-size: 0.8em;
color: var(--yj-text-tertiary, #888);
margin: 0 0 1em;
margin: 0.25em 0 0;
}
.body {
padding: 0 1.25em 1.25em;
}
.fields {
@@ -45,18 +81,55 @@ export class ConfigSection extends LitElement {
@property({ type: String })
description = '';
@property({ type: Boolean })
open = false;
@state()
private expanded = false;
override connectedCallback(): void {
super.connectedCallback();
this.expanded = this.open;
}
private toggle = (): void => {
this.expanded = !this.expanded;
};
override render() {
return html`
<div class="section">
<h3>${this.heading}</h3>
${this.description
? html`<p class="description">
${this.description}
</p>`
: nothing}
<div class="fields">
<slot></slot>
<div
class="header"
@click=${this.toggle}
>
<svg
class="chevron ${this.expanded ? 'open' : ''}"
viewBox="0 0 16 16"
fill="currentColor"
>
<path
d="M6 3l5 5-5 5z"
/>
</svg>
<div class="header-text">
<h3>${this.heading}</h3>
${this.description
? html`<p class="description">
${this.description}
</p>`
: nothing}
</div>
</div>
${this.expanded
? html`
<div class="body">
<div class="fields">
<slot></slot>
</div>
</div>
`
: nothing}
</div>
`;
}