-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
// Copyright 2026 Shota FUJI
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
import css from "./x-field-group.css";
export class XFieldGroup extends HTMLElement {
static observedAttributes = ["opened"];
#trigger = document.createElement("button");
constructor() {
super();
const shadow = this.attachShadow({
mode: "open",
});
const bodyId =
"group-" +
(typeof crypto.randomUUID === "function"
? crypto.randomUUID()
: Math.floor(Math.random() * 0xffffffff).toString(16));
const style = document.createElement("style");
style.textContent = css;
shadow.appendChild(style);
this.#trigger.setAttribute("aria-expanded", "true");
this.#trigger.setAttribute("aria-controls", bodyId);
this.#trigger.classList.add("header");
shadow.appendChild(this.#trigger);
const title = document.createElement("slot");
title.name = "title";
this.#trigger.appendChild(title);
const description = document.createElement("slot");
description.name = "description";
this.#trigger.appendChild(description);
const caret = document.createElement("span");
caret.classList.add("caret");
this.#trigger.appendChild(caret);
const caretSymbol = document.createElement("span");
caretSymbol.classList.add("caret-symbol");
caretSymbol.textContent = ">";
caret.appendChild(caretSymbol);
const body = document.createElement("div");
body.id = bodyId;
body.classList.add("body");
shadow.appendChild(body);
const slot = document.createElement("slot");
body.appendChild(slot);
}
connectedCallback() {
this.setAttribute("opened", "");
this.#trigger.addEventListener("click", this.#onToggleClick);
}
disconnectedCallback() {
this.#trigger.removeEventListener("click", this.#onToggleClick);
}
attributeChangedCallback(name, _oldValue, newValue) {
if (name !== "opened") {
return;
}
const isOpened = typeof newValue === "string";
this.#trigger.setAttribute("aria-expanded", isOpened.toString());
}
#onToggleClick = (event) => {
event.preventDefault();
if (this.hasAttribute("opened")) {
this.removeAttribute("opened");
} else {
this.setAttribute("opened", "");
}
};
}