Files
dees-catalog/ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.ts

759 lines
25 KiB
TypeScript
Raw Normal View History

import {
customElement,
type TemplateResult,
property,
state,
html,
css,
cssManager,
DeesElement,
} from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools';
import { zIndexRegistry } from '../../00zindex.js';
import { themeDefaultStyles } from '../../00theme.js';
import { DeesWindowLayer } from '../../00group-overlay/dees-windowlayer/dees-windowlayer.js';
import '../../00group-utility/dees-icon/dees-icon.js';
import type { IDateEvent } from './types.js';
declare global {
interface HTMLElementTagNameMap {
'dees-input-datepicker-popup': DeesInputDatepickerPopup;
}
}
@customElement('dees-input-datepicker-popup')
export class DeesInputDatepickerPopup extends DeesElement {
// Properties set by the parent
@property({ attribute: false })
accessor triggerRect: DOMRect | null = null;
@property({ attribute: false })
accessor ownerComponent: HTMLElement | null = null;
@property({ type: Boolean })
accessor enableTime: boolean = false;
@property({ type: String })
accessor timeFormat: '24h' | '12h' = '24h';
@property({ type: Number })
accessor minuteIncrement: number = 1;
@property({ type: Number })
accessor weekStartsOn: 0 | 1 = 1;
@property({ type: String })
accessor minDate: string = '';
@property({ type: String })
accessor maxDate: string = '';
@property({ type: Array })
accessor disabledDates: string[] = [];
@property({ type: Boolean })
accessor enableTimezone: boolean = false;
@property({ type: String })
accessor timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
@property({ type: Array })
accessor events: IDateEvent[] = [];
@property({ type: Boolean })
accessor opensToTop: boolean = false;
// Internal state
@state()
accessor selectedDate: Date | null = null;
@state()
accessor viewDate: Date = new Date();
@state()
accessor selectedHour: number = 0;
@state()
accessor selectedMinute: number = 0;
@state()
accessor menuZIndex: number = 1000;
@state()
accessor visible: boolean = false;
private windowLayer: DeesWindowLayer | null = null;
private isDestroying: boolean = false;
public static styles = [
themeDefaultStyles,
cssManager.defaultStyles,
css`
:host {
position: fixed;
top: 0;
left: 0;
width: 0;
height: 0;
pointer-events: none;
}
* {
box-sizing: border-box;
}
.calendar-popup {
position: fixed;
pointer-events: auto;
will-change: transform, opacity;
transition: all 0.15s ease;
opacity: 0;
transform: translateY(-4px);
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
box-shadow: ${cssManager.bdTheme(
'0 10px 15px -3px hsl(0 0% 0% / 0.1), 0 4px 6px -4px hsl(0 0% 0% / 0.1)',
'0 10px 15px -3px hsl(0 0% 0% / 0.2), 0 4px 6px -4px hsl(0 0% 0% / 0.2)'
)};
border-radius: 6px;
padding: 12px;
user-select: none;
min-width: 280px;
}
.calendar-popup.top {
transform: translateY(4px);
}
.calendar-popup.show {
transform: translateY(0);
opacity: 1;
}
/* Calendar Header */
.calendar-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
gap: 8px;
}
.month-year-display {
font-weight: 500;
font-size: 14px;
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
flex: 1;
text-align: center;
}
.nav-button {
width: 28px;
height: 28px;
border: none;
background: transparent;
cursor: pointer;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
transition: all 0.2s ease;
}
.nav-button:hover {
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
}
/* Weekday headers */
.weekdays {
display: grid;
grid-template-columns: repeat(7, 1fr);
margin-bottom: 4px;
}
.weekday {
text-align: center;
font-size: 12px;
font-weight: 400;
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
padding: 0 0 8px 0;
}
/* Days grid */
.days-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
}
.day {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 6px;
font-size: 14px;
transition: all 0.2s ease;
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
border: none;
width: 36px;
height: 36px;
background: transparent;
position: relative;
}
.day:hover:not(.disabled) {
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
}
.day.other-month {
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
opacity: 0.5;
}
.day.today {
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
font-weight: 500;
}
.day.selected {
background: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
color: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(222.2 47.4% 11.2%)')};
font-weight: 500;
}
.day.disabled {
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
cursor: not-allowed;
opacity: 0.3;
}
/* Event indicators */
.event-indicator {
position: absolute;
bottom: 4px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 2px;
}
.event-dot {
width: 4px;
height: 4px;
border-radius: 50%;
background: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
}
.event-dot.info { background: ${cssManager.bdTheme('hsl(211 70% 52%)', 'hsl(211 70% 62%)')}; }
.event-dot.warning { background: ${cssManager.bdTheme('hsl(45 90% 45%)', 'hsl(45 90% 55%)')}; }
.event-dot.success { background: ${cssManager.bdTheme('hsl(142 69% 45%)', 'hsl(142 69% 55%)')}; }
.event-dot.error { background: ${cssManager.bdTheme('hsl(0 72% 51%)', 'hsl(0 72% 61%)')}; }
.event-count {
position: absolute;
top: 2px;
right: 2px;
min-width: 16px;
height: 16px;
padding: 0 4px;
background: ${cssManager.bdTheme('hsl(0 72% 51%)', 'hsl(0 72% 61%)')};
color: white;
border-radius: 8px;
font-size: 10px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
.event-tooltip {
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')};
color: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 0%)')};
padding: 8px 12px;
border-radius: 6px;
font-size: 12px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
z-index: 10;
}
.day.has-event:hover .event-tooltip { opacity: 1; }
/* Time selector */
.time-selector {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
}
.time-selector-title {
font-size: 12px;
font-weight: 500;
margin-bottom: 8px;
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
}
.time-inputs {
display: flex;
gap: 8px;
align-items: center;
}
.time-input {
width: 65px;
height: 36px;
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
border-radius: 6px;
padding: 0 12px;
font-size: 14px;
text-align: center;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
transition: all 0.2s ease;
}
.time-input:focus {
outline: none;
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(222.2 47.4% 11.2% / 0.1)', 'hsl(210 20% 98% / 0.1)')};
}
.time-separator {
font-size: 14px;
font-weight: 500;
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
}
.am-pm-selector { display: flex; gap: 4px; margin-left: 8px; }
.am-pm-button {
padding: 6px 12px;
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
border-radius: 6px;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
}
.am-pm-button.selected {
background: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
color: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(222.2 47.4% 11.2%)')};
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 11.2%)', 'hsl(210 20% 98%)')};
}
.am-pm-button:hover:not(.selected) {
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
}
/* Timezone selector */
.timezone-selector {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
}
.timezone-selector-title {
font-size: 12px;
font-weight: 500;
margin-bottom: 8px;
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
}
.timezone-select {
width: 100%;
height: 36px;
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
border-radius: 6px;
padding: 0 12px;
font-size: 14px;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
cursor: pointer;
}
/* Action buttons */
.calendar-actions {
display: flex;
gap: 8px;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
}
.action-button {
flex: 1;
height: 36px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
}
.today-button {
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(224 71.4% 4.1%)')};
border: 1px solid ${cssManager.bdTheme('hsl(214.3 31.8% 91.4%)', 'hsl(217.2 32.6% 17.5%)')};
color: ${cssManager.bdTheme('hsl(224 71.4% 4.1%)', 'hsl(210 20% 98%)')};
}
.today-button:hover {
background: ${cssManager.bdTheme('hsl(210 20% 98%)', 'hsl(215 27.9% 16.9%)')};
}
.clear-action-button {
background: transparent;
border: 1px solid transparent;
color: ${cssManager.bdTheme('hsl(220 8.9% 46.1%)', 'hsl(215 20.2% 65.1%)')};
}
.clear-action-button:hover {
background: ${cssManager.bdTheme('hsl(0 72.2% 50.6% / 0.1)', 'hsl(0 62.8% 30.6% / 0.1)')};
color: ${cssManager.bdTheme('hsl(0 72.2% 50.6%)', 'hsl(0 62.8% 30.6%)')};
}
`,
];
private static readonly MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
];
private static readonly TIMEZONES = [
{ value: 'UTC', label: 'UTC (Coordinated Universal Time)' },
{ value: 'America/New_York', label: 'Eastern Time (US & Canada)' },
{ value: 'America/Chicago', label: 'Central Time (US & Canada)' },
{ value: 'America/Denver', label: 'Mountain Time (US & Canada)' },
{ value: 'America/Los_Angeles', label: 'Pacific Time (US & Canada)' },
{ value: 'Europe/London', label: 'London' },
{ value: 'Europe/Paris', label: 'Paris' },
{ value: 'Europe/Berlin', label: 'Berlin' },
{ value: 'Europe/Moscow', label: 'Moscow' },
{ value: 'Asia/Dubai', label: 'Dubai' },
{ value: 'Asia/Kolkata', label: 'India Standard Time' },
{ value: 'Asia/Shanghai', label: 'China Standard Time' },
{ value: 'Asia/Tokyo', label: 'Tokyo' },
{ value: 'Australia/Sydney', label: 'Sydney' },
{ value: 'Pacific/Auckland', label: 'Auckland' },
];
public render(): TemplateResult {
if (!this.triggerRect) return html``;
const posStyle = this.computePositionStyle();
const weekDays = this.weekStartsOn === 1
? ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
const days = this.getDaysInMonth();
const isAM = this.selectedHour < 12;
return html`
<div
class="calendar-popup ${this.visible ? 'show' : ''} ${this.opensToTop ? 'top' : 'bottom'}"
style="${posStyle}; z-index: ${this.menuZIndex};"
>
<div class="calendar-header">
<button class="nav-button" @click=${this.previousMonth}>
<dees-icon icon="lucide:chevronLeft" iconSize="16"></dees-icon>
</button>
<div class="month-year-display">
${DeesInputDatepickerPopup.MONTH_NAMES[this.viewDate.getMonth()]} ${this.viewDate.getFullYear()}
</div>
<button class="nav-button" @click=${this.nextMonth}>
<dees-icon icon="lucide:chevronRight" iconSize="16"></dees-icon>
</button>
</div>
<div class="weekdays">
${weekDays.map(day => html`<div class="weekday">${day}</div>`)}
</div>
<div class="days-grid">
${days.map(day => {
const isToday = this.isToday(day);
const isSelected = this.isSelected(day);
const isOtherMonth = day.getMonth() !== this.viewDate.getMonth();
const isDayDisabled = this.isDayDisabled(day);
const dayEvents = this.getEventsForDate(day);
const hasEvents = dayEvents.length > 0;
const totalEventCount = dayEvents.reduce((sum, event) => sum + (event.count || 1), 0);
return html`
<div
class="day ${isOtherMonth ? 'other-month' : ''} ${isToday ? 'today' : ''} ${isSelected ? 'selected' : ''} ${isDayDisabled ? 'disabled' : ''} ${hasEvents ? 'has-event' : ''}"
@click=${() => !isDayDisabled && this.handleSelectDate(day)}
>
${day.getDate()}
${hasEvents ? html`
${totalEventCount > 3 ? html`
<div class="event-count">${totalEventCount}</div>
` : html`
<div class="event-indicator">
${dayEvents.slice(0, 3).map(event => html`
<div class="event-dot ${event.type || 'info'}"></div>
`)}
</div>
`}
${dayEvents[0].title ? html`
<div class="event-tooltip">
${dayEvents[0].title}
${totalEventCount > 1 ? html` (+${totalEventCount - 1} more)` : ''}
</div>
` : ''}
` : ''}
</div>
`;
})}
</div>
${this.enableTime ? html`
<div class="time-selector">
<div class="time-selector-title">Time</div>
<div class="time-inputs">
<input type="number" class="time-input"
.value=${this.timeFormat === '12h'
? (this.selectedHour === 0 ? 12 : this.selectedHour > 12 ? this.selectedHour - 12 : this.selectedHour).toString().padStart(2, '0')
: this.selectedHour.toString().padStart(2, '0')}
@input=${this.handleHourInput}
min="${this.timeFormat === '12h' ? 1 : 0}"
max="${this.timeFormat === '12h' ? 12 : 23}"
/>
<span class="time-separator">:</span>
<input type="number" class="time-input"
.value=${this.selectedMinute.toString().padStart(2, '0')}
@input=${this.handleMinuteInput}
min="0" max="59" step="${this.minuteIncrement || 1}"
/>
${this.timeFormat === '12h' ? html`
<div class="am-pm-selector">
<button class="am-pm-button ${isAM ? 'selected' : ''}" @click=${() => this.setAMPM('am')}>AM</button>
<button class="am-pm-button ${!isAM ? 'selected' : ''}" @click=${() => this.setAMPM('pm')}>PM</button>
</div>
` : ''}
</div>
</div>
` : ''}
${this.enableTimezone ? html`
<div class="timezone-selector">
<div class="timezone-selector-title">Timezone</div>
<select class="timezone-select" .value=${this.timezone} @change=${this.handleTimezoneChange}>
${DeesInputDatepickerPopup.TIMEZONES.map(tz => html`
<option value="${tz.value}" ?selected=${tz.value === this.timezone}>${tz.label}</option>
`)}
</select>
</div>
` : ''}
<div class="calendar-actions">
<button class="action-button today-button" @click=${this.handleSelectToday}>Today</button>
<button class="action-button clear-action-button" @click=${this.handleClear}>Clear</button>
</div>
</div>
`;
}
private computePositionStyle(): string {
const rect = this.triggerRect!;
const left = rect.left;
if (this.opensToTop) {
const bottom = window.innerHeight - rect.top + 4;
return `left: ${left}px; bottom: ${bottom}px; top: auto`;
} else {
const top = rect.bottom + 4;
return `left: ${left}px; top: ${top}px`;
}
}
// Calendar logic
private getDaysInMonth(): Date[] {
const year = this.viewDate.getFullYear();
const month = this.viewDate.getMonth();
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
const days: Date[] = [];
const startOffset = this.weekStartsOn === 1
? (firstDay.getDay() === 0 ? 6 : firstDay.getDay() - 1)
: firstDay.getDay();
for (let i = startOffset; i > 0; i--) days.push(new Date(year, month, 1 - i));
for (let i = 1; i <= lastDay.getDate(); i++) days.push(new Date(year, month, i));
const remaining = 42 - days.length;
for (let i = 1; i <= remaining; i++) days.push(new Date(year, month + 1, i));
return days;
}
private isToday(date: Date): boolean {
const today = new Date();
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
}
private isSelected(date: Date): boolean {
if (!this.selectedDate) return false;
return date.getDate() === this.selectedDate.getDate() && date.getMonth() === this.selectedDate.getMonth() && date.getFullYear() === this.selectedDate.getFullYear();
}
private isDayDisabled(date: Date): boolean {
if (this.minDate) { const min = new Date(this.minDate); if (date < min) return true; }
if (this.maxDate) { const max = new Date(this.maxDate); if (date > max) return true; }
if (this.disabledDates?.length) {
return this.disabledDates.some(ds => {
try { const d = new Date(ds); return date.getDate() === d.getDate() && date.getMonth() === d.getMonth() && date.getFullYear() === d.getFullYear(); }
catch { return false; }
});
}
return false;
}
private getEventsForDate(date: Date): IDateEvent[] {
if (!this.events?.length) return [];
const dateStr = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
return this.events.filter(e => e.date === dateStr);
}
private previousMonth(): void {
this.viewDate = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() - 1, 1);
}
private nextMonth(): void {
this.viewDate = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() + 1, 1);
}
// Event dispatching
private handleSelectDate(day: Date): void {
this.selectedDate = new Date(day.getFullYear(), day.getMonth(), day.getDate(), this.selectedHour, this.selectedMinute);
this.dispatchEvent(new CustomEvent('date-selected', { detail: this.selectedDate }));
if (!this.enableTime) {
this.dispatchEvent(new CustomEvent('close-request'));
}
}
private handleSelectToday(): void {
const today = new Date();
this.selectedDate = today;
this.viewDate = new Date(today);
this.selectedHour = today.getHours();
this.selectedMinute = today.getMinutes();
this.dispatchEvent(new CustomEvent('date-selected', { detail: this.selectedDate }));
if (!this.enableTime) {
this.dispatchEvent(new CustomEvent('close-request'));
}
}
private handleClear(): void {
this.dispatchEvent(new CustomEvent('date-cleared'));
this.dispatchEvent(new CustomEvent('close-request'));
}
private handleHourInput = (e: InputEvent): void => {
const input = e.target as HTMLInputElement;
let value = parseInt(input.value) || 0;
if (this.timeFormat === '12h') {
value = Math.max(1, Math.min(12, value));
if (this.selectedHour >= 12 && value !== 12) this.selectedHour = value + 12;
else if (this.selectedHour < 12 && value === 12) this.selectedHour = 0;
else this.selectedHour = value;
} else {
this.selectedHour = Math.max(0, Math.min(23, value));
}
this.emitTimeUpdate();
};
private handleMinuteInput = (e: InputEvent): void => {
const input = e.target as HTMLInputElement;
let value = parseInt(input.value) || 0;
value = Math.max(0, Math.min(59, value));
if (this.minuteIncrement > 1) value = Math.round(value / this.minuteIncrement) * this.minuteIncrement;
this.selectedMinute = value;
this.emitTimeUpdate();
};
private setAMPM(period: 'am' | 'pm'): void {
if (period === 'am' && this.selectedHour >= 12) this.selectedHour -= 12;
else if (period === 'pm' && this.selectedHour < 12) this.selectedHour += 12;
this.emitTimeUpdate();
}
private handleTimezoneChange = (e: Event): void => {
this.timezone = (e.target as HTMLSelectElement).value;
this.emitTimeUpdate();
};
private emitTimeUpdate(): void {
if (this.selectedDate) {
this.selectedDate = new Date(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate(), this.selectedHour, this.selectedMinute);
this.dispatchEvent(new CustomEvent('date-selected', { detail: this.selectedDate }));
}
}
// Show/hide lifecycle
public async show(): Promise<void> {
this.windowLayer = await DeesWindowLayer.createAndShow();
this.windowLayer.addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('close-request'));
});
this.menuZIndex = zIndexRegistry.getNextZIndex();
zIndexRegistry.register(this, this.menuZIndex);
this.style.zIndex = this.menuZIndex.toString();
document.body.appendChild(this);
await domtools.plugins.smartdelay.delayFor(0);
this.visible = true;
window.addEventListener('scroll', this.handleScrollOrResize, { capture: true, passive: true });
window.addEventListener('resize', this.handleScrollOrResize, { passive: true });
}
public async hide(): Promise<void> {
if (this.isDestroying) return;
this.isDestroying = true;
window.removeEventListener('scroll', this.handleScrollOrResize, { capture: true } as EventListenerOptions);
window.removeEventListener('resize', this.handleScrollOrResize);
zIndexRegistry.unregister(this);
if (this.windowLayer) {
this.windowLayer.destroy();
this.windowLayer = null;
}
this.visible = false;
await domtools.plugins.smartdelay.delayFor(150);
if (this.parentElement) this.parentElement.removeChild(this);
this.isDestroying = false;
}
private handleScrollOrResize = (): void => {
this.dispatchEvent(new CustomEvent('reposition-request'));
};
async disconnectedCallback() {
await super.disconnectedCallback();
window.removeEventListener('scroll', this.handleScrollOrResize, { capture: true } as EventListenerOptions);
window.removeEventListener('resize', this.handleScrollOrResize);
zIndexRegistry.unregister(this);
}
}