diff --git a/src/components/details/details.component.ts b/src/components/details/details.component.ts index 88a3f40a..68d06681 100644 --- a/src/components/details/details.component.ts +++ b/src/components/details/details.component.ts @@ -47,11 +47,13 @@ export default class SlDetails extends ShoelaceElement { private readonly localize = new LocalizeController(this); - @query('.details') details: HTMLElement; + @query('.details') details: HTMLDetailsElement; @query('.details__header') header: HTMLElement; @query('.details__body') body: HTMLElement; @query('.details__expand-icon-slot') expandIconSlot: HTMLSlotElement; + detailsObserver: MutationObserver; + /** * Indicates whether or not the details is open. You can toggle this attribute to show and hide the details, or you * can use the `show()` and `hide()` methods and this attribute will reflect the details' open state. @@ -65,11 +67,31 @@ export default class SlDetails extends ShoelaceElement { @property({ type: Boolean, reflect: true }) disabled = false; firstUpdated() { - this.body.hidden = !this.open; this.body.style.height = this.open ? 'auto' : '0'; + if (this.open) { + this.details.open = true; + } + + this.detailsObserver = new MutationObserver(changes => { + for (const change of changes) { + if (change.type === 'attributes' && change.attributeName === 'open') { + if (this.details.open) { + this.show(); + } else { + this.hide(); + } + } + } + }); + this.detailsObserver.observe(this.details, { attributes: true }); } - private handleSummaryClick() { + disconnectedCallback() { + this.detailsObserver.disconnect(); + } + + private handleSummaryClick(ev: MouseEvent) { + ev.preventDefault(); if (!this.disabled) { if (this.open) { this.hide(); @@ -106,15 +128,16 @@ export default class SlDetails extends ShoelaceElement { @watch('open', { waitUntilFirstUpdate: true }) async handleOpenChange() { if (this.open) { + this.details.open = true; // Show const slShow = this.emit('sl-show', { cancelable: true }); if (slShow.defaultPrevented) { this.open = false; + this.details.open = false; return; } await stopAnimations(this.body); - this.body.hidden = false; const { keyframes, options } = getAnimation(this, 'details.show', { dir: this.localize.dir() }); await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options); @@ -125,6 +148,7 @@ export default class SlDetails extends ShoelaceElement { // Hide const slHide = this.emit('sl-hide', { cancelable: true }); if (slHide.defaultPrevented) { + this.details.open = true; this.open = true; return; } @@ -133,9 +157,9 @@ export default class SlDetails extends ShoelaceElement { const { keyframes, options } = getAnimation(this, 'details.hide', { dir: this.localize.dir() }); await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options); - this.body.hidden = true; this.body.style.height = 'auto'; + this.details.open = false; this.emit('sl-after-hide'); } } @@ -164,7 +188,7 @@ export default class SlDetails extends ShoelaceElement { const isRtl = this.localize.dir() === 'rtl'; return html` -