import { LitElement, html, css } from 'lit';
import { property } from 'lit/decorators.js';
export class Backdrop extends LitElement {
@property({ type: Boolean, reflect: true }) open = false;
@property({ type: Boolean }) invisible = false;
@property({ type: String }) transitionDuration = '225ms';
@property({ type: Number }) zIndex = 1300;
static styles = css`
:host {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
right: 0;
bottom: 0;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
-webkit-tap-highlight-color: transparent;
z-index: var(--backdrop-z-index, 1300);
opacity: 0;
visibility: hidden;
transition: opacity var(--backdrop-transition-duration, 225ms) cubic-bezier(0.4, 0, 0.2, 1) 0ms,
visibility var(--backdrop-transition-duration, 225ms) cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}
:host([open]) {
opacity: 1;
visibility: visible;
}
:host([invisible]) {
background-color: transparent;
}
.backdrop-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
`;
connectedCallback() {
super.connectedCallback();
this.updateCSSVariables();
}
updated(changedProperties: Map) {
super.updated(changedProperties);
if (changedProperties.has('transitionDuration') || changedProperties.has('zIndex')) {
this.updateCSSVariables();
}
if (changedProperties.has('open')) {
this.handleOpenChange();
}
}
private updateCSSVariables() {
this.style.setProperty('--backdrop-transition-duration', this.transitionDuration);
this.style.setProperty('--backdrop-z-index', this.zIndex.toString());
}
private handleOpenChange() {
if (this.open) {
// Prevent body scroll when backdrop is open
document.body.style.overflow = 'hidden';
// Focus trap - focus the backdrop
this.focus();
// Add escape key listener
document.addEventListener('keydown', this.handleEscapeKey);
} else {
// Restore body scroll
document.body.style.overflow = '';
// Remove escape key listener
document.removeEventListener('keydown', this.handleEscapeKey);
}
}
private handleEscapeKey = (event: KeyboardEvent) => {
if (event.key === 'Escape' && this.open) {
this.handleClick(event);
}
};
private handleClick(event: Event) {
// Only handle clicks on the backdrop itself, not on children
if (event.target === this || event.target === this.shadowRoot?.querySelector('.backdrop-content')) {
this.dispatchEvent(new CustomEvent('backdrop-click', {
detail: { originalEvent: event },
bubbles: true,
composed: true
}));
}
}
disconnectedCallback() {
super.disconnectedCallback();
// Clean up when component is removed
document.body.style.overflow = '';
document.removeEventListener('keydown', this.handleEscapeKey);
}
render() {
return html`
`;
}
}