Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/knowler/disclosure-element. A disclosure custom element that works nicely with headings unlike `<de
Something went wrong. Try again.
disclosure-element disclosure-element.js
3.0 kB · 105 lines
JavaScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106export class DisclosureElement extends HTMLElement { static styles = new CSSStyleSheet(); static disclosureButtonStyles = new CSSStyleSheet(); static { this.styles.replaceSync(` :host { display: block; }
:host::part(contents) { display: block; } `); this.disclosureButtonStyles.replaceSync(` button { appearance: none; background: none; border: 0; font: inherit; text-align: start; padding: 0; display: inline-block
user-select: text; cursor: text; overflow: visible;
/* We need to do this instead of display: list-item + ::marker because Blink and WebKit have special handling for <button> elements which overrides the list-item display. */
&::before { display: inline; content: "▶ "; font-family: system-ui; cursor: auto; }
&[aria-expanded="true"]::before { content: "▼ "; } }
`); }
#summarySlot; #contents;
#internals = this.attachInternals();
constructor() { super();
this.attachShadow({ mode: "open" });
this.shadowRoot.adoptedStyleSheets = [ DisclosureElement.styles, DisclosureElement.disclosureButtonStyles ]; this.shadowRoot.innerHTML = ` <slot name=summary><button aria-expanded=false aria-controls=contents>Details</button></slot> <slot id=contents part=contents aria-hidden=true hidden=until-found></slot> `;
const [summary, contents] = this.shadowRoot.querySelectorAll("slot");
this.#summarySlot = summary; this.#contents = contents;
this.#internals.role = "group";
this.#summarySlot.addEventListener("slotchange", this); this.#summarySlot.addEventListener("click", this); this.#contents.addEventListener("beforematch", this); }
get #button() { const [summary] = this.#summarySlot.assignedElements({ flatten: true }); return summary.matches("button") ? summary : summary.shadowRoot.querySelector("button"); }
handleEvent(event) { switch (event.type) { case "slotchange": const [summary] = this.#summarySlot.assignedElements({ flatten: true }); if (!summary.matches("button")) { summary.attachShadow({ mode: "open" }); summary.shadowRoot.innerHTML = ` <button part=disclosure-button aria-expanded=false><slot></slot></button> `; summary.shadowRoot.adoptedStyleSheets = [DisclosureElement.disclosureButtonStyles]; } this.#button.ariaControlsElements = [this]; this.#button.ariaExpanded = !this.#contents.hidden; break; case "click": this.#contents.hidden = this.#contents.hidden ? false : "until-found"; // This is just here to overcome a Safari bug this.#contents.ariaHidden = !!this.#contents.hidden; this.#internals.states[this.#contents.hidden ? "delete" : "add"]("open"); this.#button.ariaExpanded = !this.#contents.hidden; break; case "beforematch": this.#contents.ariaHidden = "false"; this.#button.ariaExpanded = "true"; this.#internals.states.add("open"); break; } }}