// modules/sidebar.ts var SidebarController = class { constructor() { this.menuToggle = null; this.appLayout = null; this.menuTooltip = null; this.menuToggle = document.getElementById("menuToggle"); this.appLayout = document.querySelector("swp-app-layout"); this.menuTooltip = document.getElementById("menuTooltip"); this.setupListeners(); this.setupTooltips(); this.restoreState(); } /** * Check if sidebar is collapsed */ get isCollapsed() { return this.appLayout?.classList.contains("menu-collapsed") ?? false; } /** * Toggle sidebar collapsed state */ toggle() { if (!this.appLayout) return; this.appLayout.classList.toggle("menu-collapsed"); localStorage.setItem("sidebar-collapsed", String(this.isCollapsed)); } /** * Collapse the sidebar */ collapse() { this.appLayout?.classList.add("menu-collapsed"); localStorage.setItem("sidebar-collapsed", "true"); } /** * Expand the sidebar */ expand() { this.appLayout?.classList.remove("menu-collapsed"); localStorage.setItem("sidebar-collapsed", "false"); } setupListeners() { this.menuToggle?.addEventListener("click", () => this.toggle()); } setupTooltips() { if (!this.menuTooltip) return; const menuItems = document.querySelectorAll("swp-side-menu-item[data-tooltip]"); menuItems.forEach((item) => { item.addEventListener("mouseenter", () => this.showTooltip(item)); item.addEventListener("mouseleave", () => this.hideTooltip()); }); } showTooltip(item) { if (!this.isCollapsed || !this.menuTooltip) return; const rect = item.getBoundingClientRect(); const tooltipText = item.dataset.tooltip; if (!tooltipText) return; this.menuTooltip.textContent = tooltipText; this.menuTooltip.style.left = `${rect.right + 8}px`; this.menuTooltip.style.top = `${rect.top + rect.height / 2}px`; this.menuTooltip.style.transform = "translateY(-50%)"; this.menuTooltip.showPopover(); } hideTooltip() { this.menuTooltip?.hidePopover(); } restoreState() { if (!this.appLayout) return; if (localStorage.getItem("sidebar-collapsed") === "true") { this.appLayout.classList.add("menu-collapsed"); } } }; // modules/drawers.ts var DrawerController = class { constructor() { this.profileDrawer = null; this.notificationDrawer = null; this.todoDrawer = null; this.newTodoDrawer = null; this.overlay = null; this.activeDrawer = null; this.activeGenericDrawer = null; this.profileDrawer = document.getElementById("profileDrawer"); this.notificationDrawer = document.getElementById("notificationDrawer"); this.todoDrawer = document.getElementById("todoDrawer"); this.newTodoDrawer = document.getElementById("newTodoDrawer"); this.overlay = document.getElementById("drawerOverlay"); this.setupListeners(); this.setupGenericDrawers(); } /** * Get currently active drawer name */ get active() { return this.activeDrawer; } /** * Open a drawer by name */ open(name) { this.closeAll(); const drawer = this.getDrawer(name); if (drawer && this.overlay) { drawer.classList.add("active"); this.overlay.classList.add("active"); document.body.style.overflow = "hidden"; this.activeDrawer = name; } } /** * Close a specific drawer */ close(name) { const drawer = this.getDrawer(name); drawer?.classList.remove("active"); if (this.overlay && !document.querySelector('.active[class*="drawer"]')) { this.overlay.classList.remove("active"); document.body.style.overflow = ""; } if (this.activeDrawer === name) { this.activeDrawer = null; } } /** * Close all drawers */ closeAll() { [this.profileDrawer, this.notificationDrawer, this.todoDrawer, this.newTodoDrawer].forEach((drawer) => drawer?.classList.remove("active")); this.closeGenericDrawer(); this.overlay?.classList.remove("active"); document.body.style.overflow = ""; this.activeDrawer = null; } /** * Open a generic drawer by ID */ openGenericDrawer(drawerId) { this.closeAll(); const drawer = document.getElementById(drawerId); if (drawer && this.overlay) { drawer.classList.add("open"); this.overlay.classList.add("active"); document.body.style.overflow = "hidden"; this.activeGenericDrawer = drawer; } } /** * Close the currently open generic drawer */ closeGenericDrawer() { this.activeGenericDrawer?.classList.remove("open"); this.activeGenericDrawer = null; } /** * Open profile drawer */ openProfile() { this.open("profile"); } /** * Open notification drawer */ openNotification() { this.open("notification"); } /** * Open todo drawer (slides on top of profile) */ openTodo() { this.todoDrawer?.classList.add("active"); } /** * Close todo drawer */ closeTodo() { this.todoDrawer?.classList.remove("active"); this.closeNewTodo(); } /** * Open new todo drawer */ openNewTodo() { this.newTodoDrawer?.classList.add("active"); } /** * Close new todo drawer */ closeNewTodo() { this.newTodoDrawer?.classList.remove("active"); } /** * Mark all notifications as read */ markAllNotificationsRead() { if (!this.notificationDrawer) return; const unreadItems = this.notificationDrawer.querySelectorAll( 'swp-notification-item[data-unread="true"]' ); unreadItems.forEach((item) => item.removeAttribute("data-unread")); const badge = document.querySelector("swp-notification-badge"); if (badge) { badge.style.display = "none"; } } getDrawer(name) { switch (name) { case "profile": return this.profileDrawer; case "notification": return this.notificationDrawer; case "todo": return this.todoDrawer; case "newTodo": return this.newTodoDrawer; } } setupListeners() { document.getElementById("profileTrigger")?.addEventListener("click", () => this.openProfile()); document.getElementById("drawerClose")?.addEventListener("click", () => this.close("profile")); document.getElementById("notificationsBtn")?.addEventListener("click", () => this.openNotification()); document.getElementById("notificationDrawerClose")?.addEventListener("click", () => this.close("notification")); document.getElementById("markAllRead")?.addEventListener("click", () => this.markAllNotificationsRead()); document.getElementById("openTodoDrawer")?.addEventListener("click", () => this.openTodo()); document.getElementById("todoDrawerBack")?.addEventListener("click", () => this.closeTodo()); document.getElementById("addTodoBtn")?.addEventListener("click", () => this.openNewTodo()); document.getElementById("newTodoDrawerBack")?.addEventListener("click", () => this.closeNewTodo()); document.getElementById("cancelNewTodo")?.addEventListener("click", () => this.closeNewTodo()); document.getElementById("saveNewTodo")?.addEventListener("click", () => this.closeNewTodo()); this.overlay?.addEventListener("click", () => this.closeAll()); document.addEventListener("keydown", (e) => { if (e.key === "Escape") this.closeAll(); }); this.todoDrawer?.addEventListener("click", (e) => this.handleTodoClick(e)); document.addEventListener("click", (e) => this.handleVisibilityClick(e)); } handleTodoClick(e) { const target = e.target; const todoItem = target.closest("swp-todo-item"); const checkbox = target.closest("swp-todo-checkbox"); if (checkbox && todoItem) { const isCompleted = todoItem.dataset.completed === "true"; if (isCompleted) { todoItem.removeAttribute("data-completed"); } else { todoItem.dataset.completed = "true"; } } const sectionHeader = target.closest("swp-todo-section-header"); if (sectionHeader) { const section = sectionHeader.closest("swp-todo-section"); section?.classList.toggle("collapsed"); } } handleVisibilityClick(e) { const target = e.target; const option = target.closest("swp-visibility-option"); if (option) { document.querySelectorAll("swp-visibility-option").forEach((o) => o.classList.remove("active")); option.classList.add("active"); } } /** * Setup generic drawer triggers and close buttons * Uses data-drawer-trigger="drawer-id" and data-drawer-close attributes */ setupGenericDrawers() { document.addEventListener("click", (e) => { const target = e.target; const trigger = target.closest("[data-drawer-trigger]"); if (trigger) { const drawerId = trigger.dataset.drawerTrigger; if (drawerId) { this.openGenericDrawer(drawerId); } } }); document.addEventListener("click", (e) => { const target = e.target; const closeBtn = target.closest("[data-drawer-close]"); if (closeBtn) { this.closeGenericDrawer(); this.overlay?.classList.remove("active"); document.body.style.overflow = ""; } }); } }; // modules/theme.ts var ThemeController = class _ThemeController { static { this.STORAGE_KEY = "theme-preference"; } static { this.DARK_CLASS = "dark-mode"; } static { this.LIGHT_CLASS = "light-mode"; } constructor() { this.root = document.documentElement; this.themeOptions = document.querySelectorAll("swp-theme-option"); this.applyTheme(this.current); this.updateUI(); this.setupListeners(); } /** * Get the current theme setting */ get current() { const stored = localStorage.getItem(_ThemeController.STORAGE_KEY); if (stored === "dark" || stored === "light" || stored === "system") { return stored; } return "system"; } /** * Check if dark mode is currently active */ get isDark() { return this.root.classList.contains(_ThemeController.DARK_CLASS) || this.systemPrefersDark && !this.root.classList.contains(_ThemeController.LIGHT_CLASS); } /** * Check if system prefers dark mode */ get systemPrefersDark() { return window.matchMedia("(prefers-color-scheme: dark)").matches; } /** * Set theme and persist preference */ set(theme) { localStorage.setItem(_ThemeController.STORAGE_KEY, theme); this.applyTheme(theme); this.updateUI(); } /** * Toggle between light and dark themes */ toggle() { this.set(this.isDark ? "light" : "dark"); } applyTheme(theme) { this.root.classList.remove(_ThemeController.DARK_CLASS, _ThemeController.LIGHT_CLASS); if (theme === "dark") { this.root.classList.add(_ThemeController.DARK_CLASS); } else if (theme === "light") { this.root.classList.add(_ThemeController.LIGHT_CLASS); } } updateUI() { if (!this.themeOptions) return; const darkActive = this.isDark; this.themeOptions.forEach((option) => { const theme = option.dataset.theme; const isActive = theme === "dark" && darkActive || theme === "light" && !darkActive; option.classList.toggle("active", isActive); }); } setupListeners() { this.themeOptions.forEach((option) => { option.addEventListener("click", (e) => this.handleOptionClick(e)); }); window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => this.handleSystemChange()); } handleOptionClick(e) { const target = e.target; const option = target.closest("swp-theme-option"); if (option) { const theme = option.dataset.theme; if (theme) { this.set(theme); } } } handleSystemChange() { if (this.current === "system") { this.updateUI(); } } }; // modules/search.ts var SearchController = class { constructor() { this.input = null; this.container = null; this.input = document.getElementById("globalSearch"); this.container = document.querySelector("swp-topbar-search"); this.setupListeners(); } /** * Get current search value */ get value() { return this.input?.value ?? ""; } /** * Set search value */ set value(val) { if (this.input) { this.input.value = val; } } /** * Focus the search input */ focus() { this.input?.focus(); } /** * Blur the search input */ blur() { this.input?.blur(); } /** * Clear the search input */ clear() { this.value = ""; } setupListeners() { document.addEventListener("keydown", (e) => this.handleKeyboard(e)); if (this.input) { this.input.addEventListener("input", (e) => this.handleInput(e)); const form = this.input.closest("form"); form?.addEventListener("submit", (e) => this.handleSubmit(e)); } } handleKeyboard(e) { if ((e.metaKey || e.ctrlKey) && e.key === "k") { e.preventDefault(); this.focus(); return; } if (e.key === "Escape" && document.activeElement === this.input) { this.blur(); } } handleInput(e) { const target = e.target; const query = target.value.trim(); document.dispatchEvent(new CustomEvent("app:search", { detail: { query }, bubbles: true })); } handleSubmit(e) { e.preventDefault(); const query = this.value.trim(); if (!query) return; document.dispatchEvent(new CustomEvent("app:search-submit", { detail: { query }, bubbles: true })); } }; // modules/lockscreen.ts var LockScreenController = class _LockScreenController { constructor(drawers) { // Demo PIN this.lockScreen = null; this.pinInput = null; this.pinKeypad = null; this.lockTimeEl = null; this.pinDigits = null; this.currentPin = ""; this.drawers = null; this.drawers = drawers ?? null; this.lockScreen = document.getElementById("lockScreen"); this.pinInput = document.getElementById("pinInput"); this.pinKeypad = document.getElementById("pinKeypad"); this.lockTimeEl = document.getElementById("lockTime"); this.pinDigits = this.pinInput?.querySelectorAll("swp-pin-digit") ?? null; this.setupListeners(); } static { this.CORRECT_PIN = "1234"; } /** * Check if lock screen is active */ get isActive() { return this.lockScreen?.classList.contains("active") ?? false; } /** * Show the lock screen */ show() { this.drawers?.closeAll(); if (this.lockScreen) { this.lockScreen.classList.add("active"); document.body.style.overflow = "hidden"; } this.currentPin = ""; this.updateDisplay(); if (this.lockTimeEl) { this.lockTimeEl.textContent = `L\xE5st kl. ${this.formatTime()}`; } } /** * Hide the lock screen */ hide() { if (this.lockScreen) { this.lockScreen.classList.remove("active"); document.body.style.overflow = ""; } this.currentPin = ""; this.updateDisplay(); } formatTime() { const now = /* @__PURE__ */ new Date(); const hours = now.getHours().toString().padStart(2, "0"); const minutes = now.getMinutes().toString().padStart(2, "0"); return `${hours}:${minutes}`; } updateDisplay() { if (!this.pinDigits) return; this.pinDigits.forEach((digit, index) => { digit.classList.remove("filled", "error"); if (index < this.currentPin.length) { digit.textContent = "\u2022"; digit.classList.add("filled"); } else { digit.textContent = ""; } }); } showError() { if (!this.pinDigits) return; this.pinDigits.forEach((digit) => digit.classList.add("error")); this.pinInput?.classList.add("shake"); setTimeout(() => { this.currentPin = ""; this.updateDisplay(); this.pinInput?.classList.remove("shake"); }, 500); } verify() { if (this.currentPin === _LockScreenController.CORRECT_PIN) { this.hide(); } else { this.showError(); } } addDigit(digit) { if (this.currentPin.length >= 4) return; this.currentPin += digit; this.updateDisplay(); if (this.currentPin.length === 4) { setTimeout(() => this.verify(), 200); } } removeDigit() { if (this.currentPin.length === 0) return; this.currentPin = this.currentPin.slice(0, -1); this.updateDisplay(); } clearPin() { this.currentPin = ""; this.updateDisplay(); } setupListeners() { this.pinKeypad?.addEventListener("click", (e) => this.handleKeypadClick(e)); document.addEventListener("keydown", (e) => this.handleKeyboard(e)); document.querySelector("swp-side-menu-action.lock")?.addEventListener("click", () => this.show()); } handleKeypadClick(e) { const target = e.target; const key = target.closest("swp-pin-key"); if (!key) return; const digit = key.dataset.digit; const action = key.dataset.action; if (digit) { this.addDigit(digit); } else if (action === "backspace") { this.removeDigit(); } else if (action === "clear") { this.clearPin(); } } handleKeyboard(e) { if (!this.isActive) return; e.preventDefault(); if (e.key >= "0" && e.key <= "9") { this.addDigit(e.key); } else if (e.key === "Backspace") { this.removeDigit(); } else if (e.key === "Escape") { this.clearPin(); } } }; // app.ts var App = class { constructor() { this.sidebar = new SidebarController(); this.drawers = new DrawerController(); this.theme = new ThemeController(); this.search = new SearchController(); this.lockScreen = new LockScreenController(this.drawers); } }; var app; function init() { app = new App(); if (typeof window !== "undefined") { window.app = app; } } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } var app_default = App; export { App, app, app_default as default }; //# sourceMappingURL=app.js.map