Skip to content

Commit e09e82f

Browse files
committed
Adds an accordion component
1 parent e64cbef commit e09e82f

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { css, html, LitElement } from 'lit';
2+
import { customElement, property } from 'lit/decorators.js';
3+
4+
const accordionTagName = 'gl-accordion';
5+
6+
@customElement(accordionTagName)
7+
export class GlAccordion extends LitElement {
8+
static override shadowRootOptions: ShadowRootInit = {
9+
...LitElement.shadowRootOptions,
10+
delegatesFocus: true,
11+
};
12+
13+
static override styles = css`
14+
:host {
15+
display: block;
16+
font-family: var(--vscode-font-family);
17+
font-size: var(--vscode-font-size);
18+
font-weight: var(--vscode-font-weight);
19+
background-color: var(--vscode-editor-background);
20+
color: var(--vscode-foreground);
21+
}
22+
23+
/*
24+
details {
25+
border: 1px solid var(--vscode-panel-border);
26+
border-radius: 4px;
27+
overflow: hidden;
28+
}
29+
*/
30+
31+
.header {
32+
padding: 8px 12px;
33+
background-color: var(--vscode-sideBar-background);
34+
cursor: pointer;
35+
user-select: none;
36+
list-style: none;
37+
outline: none;
38+
display: flex;
39+
align-items: center;
40+
gap: 0.6rem;
41+
}
42+
43+
.header::-webkit-details-marker {
44+
display: none;
45+
}
46+
47+
.label {
48+
flex: 1;
49+
display: block;
50+
}
51+
52+
.icon {
53+
flex: none;
54+
width: 20px;
55+
color: var(--vscode-foreground);
56+
opacity: 0.6;
57+
}
58+
59+
.header:hover {
60+
background-color: var(--vscode-list-hoverBackground);
61+
}
62+
63+
.header:focus {
64+
outline: 1px solid var(--vscode-focusBorder);
65+
outline-offset: -1px;
66+
}
67+
68+
.content {
69+
padding: 12px;
70+
background-color: var(--vscode-editor-background);
71+
}
72+
`;
73+
74+
@property({ type: Boolean }) open = false;
75+
76+
get headerId() {
77+
return `gl-accordion-header-${this.id ?? Math.random().toString(36).substring(2, 9)}`;
78+
}
79+
80+
override render() {
81+
return html`
82+
<details ?open=${this.open} @toggle=${this._handleToggle} role="region" aria-labelledby=${this.headerId}>
83+
<summary part="header" class="header" id=${this.headerId} role="button" aria-expanded=${this.open}>
84+
<slot class="label" name="header"></slot>
85+
<code-icon class="icon" icon=${this.open ? 'chevron-down' : 'chevron-right'}></code-icon>
86+
</summary>
87+
<div part="content" class="content">
88+
<slot></slot>
89+
</div>
90+
</details>
91+
`;
92+
}
93+
94+
private _handleToggle(e: Event) {
95+
const details = e.target as HTMLDetailsElement;
96+
this.open = details.open;
97+
this.dispatchEvent(
98+
new CustomEvent('gl-toggle', {
99+
detail: { open: this.open },
100+
bubbles: true,
101+
composed: true,
102+
}),
103+
);
104+
}
105+
}
106+
107+
declare global {
108+
interface HTMLElementTagNameMap {
109+
[accordionTagName]: GlAccordion;
110+
}
111+
}

0 commit comments

Comments
 (0)