// SPDX-License-Identifier: AGPL-3.0-or-later import { LitElement, html, css, } from "//shared.localhost:8888/third_party/lit/lit-all.min.js"; export class NotificationPanel extends LitElement { static properties = { open: { type: Boolean, reflect: true }, notifications: { type: Array, state: true }, }; static styles = css` @import url("//system.localhost:8888/notification_panel.css"); `; constructor() { super(); this.open = false; this.notifications = []; this.handleKeyDown = this.handleKeyDown.bind(this); } connectedCallback() { super.connectedCallback(); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListeners(); } updated(changedProperties) { if (changedProperties.has("open")) { if (this.open) { requestAnimationFrame(() => { document.addEventListener("keydown", this.handleKeyDown); }); } else { this.removeEventListeners(); } } } removeEventListeners() { document.removeEventListener("keydown", this.handleKeyDown); } handleKeyDown(e) { if (e.key === "Escape") { this.close(); } } close() { this.open = false; this.dispatchEvent( new CustomEvent("panel-closed", { bubbles: true, composed: true, }) ); } handleBackdropClick(e) { if (e.target.classList.contains("backdrop")) { this.close(); } } handleNotificationClick(notification, e) { // Don't handle click if dismiss button was clicked if (e.target.closest(".notification-dismiss")) { return; } this.dispatchEvent( new CustomEvent("notification-click", { bubbles: true, composed: true, detail: { notification }, }) ); } handleDismiss(notification, e) { e.stopPropagation(); this.dispatchEvent( new CustomEvent("notification-dismiss", { bubbles: true, composed: true, detail: { notification }, }) ); } handleClearAll() { this.dispatchEvent( new CustomEvent("notification-clear-all", { bubbles: true, composed: true, }) ); } formatTimeAgo(timestamp) { if (!timestamp) { return ""; } const now = Date.now(); const diff = now - timestamp; const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) { return `${days}d ago`; } if (hours > 0) { return `${hours}h ago`; } if (minutes > 0) { return `${minutes}m ago`; } return "Just now"; } renderNotificationIcon(notification) { if (notification.iconUrl) { return html``; } return html``; } renderNotification(notification) { return html`
this.handleNotificationClick(notification, e)} >
${this.renderNotificationIcon(notification)}
${notification.title}
${notification.body}
${this.formatTimeAgo(notification.timestamp)}
`; } renderEmptyState() { return html`
No notifications
`; } render() { return html`
Notifications
${this.notifications.length > 0 ? html`` : ""}
${this.notifications.length > 0 ? this.notifications.map((n) => this.renderNotification(n)) : this.renderEmptyState()}
`; } } customElements.define("notification-panel", NotificationPanel);