import {
afterNextRender,
booleanAttribute,
ChangeDetectionStrategy,
Component,
computed,
effect,
ElementRef,
input,
signal,
viewChild,
ViewEncapsulation,
} from "@angular/core";
import { SdResizeDirective, type SdResizeEvent } from "../../core/events/sd-resize";
@Component({
selector: "sd-collapse",
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
standalone: true,
imports: [SdResizeDirective],
template: `
`,
styles: [
/* language=SCSS */ `
sd-collapse {
display: block;
overflow: hidden;
}
`,
],
})
export class SdCollapse {
open = input(false, { transform: booleanAttribute });
private readonly _contentElRef = viewChild.required>("contentEl");
contentHeight = signal(0);
private readonly _initialized = signal(false);
contentMarginTop = computed(() => {
return this.open() ? "" : `-${this.contentHeight()}px`;
});
contentTransition = computed(() => {
if (!this._initialized()) return "";
return this.open() ? "margin-top 0.1s ease-out" : "margin-top 0.1s ease-in";
});
constructor() {
afterNextRender(() => {
this.contentHeight.set(this._contentElRef().nativeElement.offsetHeight);
requestAnimationFrame(() => {
this._initialized.set(true);
});
});
effect(() => {
// Re-measure height on open transition to handle stale values from closed state
this.open();
this.contentHeight.set(this._contentElRef().nativeElement.offsetHeight);
});
}
onContentResize(event: SdResizeEvent): void {
if (event.heightChanged) {
this.contentHeight.set(this._contentElRef().nativeElement.offsetHeight);
}
}
}