web-mojo 2.1.995 → 2.1.1044
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -16
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +739 -24
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.css +305 -266
- package/dist/auth.es.js +537 -2175
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +3 -2
- package/dist/charts.es.js.map +1 -1
- package/dist/chunks/ChatView-Bvkdj-lq.js +2 -0
- package/dist/chunks/ChatView-Bvkdj-lq.js.map +1 -0
- package/dist/chunks/{ChatView-Bk3XGtNh.js → ChatView-DBgQzOyI.js} +14 -6
- package/dist/chunks/ChatView-DBgQzOyI.js.map +1 -0
- package/dist/chunks/{ContextMenu-BuEqfeZS.js → ContextMenu-hQH_6Pyi.js} +349 -2
- package/dist/chunks/ContextMenu-hQH_6Pyi.js.map +1 -0
- package/dist/chunks/ContextMenu-snx9Dd1s.js +3 -0
- package/dist/chunks/ContextMenu-snx9Dd1s.js.map +1 -0
- package/dist/chunks/{DataView-OUqaLmGB.js → DataView-CWejLV3B.js} +2 -1
- package/dist/chunks/DataView-CWejLV3B.js.map +1 -0
- package/dist/chunks/DataView-D7j4IWyS.js +2 -0
- package/dist/chunks/DataView-D7j4IWyS.js.map +1 -0
- package/dist/chunks/Dialog-7T8ENHYD.js +2 -0
- package/dist/chunks/Dialog-7T8ENHYD.js.map +1 -0
- package/dist/chunks/{Dialog-BiVgKzSK.js → Dialog-BcJG5Vta.js} +1358 -4
- package/dist/chunks/Dialog-BcJG5Vta.js.map +1 -0
- package/dist/chunks/MetricsMiniChartWidget-CN1HPnWf.js +2 -0
- package/dist/chunks/{MetricsMiniChartWidget-Esvv-lFp.js.map → MetricsMiniChartWidget-CN1HPnWf.js.map} +1 -1
- package/dist/chunks/{MetricsMiniChartWidget-CCroU6BZ.js → MetricsMiniChartWidget-DALWxrzu.js} +2 -2
- package/dist/chunks/{MetricsMiniChartWidget-CCroU6BZ.js.map → MetricsMiniChartWidget-DALWxrzu.js.map} +1 -1
- package/dist/chunks/{PDFViewer-NeL91Gon.js → PDFViewer-CgdSGU1n.js} +2 -2
- package/dist/chunks/{PDFViewer-NeL91Gon.js.map → PDFViewer-CgdSGU1n.js.map} +1 -1
- package/dist/chunks/{PDFViewer-D4uo3oiA.js → PDFViewer-DtJIlPXi.js} +2 -2
- package/dist/chunks/{PDFViewer-D4uo3oiA.js.map → PDFViewer-DtJIlPXi.js.map} +1 -1
- package/dist/chunks/{TopNav-CYTDmhAF.js → TokenManager-BanwFrq7.js} +368 -5
- package/dist/chunks/TokenManager-BanwFrq7.js.map +1 -0
- package/dist/chunks/TokenManager-DIEFCQ3B.js +2 -0
- package/dist/chunks/TokenManager-DIEFCQ3B.js.map +1 -0
- package/dist/chunks/version-BaFu2yii.js +38 -0
- package/dist/chunks/version-BaFu2yii.js.map +1 -0
- package/dist/chunks/version-WMgX72-y.js +2 -0
- package/dist/chunks/version-WMgX72-y.js.map +1 -0
- package/dist/css/web-mojo.css +1 -17
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.cjs.js.map +1 -1
- package/dist/docit.es.js +4 -6
- package/dist/docit.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +29 -31
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.cjs.js.map +1 -1
- package/dist/lightbox.es.js +4 -3
- package/dist/lightbox.es.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/ChatView-Bk3XGtNh.js.map +0 -1
- package/dist/chunks/ChatView-Bz_YZdHd.js +0 -2
- package/dist/chunks/ChatView-Bz_YZdHd.js.map +0 -1
- package/dist/chunks/ContextMenu-BuEqfeZS.js.map +0 -1
- package/dist/chunks/ContextMenu-DcLhcYMp.js +0 -3
- package/dist/chunks/ContextMenu-DcLhcYMp.js.map +0 -1
- package/dist/chunks/DataView-CdDY9ijM.js +0 -2
- package/dist/chunks/DataView-CdDY9ijM.js.map +0 -1
- package/dist/chunks/DataView-OUqaLmGB.js.map +0 -1
- package/dist/chunks/Dialog-BiVgKzSK.js.map +0 -1
- package/dist/chunks/Dialog-DmIPK_Bi.js +0 -2
- package/dist/chunks/Dialog-DmIPK_Bi.js.map +0 -1
- package/dist/chunks/MetricsMiniChartWidget-Esvv-lFp.js +0 -2
- package/dist/chunks/Page-CvbwEoLv.js +0 -2
- package/dist/chunks/Page-CvbwEoLv.js.map +0 -1
- package/dist/chunks/Page-Deq4y2Kq.js +0 -351
- package/dist/chunks/Page-Deq4y2Kq.js.map +0 -1
- package/dist/chunks/TokenManager-CAZNcCMs.js +0 -366
- package/dist/chunks/TokenManager-CAZNcCMs.js.map +0 -1
- package/dist/chunks/TokenManager-CJBYcVqs.js +0 -2
- package/dist/chunks/TokenManager-CJBYcVqs.js.map +0 -1
- package/dist/chunks/TopNav-23B5R-dl.js +0 -2
- package/dist/chunks/TopNav-23B5R-dl.js.map +0 -1
- package/dist/chunks/TopNav-CYTDmhAF.js.map +0 -1
- package/dist/chunks/WebApp-CQKxglmg.js +0 -2
- package/dist/chunks/WebApp-CQKxglmg.js.map +0 -1
- package/dist/chunks/WebApp-CWuDQOYo.js +0 -1388
- package/dist/chunks/WebApp-CWuDQOYo.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog-DmIPK_Bi.js","sources":["../../src/core/views/feedback/Dialog.js"],"sourcesContent":["/**\n * Dialog - Complete Bootstrap 5 Modal component for MOJO framework\n * Supports all Bootstrap 5 modal features including sizes, fullscreen, scrollable, etc.\n * Can accept View instances as body content\n */\n\nimport View from '@core/View.js';\n\n\nclass Dialog extends View {\n static _openDialogs = [];\n static _baseZIndex = {\n backdrop: 1050,\n modal: 1055\n };\n\n /**\n * Check if there's a fullscreen table and return appropriate z-index\n */\n static getFullscreenAwareZIndex() {\n const fullscreenTable = document.querySelector('.table-fullscreen');\n if (fullscreenTable) {\n // Fullscreen table uses z-index 9999, so modals should be much higher\n return {\n backdrop: 10040,\n modal: 10050\n };\n }\n return this._baseZIndex;\n }\n\n static _busyIndicator = null;\n static _busyCounter = 0;\n static _busyTimeout = null;\n\n /**\n * Fix all backdrop stacking - ensures proper layering of all open modals\n */\n static fixAllBackdropStacking() {\n const backdrops = document.querySelectorAll('.modal-backdrop');\n const openDialogs = Dialog._openDialogs;\n \n if (backdrops.length === 0 || openDialogs.length === 0) return;\n \n // Sort dialogs by z-index to get correct stacking order\n const sortedDialogs = [...openDialogs].sort((a, b) => \n (a._dialogZIndex || 0) - (b._dialogZIndex || 0)\n );\n \n // Set backdrop z-indices to create proper stacking\n // Each backdrop should cover all previous modals but stay below its own modal\n backdrops.forEach((backdrop, index) => {\n if (index < sortedDialogs.length) {\n const dialog = sortedDialogs[index];\n const backdropZIndex = dialog._dialogZIndex - 5;\n backdrop.style.zIndex = backdropZIndex;\n \n // Move backdrop to correct container\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n \n if (backdrop.parentNode !== targetContainer) {\n targetContainer.appendChild(backdrop);\n }\n }\n });\n }\n\n /**\n * Update backdrop stacking for all open dialogs\n */\n static updateAllBackdropStacking() {\n Dialog.fixAllBackdropStacking();\n }\n\n /**\n * Shows a full-screen busy indicator.\n * Manages a counter for nested calls, only showing one indicator.\n * @param {object} options - Options { timeout, message }\n */\n static showBusy(options = {}) {\n const { timeout = 30000, message = 'Loading...' } = options;\n\n this._busyCounter++;\n\n if (this._busyCounter === 1) {\n if (this._busyTimeout) {\n clearTimeout(this._busyTimeout);\n }\n\n if (!this._busyIndicator) {\n const zIndexBase = this.getFullscreenAwareZIndex();\n const busyZIndex = zIndexBase.modal + 1000; // Higher than any modal\n \n this._busyIndicator = document.createElement('div');\n this._busyIndicator.className = 'mojo-busy-indicator';\n this._busyIndicator.innerHTML = `\n <div class=\"mojo-busy-spinner\">\n <div class=\"spinner-border text-light\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <p class=\"mojo-busy-message mt-3 text-light\">${message}</p>\n </div>\n <style>\n .mojo-busy-indicator {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background-color: rgba(0, 0, 0, 0.5); z-index: ${busyZIndex};\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.15s linear;\n }\n .mojo-busy-indicator.show { opacity: 1; }\n .mojo-busy-spinner .spinner-border { width: 3rem; height: 3rem; }\n </style>\n `;\n document.body.appendChild(this._busyIndicator);\n }\n\n const msgElement = this._busyIndicator.querySelector('.mojo-busy-message');\n if (msgElement) msgElement.textContent = message;\n\n setTimeout(() => this._busyIndicator.classList.add('show'), 10);\n\n this._busyTimeout = setTimeout(() => {\n console.error('Busy indicator timed out.');\n this.hideBusy(true); // Force hide\n this.alert({\n title: 'Operation Timed Out',\n message: 'The operation took too long. Please check your connection and try again.',\n type: 'danger'\n });\n }, timeout);\n }\n }\n\n /**\n * Hides the full-screen busy indicator.\n * Decrements the counter and only hides when the counter reaches zero.\n * @param {boolean} force - If true, forces the indicator to hide immediately.\n */\n static hideBusy(force = false) {\n if (force) {\n this._busyCounter = 0;\n } else {\n this._busyCounter--;\n }\n\n if (this._busyCounter <= 0) {\n this._busyCounter = 0;\n if (this._busyTimeout) {\n clearTimeout(this._busyTimeout);\n this._busyTimeout = null;\n }\n\n if (this._busyIndicator) {\n this._busyIndicator.classList.remove('show');\n setTimeout(() => {\n if (this._busyIndicator && this._busyCounter === 0) {\n this._busyIndicator.remove();\n this._busyIndicator = null;\n }\n }, 150);\n }\n }\n }\n\n constructor(options = {}) {\n // Generate unique ID if not provided\n const modalId = options.id || `modal-${Date.now()}`;\n\n super({\n ...options,\n id: modalId, // Pass the ID to parent constructor\n tagName: 'div',\n className: `modal ${options.fade !== false ? 'fade' : ''} ${options.className || ''}`,\n attributes: {\n tabindex: '-1',\n 'aria-hidden': 'true',\n 'aria-labelledby': options.labelledBy || `${modalId}-label`,\n 'aria-describedby': options.describedBy || null,\n ...options.attributes\n }\n });\n\n // Store modal ID for internal use\n this.modalId = modalId;\n\n // Dialog configuration\n this.title = options.title || '';\n this.titleId = `${this.modalId}-label`;\n\n // Size options: sm, md (default), lg, xl, fullscreen, auto\n // Or responsive fullscreen: fullscreen-sm-down, fullscreen-md-down, etc.\n // 'auto' enables dynamic sizing based on content dimensions\n this.size = options.size || '';\n\n // Layout options\n this.centered = options.centered !== undefined ? options.centered : false;\n this.scrollable = options.scrollable !== undefined ? options.scrollable : false;\n // Auto-sizing: dynamically size modal based on content dimensions\n // Can be enabled with autoSize: true or size: 'auto'\n // Waits for modal animation to complete before measuring content\n this.autoSize = options.autoSize || options.size === 'auto'; // Auto-size modal based on content dimensions\n\n // Bootstrap modal options\n this.backdrop = options.backdrop !== undefined ? options.backdrop : true; // true, false, 'static'\n this.keyboard = options.keyboard !== undefined ? options.keyboard : true;\n this.focus = options.focus !== undefined ? options.focus : true;\n\n // Content\n this.header = options.header !== undefined ? options.header : true;\n this.headerContent = options.headerContent || null;\n this.closeButton = options.closeButton !== undefined ? options.closeButton : true;\n this.contextMenu = options.contextMenu || null;\n\n // Enhanced body handling - support View, Promise<View>, or function returning View\n this.body = options.body || options.content || '';\n this.bodyView = null; // Will hold View instance if body is a View\n this.bodyClass = options.bodyClass || '';\n this.noBodyPadding = options.noBodyPadding || false; // Remove default modal-body padding\n\n // Auto-sizing constraints - only used when autoSize is enabled\n this.minWidth = options.minWidth || 300; // Minimum modal width (px)\n this.minHeight = options.minHeight || 200; // Minimum modal height (px)\n this.maxWidthPercent = options.maxWidthPercent || 0.9; // Max width as % of viewport\n this.maxHeightPercent = options.maxHeightPercent || 0.8; // Max height as % of viewport\n\n // Handle different body types\n this._processBodyContent(this.body);\n\n this.footer = options.footer || null;\n this.footerView = null; // Will hold View instance if footer is a View\n this.footerClass = options.footerClass || '';\n\n // Handle different footer types\n this._processFooterContent(this.footer);\n\n // Buttons configuration\n this.buttons = options.buttons || null;\n\n // Callbacks for Bootstrap events\n this.onShow = options.onShow || null; // show.bs.modal\n this.onShown = options.onShown || null; // shown.bs.modal\n this.onHide = options.onHide || null; // hide.bs.modal\n this.onHidden = options.onHidden || null; // hidden.bs.modal\n this.onHidePrevented = options.onHidePrevented || null; // hidePrevented.bs.modal\n\n // Auto show on creation\n this.autoShow = options.autoShow !== undefined ? options.autoShow : false;\n\n // Bootstrap modal instance\n this.modal = null;\n\n // Related target (button that triggered the modal)\n this.relatedTarget = options.relatedTarget || null;\n }\n\n /**\n * Process body content to detect and handle View instances\n */\n _processBodyContent(body) {\n if (body && body.render) {\n this.bodyView = body;\n this.body = ''; // Clear string body\n this.addChild(this.bodyView); // Add as child for proper lifecycle\n } else if (typeof body === 'function') {\n // Support lazy View creation\n try {\n const result = body();\n if (result instanceof View) {\n this.bodyView = result;\n this.body = '';\n this.addChild(this.bodyView);\n } else if (result instanceof Promise) {\n // Mark for async processing\n this.bodyPromise = result;\n this.body = '<div class=\"text-center\"><div class=\"spinner-border spinner-border-sm\"></div></div>';\n } else {\n this.body = result;\n }\n } catch (error) {\n console.error('Error processing body function:', error);\n this.body = body;\n }\n } else {\n this.body = body;\n }\n }\n\n /**\n * Process footer content to detect and handle View instances\n */\n _processFooterContent(footer) {\n if (footer instanceof View) {\n this.footerView = footer;\n this.footer = null;\n this.addChild(this.footerView);\n } else if (typeof footer === 'function') {\n // Support lazy View creation\n try {\n const result = footer();\n if (result instanceof View) {\n this.footerView = result;\n this.footer = null;\n this.addChild(this.footerView);\n } else if (result instanceof Promise) {\n // Mark for async processing\n this.footerPromise = result;\n this.footer = '<div class=\"text-center\"><div class=\"spinner-border spinner-border-sm\"></div></div>';\n } else {\n this.footer = result;\n }\n } catch (error) {\n console.error('Error processing footer function:', error);\n this.footer = footer;\n }\n } else {\n this.footer = footer;\n }\n }\n\n /**\n * Get dialog template with all Bootstrap 5 features\n */\n async getTemplate() {\n // Build dialog classes\n const dialogClasses = ['modal-dialog'];\n\n // Add size class (excluding 'auto' which uses default sizing)\n if (this.size && this.size !== 'auto') {\n if (this.size.startsWith('fullscreen')) {\n // Fullscreen or responsive fullscreen\n dialogClasses.push(`modal-${this.size}`);\n } else if (['sm', 'lg', 'xl'].includes(this.size)) {\n // Standard sizes\n dialogClasses.push(`modal-${this.size}`);\n }\n }\n\n // Add centered class\n if (this.centered) {\n dialogClasses.push('modal-dialog-centered');\n }\n\n // Add scrollable class\n if (this.scrollable) {\n dialogClasses.push('modal-dialog-scrollable');\n }\n\n return `\n <div class=\"${dialogClasses.join(' ')}\">\n <div class=\"modal-content\">\n ${await this.buildHeader()}\n ${await this.buildBody()}\n ${await this.buildFooter()}\n </div>\n </div>\n `;\n }\n\n /**\n * Build modal header\n */\n async buildHeader() {\n if (!this.header) {\n return '';\n }\n\n if (this.headerContent) {\n return `<div class=\"modal-header\">${this.headerContent}</div>`;\n }\n\n // Build context menu or close button\n let headerActions = '';\n if (this.contextMenu && this.contextMenu.items && this.contextMenu.items.length > 0) {\n headerActions = await this.buildContextMenu();\n } else if (this.closeButton) {\n headerActions = '<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>';\n }\n\n return `\n <div class=\"modal-header\">\n ${this.title ? `<h5 class=\"modal-title\" id=\"${this.titleId}\">${this.title}</h5>` : ''}\n ${headerActions}\n </div>\n `;\n }\n\n async buildContextMenu() {\n const menuItems = await this.filterContextMenuItems();\n if (menuItems.length === 0) {\n // If no items pass permission checks, show regular close button\n return this.closeButton ? '<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>' : '';\n }\n\n const triggerIcon = this.contextMenu.icon || 'bi-three-dots-vertical';\n const buttonClass = this.contextMenu.buttonClass || 'btn btn-link p-1 mojo-modal-context-menu-btn';\n\n const menuItemsHtml = menuItems.map(item => {\n if (item.type === 'divider') {\n return '<li><hr class=\"dropdown-divider\"></li>';\n }\n\n const icon = item.icon ? `<i class=\"${item.icon} me-2\"></i>` : '';\n const label = item.label || '';\n\n if (item.href) {\n return `<li><a class=\"dropdown-item\" href=\"${item.href}\"${item.target ? ` target=\"${item.target}\"` : ''}>${icon}${label}</a></li>`;\n } else if (item.action) {\n const dataAttrs = Object.keys(item)\n .filter(key => key.startsWith('data-'))\n .map(key => `${key}=\"${item[key]}\"`)\n .join(' ');\n return `<li><a class=\"dropdown-item\" data-action=\"${item.action}\" ${dataAttrs}>${icon}${label}</a></li>`;\n }\n\n return '';\n }).join('');\n\n return `\n <div class=\"dropdown\">\n <button class=\"${buttonClass}\" type=\"button\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"${triggerIcon}\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n ${menuItemsHtml}\n </ul>\n </div>\n `;\n }\n\n async filterContextMenuItems() {\n if (!this.contextMenu || !this.contextMenu.items) {\n return [];\n }\n\n const filteredItems = [];\n\n for (const item of this.contextMenu.items) {\n // Always include dividers\n if (item.type === 'divider') {\n filteredItems.push(item);\n continue;\n }\n\n // Check permissions if specified\n if (item.permissions) {\n try {\n const app = this.getApp?.();\n let user = null;\n\n if (app) {\n user = app.activeUser || app.getState?.('activeUser');\n }\n\n // Also check window.getApp as fallback for mock systems\n if (!user && typeof window !== 'undefined' && window.getApp) {\n try {\n const globalApp = window.getApp();\n user = globalApp?.activeUser;\n } catch (e) {\n // Ignore global app errors\n }\n }\n\n if (user && user.hasPermission) {\n if (!user.hasPermission(item.permissions)) {\n continue; // Skip this item\n }\n } else {\n // If no permission system available, skip items with permission requirements\n continue;\n }\n } catch (error) {\n console.warn('Error checking permissions for context menu item:', error);\n continue;\n }\n }\n\n filteredItems.push(item);\n }\n\n return filteredItems;\n }\n\n /**\n * Build modal body\n */\n async buildBody() {\n // If we have a View instance as body\n if (this.bodyView) {\n this.bodyView.replaceById = true;\n const bodyClass = this.noBodyPadding ? `modal-body p-0 ${this.bodyClass}` : `modal-body ${this.bodyClass}`;\n return `<div class=\"${bodyClass}\" data-view-container=\"body\">\n <!-- View will be mounted here -->\n <div id=\"${this.bodyView.id}\"></div>\n </div>`;\n }\n\n // Regular string/HTML body\n if (!this.body && this.body !== '') {\n return '';\n }\n\n const bodyClass = this.noBodyPadding ? `modal-body p-0 ${this.bodyClass}` : `modal-body ${this.bodyClass}`;\n return `\n <div class=\"${bodyClass}\">\n ${this.body}\n </div>\n `;\n }\n\n /**\n * Build modal footer\n */\n async buildFooter() {\n // If we have a View instance as footer\n if (this.footerView) {\n return `<div class=\"modal-footer ${this.footerClass}\" data-view-container=\"footer\"></div>`;\n }\n\n // Custom footer content\n if (this.footer !== null && typeof this.footer === 'string') {\n return `<div class=\"modal-footer ${this.footerClass}\">${this.footer}</div>`;\n }\n\n // Build footer from buttons\n if (this.buttons && this.buttons.length > 0) {\n const buttonsHtml = this.buttons.map(btn => {\n const dismissAttr = btn.dismiss ? 'data-bs-dismiss=\"modal\"' : '';\n const actionAttr = btn.action ? `data-action=\"${btn.action}\"` : '';\n const idAttr = btn.id ? `id=\"${btn.id}\"` : '';\n const disabledAttr = btn.disabled ? 'disabled' : '';\n\n return `\n <button type=\"${btn.type || 'button'}\"\n class=\"btn ${btn.class || 'btn-secondary'}\"\n ${idAttr}\n ${dismissAttr}\n ${actionAttr}\n ${disabledAttr}>\n ${btn.icon ? `<i class=\"bi ${btn.icon} me-1\"></i>` : ''}\n ${btn.text || 'Button'}\n </button>\n `;\n }).join('');\n\n return `<div class=\"modal-footer ${this.footerClass}\">${buttonsHtml}</div>`;\n }\n\n // No footer\n return '';\n }\n\n\n /**\n * Override mount to not require a container for dialogs\n * Dialogs are appended to body or fullscreen element\n */\n async mount(_container = null) {\n if (this.mounted || this.destroyed) {\n return;\n }\n\n // For dialogs, we only need the element, not a container\n if (!this.element) {\n throw new Error('Cannot mount dialog without element');\n }\n\n // Call lifecycle hooks\n await this.onBeforeMount();\n\n // Append to fullscreen element if it exists, otherwise to body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n targetContainer.appendChild(this.element);\n\n // Bind DOM events\n this.bindEvents();\n\n // Set mounted flag\n this.mounted = true;\n\n // Call after mount (this initializes Bootstrap modal)\n await this.onAfterMount();\n\n // Emit mounted event\n this.emit('mounted', { view: this });\n\n return this;\n }\n\n /**\n * After render - prepare for View instances and apply syntax highlighting\n */\n async onAfterRender() {\n await super.onAfterRender();\n\n // Apply Prism syntax highlighting if available and there are code blocks\n if (window.Prism && this.element) {\n const codeBlocks = this.element.querySelectorAll('pre code');\n if (codeBlocks.length > 0) {\n // Use Prism's highlightAllUnder to highlight code within this dialog\n window.Prism.highlightAllUnder(this.element);\n }\n }\n\n // Child views will be mounted in onAfterMount when element is in DOM\n\n // Apply auto-sizing after rendering if enabled\n if (this.autoSize) {\n this.setupAutoSizing();\n }\n }\n\n /**\n * After mount - initialize Bootstrap modal and mount child views\n */\n async onAfterMount() {\n await super.onAfterMount();\n\n if (typeof window !== 'undefined' && window.bootstrap && window.bootstrap.Modal) {\n // Set data attributes if needed\n if (this.backdrop === 'static') {\n this.element.setAttribute('data-bs-backdrop', 'static');\n }\n if (!this.keyboard) {\n this.element.setAttribute('data-bs-keyboard', 'false');\n }\n\n // Initialize Bootstrap modal with options\n this.modal = new window.bootstrap.Modal(this.element, {\n backdrop: this.backdrop,\n keyboard: this.keyboard,\n focus: this.focus\n });\n\n // Bind Bootstrap events\n this.bindBootstrapEvents();\n\n // Auto show if requested\n if (this.autoShow) {\n this.show(this.relatedTarget);\n }\n }\n }\n\n /**\n * Setup auto-sizing - wait for modal animation to complete\n */\n setupAutoSizing() {\n if (!this.element) return;\n\n // Listen for modal shown event to apply sizing after animation\n this.element.addEventListener('shown.bs.modal', () => {\n this.applyAutoSizing();\n }, { once: true });\n\n // Fallback: apply immediately if modal is already shown or no animation\n setTimeout(() => {\n if (this.isShown()) {\n this.applyAutoSizing();\n }\n }, 100);\n }\n\n /**\n * Apply auto-sizing based on content dimensions\n */\n applyAutoSizing() {\n if (!this.element) return;\n\n try {\n const modalDialog = this.element.querySelector('.modal-dialog');\n const modalContent = this.element.querySelector('.modal-content');\n const modalBody = this.element.querySelector('.modal-body');\n\n if (!modalDialog || !modalContent || !modalBody) {\n console.warn('Dialog auto-sizing: Required elements not found');\n return;\n }\n\n // Wait for child views to fully render\n if (this.bodyView && !this.bodyView.element) {\n setTimeout(() => this.applyAutoSizing(), 50);\n return;\n }\n\n // Store original styles for restoration\n const originalStyles = {\n dialogMaxWidth: modalDialog.style.maxWidth,\n dialogWidth: modalDialog.style.width,\n contentWidth: modalContent.style.width,\n contentMaxHeight: modalContent.style.maxHeight,\n bodyOverflow: modalBody.style.overflowY\n };\n\n // Temporarily remove size constraints to measure natural content size\n modalDialog.style.maxWidth = 'none';\n modalDialog.style.width = 'auto';\n modalContent.style.width = 'auto';\n modalContent.style.maxHeight = 'none';\n\n // Force layout recalculation\n modalContent.offsetHeight;\n\n // Measure content dimensions after forced layout\n const contentRect = modalContent.getBoundingClientRect();\n\n // Calculate viewport constraints with margins\n const viewportMargin = 40;\n const maxWidth = Math.min(\n window.innerWidth * this.maxWidthPercent,\n window.innerWidth - viewportMargin\n );\n const maxHeight = Math.min(\n window.innerHeight * this.maxHeightPercent,\n window.innerHeight - viewportMargin\n );\n\n // Calculate optimal dimensions with padding for content\n let optimalWidth = Math.max(this.minWidth, Math.ceil(contentRect.width + 20));\n let optimalHeight = Math.max(this.minHeight, Math.ceil(contentRect.height));\n\n // Apply viewport constraints\n optimalWidth = Math.min(optimalWidth, maxWidth);\n const heightExceedsMax = contentRect.height > maxHeight;\n\n // Apply the calculated size\n modalDialog.style.maxWidth = `${optimalWidth}px`;\n modalDialog.style.width = `${optimalWidth}px`;\n\n // Handle height overflow with scrolling\n if (heightExceedsMax) {\n modalContent.style.maxHeight = `${maxHeight}px`;\n modalBody.style.overflowY = 'auto';\n optimalHeight = maxHeight;\n }\n\n // Store the applied dimensions\n this.autoSizedWidth = optimalWidth;\n this.autoSizedHeight = optimalHeight;\n this._originalStyles = originalStyles;\n\n } catch (error) {\n console.error('Error in dialog auto-sizing:', error);\n // Fallback: ensure modal is still usable\n this.element.querySelector('.modal-dialog').style.maxWidth = '';\n }\n }\n\n /**\n * Reset auto-sizing and restore original modal dimensions\n */\n resetAutoSizing() {\n if (!this.autoSize || !this._originalStyles || !this.element) return;\n\n try {\n const modalDialog = this.element.querySelector('.modal-dialog');\n const modalContent = this.element.querySelector('.modal-content');\n const modalBody = this.element.querySelector('.modal-body');\n\n if (modalDialog && modalContent && modalBody) {\n // Restore original styles\n modalDialog.style.maxWidth = this._originalStyles.dialogMaxWidth || '';\n modalDialog.style.width = this._originalStyles.dialogWidth || '';\n modalContent.style.width = this._originalStyles.contentWidth || '';\n modalContent.style.maxHeight = this._originalStyles.contentMaxHeight || '';\n modalBody.style.overflowY = this._originalStyles.bodyOverflow || '';\n\n // Clear stored dimensions\n delete this.autoSizedWidth;\n delete this.autoSizedHeight;\n delete this._originalStyles;\n }\n } catch (error) {\n console.error('Error resetting dialog auto-sizing:', error);\n }\n }\n\n /**\n * Bind Bootstrap modal events\n */\n bindBootstrapEvents() {\n // show.bs.modal\n this.element.addEventListener('show.bs.modal', (e) => {\n // Manage stacking for multiple dialogs with fullscreen awareness\n const stackIndex = Dialog._openDialogs.length;\n const zIndexBase = Dialog.getFullscreenAwareZIndex();\n const newZIndex = zIndexBase.modal + (stackIndex * 20);\n this.element.style.zIndex = newZIndex;\n \n // Store z-index on this dialog for later reference\n this._dialogZIndex = newZIndex;\n this._backdropZIndex = newZIndex - 10; // Ensure backdrop covers previous modals\n \n Dialog._openDialogs.push(this);\n\n if (this.onShow) this.onShow(e);\n this.emit('show', {\n dialog: this,\n relatedTarget: e.relatedTarget\n });\n });\n\n // shown.bs.modal\n this.element.addEventListener('shown.bs.modal', (e) => {\n // Fix all backdrop stacking after Bootstrap has finished\n setTimeout(() => {\n Dialog.fixAllBackdropStacking();\n }, 50);\n \n if (this.onShown) this.onShown(e);\n this.emit('shown', {\n dialog: this,\n relatedTarget: e.relatedTarget\n });\n\n // Focus first input if exists\n if (this.focus) {\n const firstInput = this.element.querySelector('input:not([type=\"hidden\"]), textarea, select');\n if (firstInput) {\n firstInput.focus();\n }\n }\n });\n\n // hide.bs.modal\n this.element.addEventListener('hide.bs.modal', (e) => {\n // Blur any focused element inside the modal to prevent accessibility warning\n const focusedElement = this.element.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n if (this.onHide) {\n const result = this.onHide(e);\n if (result === false) {\n e.preventDefault();\n return;\n }\n }\n this.emit('hide', { dialog: this });\n });\n\n // hidden.bs.modal\n this.element.addEventListener('hidden.bs.modal', (e) => {\n // Manage stacking\n const index = Dialog._openDialogs.indexOf(this);\n if (index > -1) {\n Dialog._openDialogs.splice(index, 1);\n }\n\n // If there are still modals open, ensure body has modal-open class\n // and properly manage backdrop stacking\n if (Dialog._openDialogs.length > 0) {\n document.body.classList.add('modal-open');\n \n setTimeout(() => { // Let Bootstrap finish its hide animation\n Dialog.fixAllBackdropStacking();\n }, 50);\n }\n\n // Restore focus to the element that had it before modal opened\n if (this.previousFocus && document.body.contains(this.previousFocus)) {\n this.previousFocus.focus();\n }\n\n if (this.onHidden) this.onHidden(e);\n this.emit('hidden', { dialog: this });\n });\n\n // hidePrevented.bs.modal\n this.element.addEventListener('hidePrevented.bs.modal', (e) => {\n if (this.onHidePrevented) this.onHidePrevented(e);\n this.emit('hidePrevented', { dialog: this });\n });\n }\n\n\n\n /**\n * Show the dialog\n * @param {HTMLElement} relatedTarget - Optional element that triggered the modal\n */\n show(relatedTarget = null) {\n // Capture the currently focused element for later restoration\n this.previousFocus = document.activeElement;\n window.lastDialog = this;\n if (this.modal) {\n this.modal.show(relatedTarget);\n }\n }\n\n /**\n * Hide the dialog\n */\n hide() {\n // Blur any focused element inside the modal before hiding\n const focusedElement = this.element?.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n if (this.modal) {\n this.modal.hide();\n }\n }\n\n /**\n * Toggle the dialog\n * @param {HTMLElement} relatedTarget - Optional element that triggered the modal\n */\n toggle(relatedTarget = null) {\n if (this.modal) {\n this.modal.toggle(relatedTarget);\n }\n }\n\n /**\n * Destroy the dialog and clean up resources\n */\n async destroy() {\n // Hide modal if it's showing\n if (this.modal) {\n // Remove focus from any element inside the modal\n const focusedElement = this.element?.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n // Dispose of Bootstrap modal instance\n this.modal.dispose();\n this.modal = null;\n }\n\n // Restore previous focus if available\n if (this.previousFocus && document.body.contains(this.previousFocus)) {\n this.previousFocus.focus();\n this.previousFocus = null;\n }\n\n // Clean up auto-sizing\n if (this.autoSize) {\n this.resetAutoSizing();\n }\n\n // Call parent destroy\n await super.destroy();\n }\n\n /**\n * Handle dynamic height changes\n */\n handleUpdate() {\n if (this.modal) {\n this.modal.handleUpdate();\n }\n }\n\n /**\n * Update dialog content\n * @param {string|View} content - New content (string or View instance)\n */\n async setContent(content) {\n // Handle View instance\n if (content instanceof View) {\n // Clean up old view if exists\n if (this.bodyView) {\n await this.bodyView.destroy();\n this.removeChild(this.bodyView);\n }\n\n this.bodyView = content;\n this.body = '';\n this.addChild(this.bodyView);\n\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n bodyEl.innerHTML = '';\n // Pass container to render - it will handle mounting internally\n await this.bodyView.render(bodyEl);\n }\n } else {\n // String content\n this.body = content;\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n bodyEl.innerHTML = content;\n }\n }\n\n // Update modal position if needed\n this.handleUpdate();\n }\n\n /**\n * Update dialog title\n */\n setTitle(title) {\n this.title = title;\n const titleEl = this.element?.querySelector('.modal-title');\n if (titleEl) {\n titleEl.textContent = title;\n }\n }\n\n /**\n * Set loading state\n */\n setLoading(loading = true, message = 'Loading...') {\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n if (loading) {\n bodyEl.innerHTML = `\n <div class=\"text-center py-4\">\n <div class=\"spinner-border text-primary mb-3\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <p>${message}</p>\n </div>\n `;\n } else if (this.bodyView) {\n bodyEl.replaceChildren(this.bodyView.element);\n }\n }\n }\n\n /**\n * Clean up\n */\n async onBeforeDestroy() {\n // Clean up child views\n if (this.bodyView) {\n await this.bodyView.destroy();\n }\n if (this.footerView) {\n await this.footerView.destroy();\n }\n\n await super.onBeforeDestroy();\n\n if (this.modal) {\n this.modal.dispose();\n this.modal = null;\n }\n }\n\n /**\n * Static method to show code in a dialog\n */\n static async showCode(options = {}) {\n const dialog = new Dialog({\n title: options.title || 'Source Code',\n size: options.size || 'lg',\n scrollable: true,\n body: Dialog.formatCode(options.code, options.language),\n buttons: [\n {\n text: 'Copy to Clipboard',\n class: 'btn-primary',\n icon: 'bi-clipboard',\n action: 'copy'\n },\n {\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n }\n ]\n });\n\n // Handle copy action\n dialog.on('action:copy', async () => {\n if (navigator.clipboard) {\n try {\n await navigator.clipboard.writeText(options.code);\n dialog.showCopySuccess();\n } catch (err) {\n console.error('Failed to copy:', err);\n }\n }\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Apply syntax highlighting after mounting\n if (window.Prism && dialog.element) {\n window.Prism.highlightAllUnder(dialog.element);\n }\n\n // Show the dialog\n dialog.show();\n\n // Clean up when hidden\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n });\n\n return dialog;\n }\n\n /**\n * Format code for display with syntax highlighting support\n */\n static formatCode(code, language = 'javascript') {\n let highlightedCode;\n\n // Check if Prism.js is available and has the language\n if (window.Prism && window.Prism.languages[language]) {\n // Use Prism to highlight the code\n highlightedCode = window.Prism.highlight(code, window.Prism.languages[language], language);\n } else {\n // Fallback: just escape HTML\n highlightedCode = code\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n }\n\n // Add Prism classes for styling even if highlighting wasn't applied\n const prismClass = window.Prism ? `language-${language}` : '';\n\n // Modern VS Code-like dark theme styling\n const codeStyles = `\n max-height: 60vh;\n overflow-y: auto;\n background: #1e1e1e;\n color: #d4d4d4;\n padding: 1.25rem;\n border-radius: 0.5rem;\n margin: 0;\n font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', 'Monaco', monospace;\n font-size: 0.9rem;\n line-height: 1.6;\n border: 1px solid #2d2d30;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);\n `.replace(/\\s+/g, ' ').trim();\n\n return `\n <style>\n /* Custom Prism theme overrides for Dialog */\n .dialog-code-block .token.comment { color: #6a9955; }\n .dialog-code-block .token.string { color: #ce9178; }\n .dialog-code-block .token.keyword { color: #569cd6; }\n .dialog-code-block .token.function { color: #dcdcaa; }\n .dialog-code-block .token.number { color: #b5cea8; }\n .dialog-code-block .token.operator { color: #d4d4d4; }\n .dialog-code-block .token.class-name { color: #4ec9b0; }\n .dialog-code-block .token.punctuation { color: #d4d4d4; }\n .dialog-code-block .token.boolean { color: #569cd6; }\n .dialog-code-block .token.property { color: #9cdcfe; }\n .dialog-code-block .token.tag { color: #569cd6; }\n .dialog-code-block .token.attr-name { color: #9cdcfe; }\n .dialog-code-block .token.attr-value { color: #ce9178; }\n .dialog-code-block ::selection { background: #264f78; }\n </style>\n <pre class=\"${prismClass} dialog-code-block\" style=\"${codeStyles}\">\n <code class=\"${prismClass}\" style=\"color: inherit; background: transparent; text-shadow: none;\">${highlightedCode}</code>\n </pre>\n `;\n }\n\n /**\n * Trigger Prism highlighting on already rendered code blocks\n * Call this after inserting code into the DOM if not using formatCode\n */\n static highlightCodeBlocks(container = document) {\n if (window.Prism && window.Prism.highlightAllUnder) {\n window.Prism.highlightAllUnder(container);\n }\n }\n\n /**\n * Show copy success feedback\n */\n showCopySuccess() {\n const btn = this.element.querySelector('[data-action=\"copy\"]');\n if (btn) {\n const originalHtml = btn.innerHTML;\n btn.innerHTML = '<i class=\"bi bi-check me-1\"></i>Copied!';\n btn.classList.remove('btn-primary');\n btn.classList.add('btn-success');\n btn.disabled = true;\n\n setTimeout(() => {\n btn.innerHTML = originalHtml;\n btn.classList.remove('btn-success');\n btn.classList.add('btn-primary');\n btn.disabled = false;\n }, 2000);\n }\n }\n\n\n\n\n /**\n * Show a dialog with promise-based button handling\n * - If a button has a handler, it will be called. Return semantics:\n * - true or undefined: resolve and close (with button.value || button.action || index)\n * - null or false: keep dialog open (do not resolve)\n * - any other value: resolve with that value and close\n * - If no handler, resolve with action/value/index and close\n * @param {Object} options - Dialog options\n * @returns {Promise} Resolves with value/action/index or null on dismiss\n */\n static async showDialog(options = {}) {\n // Handle legacy signature (message, title, options)\n if (typeof options === 'string') {\n const message = arguments[0];\n const title = arguments[1] || 'Alert';\n const opts = arguments[2] || {};\n options = {\n ...opts,\n body: message,\n title: title\n };\n }\n\n const {\n title = 'Dialog',\n content,\n body = content || '',\n size = 'md',\n centered = true,\n buttons = [\n { text: 'OK', class: 'btn-primary', value: true }\n ],\n rejectOnDismiss = false, // Default to return null on dismissal\n ...dialogOptions\n } = options;\n\n // Create the dialog (preserve original button action/dismiss attributes)\n const dialog = new Dialog({\n title,\n body: body,\n size,\n centered,\n buttons,\n ...dialogOptions\n });\n\n // Render and mount\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Return promise that resolves based on button clicks\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n // Handle button clicks\n const buttonElements = dialog.element.querySelectorAll('.modal-footer button');\n buttonElements.forEach((btnElement, index) => {\n const buttonConfig = buttons[index];\n if (!buttonConfig) return;\n\n btnElement.addEventListener('click', async (e) => {\n if (resolved) return;\n\n const defaultResolveValue = (\n buttonConfig.value !== undefined\n ? buttonConfig.value\n : (buttonConfig.action ?? index)\n );\n\n // If a handler is provided, call it and respect its return semantics\n if (typeof buttonConfig.handler === 'function') {\n try {\n const result = await buttonConfig.handler({\n dialog,\n button: buttonConfig,\n index,\n event: e\n });\n\n // null/false -> keep dialog open\n if (result === null || result === false) {\n return;\n }\n\n // Determine resolve value and close\n const valueToResolve =\n (result === true || result === undefined)\n ? defaultResolveValue\n : result;\n\n resolved = true;\n // Close the dialog (Bootstrap will close if dismiss attribute is present)\n if (!buttonConfig.dismiss) {\n dialog.hide();\n }\n resolve(valueToResolve);\n } catch (err) {\n console.error('Dialog button handler error:', err);\n // Keep dialog open on handler error\n return;\n }\n } else {\n // No handler: resolve with action/value/index and close\n resolved = true;\n if (!buttonConfig.dismiss) {\n dialog.hide();\n }\n resolve(defaultResolveValue);\n }\n });\n });\n\n // Handle backdrop click or ESC key\n dialog.on('hidden', () => {\n // If not already resolved by a button handler, resolve as dismiss\n if (!resolved) {\n resolved = true;\n if (rejectOnDismiss) {\n reject(new Error('Dialog dismissed'));\n } else {\n resolve(null);\n }\n }\n // Always cleanup after hide\n setTimeout(() => {\n dialog.destroy();\n dialog.element.remove();\n }, 100);\n });\n\n // Show the dialog\n dialog.show();\n });\n }\n\n /**\n * Static alert dialog helper\n * @param {Object|string} options - Alert options or message string\n * @returns {Promise} Resolves when OK is clicked\n */\n static async alert(options = {}) {\n // Handle string argument\n if (typeof options === 'string') {\n options = {\n message: options,\n title: 'Alert'\n };\n }\n\n const {\n message = '',\n title = 'Alert',\n type = 'info', // info, success, warning, danger\n ...dialogOptions\n } = options;\n\n // Add icon based on type\n let icon = '';\n let titleClass = '';\n switch(type) {\n case 'success':\n icon = '<i class=\"bi bi-check-circle-fill text-success me-2\"></i>';\n titleClass = 'text-success';\n break;\n case 'warning':\n icon = '<i class=\"bi bi-exclamation-triangle-fill text-warning me-2\"></i>';\n titleClass = 'text-warning';\n break;\n case 'danger':\n case 'error':\n icon = '<i class=\"bi bi-x-circle-fill text-danger me-2\"></i>';\n titleClass = 'text-danger';\n break;\n default:\n icon = '<i class=\"bi bi-info-circle-fill text-info me-2\"></i>';\n titleClass = 'text-info';\n }\n\n return Dialog.showDialog({\n title: `<span class=\"${titleClass}\">${icon}${title}</span>`,\n body: `<p>${message}</p>`,\n size: 'sm',\n centered: true,\n buttons: [\n { text: 'OK', class: 'btn-primary', value: true }\n ],\n ...dialogOptions\n });\n }\n\n /**\n * Static confirm dialog\n */\n static async confirm(message, title = 'Confirm', options = {}) {\n if (typeof message === 'object') {\n options = message;\n message = options.message;\n title = options.title || title;\n }\n const dialog = new Dialog({\n title,\n body: `<p>${message}</p>`,\n size: options.size || 'sm',\n centered: true,\n backdrop: 'static',\n buttons: [\n { text: options.cancelText || 'Cancel', class: 'btn-secondary', dismiss: true, action: 'cancel' },\n { text: options.confirmText || 'Confirm', class: options.confirmClass || 'btn-primary', action: 'confirm' }\n ],\n ...options\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n dialog.show();\n\n return new Promise((resolve) => {\n let result = false;\n\n dialog.on('action:confirm', () => {\n result = true;\n dialog.hide();\n });\n\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n resolve(result);\n });\n });\n }\n\n /**\n * Static prompt dialog\n */\n static async prompt(message, title = 'Input', options = {}) {\n const inputId = `prompt-input-${Date.now()}`;\n const defaultValue = options.defaultValue || '';\n const inputType = options.inputType || 'text';\n const placeholder = options.placeholder || '';\n\n const dialog = new Dialog({\n title,\n body: `\n <p>${message}</p>\n <input type=\"${inputType}\"\n class=\"form-control\"\n id=\"${inputId}\"\n value=\"${defaultValue}\"\n placeholder=\"${placeholder}\">\n `,\n size: options.size || 'sm',\n centered: true,\n backdrop: 'static',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', dismiss: true },\n { text: 'OK', class: 'btn-primary', action: 'ok' }\n ],\n ...options\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n dialog.show();\n\n // Focus the input\n dialog.on('shown', () => {\n const input = dialog.element.querySelector(`#${inputId}`);\n if (input) {\n input.focus();\n input.select();\n }\n });\n\n return new Promise((resolve) => {\n let result = null;\n\n dialog.on('action:ok', () => {\n const input = dialog.element.querySelector(`#${inputId}`);\n result = input ? input.value : null;\n dialog.hide();\n });\n\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n resolve(result);\n });\n });\n }\n\n /**\n * Get Bootstrap modal instance\n */\n getModal() {\n return this.modal;\n }\n\n /**\n * Check if modal is shown\n */\n isShown() {\n return this.element?.classList.contains('show') || false;\n }\n\n /**\n * Show form in a dialog for simple data collection (no model saving)\n * @param {object} options - Configuration options\n * @returns {Promise} Promise that resolves with form data or null if cancelled\n */\n static async showForm(options = {}) {\n const {\n title = 'Form',\n formConfig = {},\n size = 'md',\n centered = true,\n submitText = 'Submit',\n cancelText = 'Cancel',\n ...dialogOptions\n } = options;\n\n // Create the FormView (no model for simple form) - lazy loaded\n const FormView = (await import('@core/forms/FormView.js')).default;\n const formView = new FormView({\n fileHandling: options.fileHandling || 'base64',\n data: options.data,\n defaults: options.defaults,\n model: options.model,\n formConfig: {\n fields: formConfig.fields || options.fields,\n ...formConfig,\n submitButton: false,\n resetButton: false\n }\n });\n\n // Create the dialog with the FormView as body\n const dialog = new Dialog({\n title,\n body: formView,\n size,\n centered,\n buttons: [\n {\n text: cancelText,\n class: 'btn-secondary',\n action: 'cancel'\n },\n {\n text: submitText,\n class: 'btn-primary',\n action: 'submit'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Handle dialog actions\n dialog.on('action:submit', async () => {\n if (resolved) return;\n\n // Validate form\n if (!formView.validate()) {\n formView.focusFirstError();\n return;\n }\n\n if (options.autoSave && options.model) {\n dialog.setLoading(true);\n const result = await formView.saveModel()\n if (!result.success) {\n dialog.setLoading(false);\n dialog.render();\n dialog.getApp().toast.error(result.message);\n return;\n }\n resolved = true;\n dialog.hide();\n resolve(result);\n }\n\n // Get form data and resolve\n try {\n const formData = await formView.getFormData();\n resolved = true;\n dialog.hide();\n resolve(formData);\n } catch (error) {\n console.error('Error collecting form data:', error);\n formView.showError('Error collecting form data');\n }\n });\n\n dialog.on('action:cancel', () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(null);\n });\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(null);\n }\n // Clean up\n setTimeout(() => {\n formView.destroy();\n dialog.destroy();\n }, 100);\n });\n });\n }\n\n /**\n * Show form in a dialog with automatic model saving\n * @param {object} options - Configuration options (requires model)\n * @returns {Promise} Promise that resolves with save result or null if cancelled\n */\n static async showModelForm(options = {}) {\n const {\n title = 'Edit',\n formConfig = {},\n size = 'md',\n centered = true,\n submitText = 'Save',\n cancelText = 'Cancel',\n model,\n fields,\n ...dialogOptions\n } = options;\n\n if (!model) {\n throw new Error('showModelForm requires a model');\n }\n\n // Create the FormView with model - lazy loaded\n const FormView = (await import('@core/forms/FormView.js')).default;\n const formView = new FormView({\n fileHandling: options.fileHandling || 'base64',\n model: model,\n data: options.data,\n defaults: options.defaults,\n formConfig: {\n // Support both formConfig.fields and direct fields parameter\n fields: fields || formConfig.fields || [],\n ...formConfig,\n submitButton: false,\n resetButton: false\n }\n });\n\n // Create the dialog with the FormView as body\n const dialog = new Dialog({\n title,\n body: formView,\n size,\n centered,\n buttons: [\n {\n text: cancelText,\n class: 'btn-secondary',\n action: 'cancel'\n },\n {\n text: submitText,\n class: 'btn-primary',\n action: 'submit'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Handle dialog actions\n dialog.on('action:submit', async () => {\n if (resolved) return;\n\n // Show loading state\n dialog.setLoading(true, 'Saving...');\n\n try {\n const result = await formView.handleSubmit();\n if (result.success) {\n resolved = true;\n dialog.hide();\n resolve(result);\n } else {\n // Restore form and show error\n dialog.setLoading(false);\n let errmsg = result.error;\n if (result.data && result.data.error) {\n errmsg = result.data.error;\n }\n dialog.getApp().toast.error(errmsg);\n // formView.showError(result.error || 'Save failed. Please try again.');\n }\n } catch (error) {\n console.error('Error saving form:', error);\n // Restore form and show error\n await dialog.setContent(formView);\n formView.showError(error.message || 'An error occurred while saving');\n }\n });\n\n dialog.on('action:cancel', () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(null);\n });\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(null);\n }\n // Clean up\n setTimeout(() => {\n formView.destroy();\n dialog.destroy();\n }, 100);\n });\n });\n }\n\n /**\n * Show data in a dialog using DataView component\n * @param {object} options - Configuration options\n * @returns {Promise} Promise that resolves when dialog is closed\n */\n static async showData(options = {}) {\n const {\n title = 'Data View',\n data = {},\n model = null,\n fields = [],\n columns = 2,\n responsive = true,\n showEmptyValues = false,\n emptyValueText = '—',\n size = 'lg',\n centered = true,\n closeText = 'Close',\n ...dialogOptions\n } = options;\n\n\n // Create the DataView - lazy loaded\n const DataView = (await import('@core/views/data/DataView.js')).default;\n const dataView = new DataView({\n data,\n model,\n fields,\n columns,\n responsive,\n showEmptyValues,\n emptyValueText\n });\n\n // Create the dialog with the DataView as body\n const dialog = new Dialog({\n title,\n body: dataView,\n size,\n centered,\n buttons: [\n {\n text: closeText,\n class: 'btn-secondary',\n value: 'close'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog and return promise\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Get close button\n const closeBtn = dialog.element.querySelector('.modal-footer button');\n\n // Handle close\n const handleClose = () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(true);\n };\n\n // Attach event listener\n closeBtn?.addEventListener('click', handleClose);\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(true);\n }\n // Clean up\n setTimeout(() => {\n dataView.destroy();\n dialog.destroy();\n dialog.element.remove();\n }, 100);\n });\n\n // Forward DataView events\n dataView.on('field:click', (data) => {\n dialog.emit('dataview:field:click', data);\n });\n\n dataView.on('error', (data) => {\n dialog.emit('dataview:error', data);\n });\n });\n }\n}\n\nDialog.showConfirm = Dialog.confirm;\nDialog.showError = Dialog.alert;\n\nexport default Dialog;\n"],"names":["Dialog","View","static","backdrop","modal","getFullscreenAwareZIndex","document","querySelector","this","_baseZIndex","fixAllBackdropStacking","backdrops","querySelectorAll","openDialogs","_openDialogs","length","sortedDialogs","sort","a","b","_dialogZIndex","forEach","index","backdropZIndex","style","zIndex","targetContainer","body","parentNode","appendChild","updateAllBackdropStacking","showBusy","options","timeout","message","_busyCounter","_busyTimeout","clearTimeout","_busyIndicator","busyZIndex","createElement","className","innerHTML","msgElement","textContent","setTimeout","classList","add","console","error","hideBusy","alert","title","type","force","remove","constructor","modalId","id","Date","now","super","tagName","fade","attributes","tabindex","labelledBy","describedBy","titleId","size","centered","scrollable","autoSize","keyboard","focus","header","headerContent","closeButton","contextMenu","content","bodyView","bodyClass","noBodyPadding","minWidth","minHeight","maxWidthPercent","maxHeightPercent","_processBodyContent","footer","footerView","footerClass","_processFooterContent","buttons","onShow","onShown","onHide","onHidden","onHidePrevented","autoShow","relatedTarget","render","addChild","result","Promise","bodyPromise","footerPromise","getTemplate","dialogClasses","startsWith","includes","push","join","buildHeader","buildBody","buildFooter","headerActions","items","buildContextMenu","menuItems","filterContextMenuItems","triggerIcon","icon","buttonClass","map","item","label","href","target","action","dataAttrs","Object","keys","filter","key","filteredItems","permissions","app","getApp","user","activeUser","getState","window","globalApp","e","hasPermission","warn","replaceById","buttonsHtml","btn","dismissAttr","dismiss","actionAttr","idAttr","disabledAttr","disabled","class","text","mount","_container","mounted","destroyed","element","Error","onBeforeMount","bindEvents","onAfterMount","emit","view","onAfterRender","Prism","highlightAllUnder","setupAutoSizing","bootstrap","Modal","setAttribute","bindBootstrapEvents","show","addEventListener","applyAutoSizing","once","isShown","modalDialog","modalContent","modalBody","originalStyles","dialogMaxWidth","maxWidth","dialogWidth","width","contentWidth","contentMaxHeight","maxHeight","bodyOverflow","overflowY","offsetHeight","contentRect","getBoundingClientRect","viewportMargin","Math","min","innerWidth","innerHeight","optimalWidth","max","ceil","optimalHeight","height","heightExceedsMax","autoSizedWidth","autoSizedHeight","_originalStyles","resetAutoSizing","stackIndex","newZIndex","_backdropZIndex","dialog","firstInput","focusedElement","blur","preventDefault","indexOf","splice","previousFocus","contains","activeElement","lastDialog","hide","toggle","destroy","dispose","handleUpdate","setContent","removeChild","bodyEl","setTitle","titleEl","setLoading","loading","replaceChildren","onBeforeDestroy","showCode","formatCode","code","language","on","async","navigator","clipboard","writeText","showCopySuccess","err","highlightedCode","languages","highlight","replace","prismClass","trim","highlightCodeBlocks","container","originalHtml","showDialog","arguments","value","rejectOnDismiss","dialogOptions","resolve","reject","resolved","btnElement","buttonConfig","defaultResolveValue","handler","button","event","valueToResolve","titleClass","confirm","cancelText","confirmText","confirmClass","prompt","inputId","defaultValue","inputType","placeholder","input","select","getModal","showForm","formConfig","submitText","formView","FormView","then","require","n","FormView$1","default","fileHandling","data","defaults","model","fields","submitButton","resetButton","validate","autoSave","saveModel","success","toast","formData","getFormData","showError","focusFirstError","showModelForm","handleSubmit","errmsg","showData","columns","responsive","showEmptyValues","emptyValueText","closeText","dataView","DataView","closeBtn","showConfirm"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const t=require("./Dialog-DmIPK_Bi.js"),e=require("./Rest-BNYqGlnP.js"),s=require("./WebSocketClient-DzcqAmho.js");class BaseChart extends e.View{constructor(t={}){super({...t,className:`chart-component ${t.className||""}`,tagName:"div"}),this.chart=null,this.chartType=t.chartType||"line",this.endpoint=t.endpoint||null,this.data=t.data||null,this.dataTransform=t.dataTransform||null,this.refreshInterval=t.refreshInterval||null,this.autoRefresh=!1!==t.autoRefresh,this.refreshTimer=null,this.websocketUrl=t.websocketUrl||null,this.websocket=null,this.websocketReconnect=!1!==t.websocketReconnect,this.width=t.width||null,this.height=t.height||null,this.contentStyle=[this.width?`width: ${this.width}px;`:"",this.height?`height: ${this.height}px;`:""].filter(Boolean).join(" "),void 0===t.maintainAspectRatio&&(t.maintainAspectRatio=!0),this.title=t.title||"",this.chartTitle=t.chartTitle||"",this.chartOptions={responsive:!0,maintainAspectRatio:t.maintainAspectRatio,interaction:{intersect:!1,mode:"index"},plugins:{legend:{display:!1!==t.showLegend,position:t.legendPosition||"top"},title:{display:!!this.chartTitle,text:this.chartTitle},tooltip:{enabled:!1!==t.showTooltips,backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",bodyColor:"#fff",borderColor:"rgba(255,255,255,0.1)",borderWidth:1}},...t.chartOptions},this.xAxis=t.xAxis||null,this.yAxis=t.yAxis||null,this.tooltipFormatters=t.tooltip||{},this.theme=t.theme||"light",this.colorScheme=t.colorScheme||"default",this.animations=!1!==t.animations,this.exportEnabled=!0===t.exportEnabled,this.exportFormats=t.exportFormats||["png","jpg","csv"],this.isLoading=!1,this.hasError=!1,this.lastFetch=null,this.dataPoints=0,this.canvas=null,this.chartJsCdn=t.chartJsCdn||"https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js",this.dataFormatter=e.dataFormatter,this._essentialListeners=[]}get refreshEnabled(){return!(!this.endpoint&&!this.websocketUrl)}buildDefaultHeaderConfig(){return{titleHtml:this.title||"",chartTitle:this.chartTitle||"",showExport:!0===this.exportEnabled,showRefresh:this.refreshEnabled,showTheme:!0,controls:[]}}async getTemplate(){return'\n <div class="chart-container" data-theme="{{theme}}">\n <div class="chart-header mb-3">\n <div data-container="header"></div>\n <div class="chart-header-aux mt-2">\n <div data-container="header-aux"></div>\n </div>\n </div>\n\n <div class="chart-content position-relative" {{#contentStyle}}style="{{contentStyle}}"{{/contentStyle}}>\n <canvas class="chart-canvas" data-container="canvas"></canvas>\n\n \x3c!-- Loading overlay --\x3e\n <div class="chart-overlay d-none" data-loading>\n <div class="d-flex flex-column align-items-center">\n <div class="spinner-border text-primary mb-2" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n <small class="text-muted">Loading chart data...</small>\n </div>\n </div>\n\n \x3c!-- Error overlay --\x3e\n <div class="chart-overlay d-none" data-error>\n <div class="alert alert-danger mb-0" role="alert">\n <div class="d-flex align-items-center">\n <i class="bi bi-exclamation-triangle me-2"></i>\n <div class="flex-grow-1">\n <strong>Error:</strong> <span class="error-message">Failed to load chart data</span>\n </div>\n <button class="btn btn-sm btn-outline-danger ms-2" data-action="retry-load">\n <i class="bi bi-arrow-clockwise"></i> Retry\n </button>\n </div>\n </div>\n </div>\n\n \x3c!-- No data overlay --\x3e\n <div class="chart-overlay d-none" data-no-data>\n <div class="text-center text-muted">\n <i class="bi bi-bar-chart display-4 mb-3 opacity-50"></i>\n <p class="mb-0">No data available</p>\n {{#refreshEnabled}}\n <button class="btn btn-sm btn-outline-secondary mt-2" data-action="refresh-chart">\n <i class="bi bi-arrow-clockwise"></i> Refresh\n </button>\n {{/refreshEnabled}}\n </div>\n </div>\n\n \x3c!-- WebSocket status indicator --\x3e\n <div class="position-absolute top-0 end-0 mt-2 me-2">\n <span class="badge bg-success websocket-status" style="display: none;" data-websocket-status>\n <i class="bi bi-wifi"></i> Live\n </span>\n </div>\n </div>\n\n <div class="chart-footer mt-2" style="display: none;">\n <div class="row">\n <div class="col">\n <small class="text-muted">\n <i class="bi bi-graph-up me-1"></i>\n <span class="data-points">0 data points</span>\n </small>\n </div>\n <div class="col text-end">\n <small class="text-muted refresh-info">\n Auto-refresh: <span class="refresh-status">Off</span>\n </small>\n </div>\n </div>\n </div>\n </div>\n '}async onInit(){await this.initializeChartJS();try{const t=this.headerConfig||(this.buildDefaultHeaderConfig?this.buildDefaultHeaderConfig():null);t&&(this.headerView=new ChartHeaderView({...t,containerId:"header"}),this.addChild(this.headerView))}catch(t){}}async onAfterRender(){this.canvas=this.element.querySelector(".chart-canvas"),this.titleElement=this.element.querySelector(".chart-title"),this.contentElement=this.element.querySelector(".chart-content"),this.footerElement=this.element.querySelector(".chart-footer"),this.loadingOverlay=this.element.querySelector("[data-loading]"),this.errorOverlay=this.element.querySelector("[data-error]"),this.noDataOverlay=this.element.querySelector("[data-no-data]"),this.websocketStatus=this.element.querySelector("[data-websocket-status]"),this.refreshBtn=this.element.querySelector(".refresh-btn"),this.themeToggle=this.element.querySelector(".theme-toggle"),this.applyTheme(),this.endpoint?(await this.fetchData(),await this.updateChart(this.data,!0),(this.height||this.width)&&this._updateChartDimensions()):this.data?(await this.updateChart(this.data,!0),(this.height||this.width)&&this._updateChartDimensions()):this.showNoData(),this.autoRefresh&&this.refreshInterval&&this.endpoint&&this.startAutoRefresh(),this.websocketUrl&&await this.connectWebSocket(),this.setupResizeObserver(),this.showFooter()}async initializeChartJS(){try{return void 0===window.Chart&&await this.loadChartJS(),!0}catch(t){return console.error("Failed to initialize Chart.js:",t),this.showError("Failed to initialize charting library"),!1}}async loadChartJS(){return new Promise((t,e)=>{const s=document.createElement("script");s.src=this.chartJsCdn,s.onload=()=>{t()},s.onerror=()=>{e(new Error("Failed to load Chart.js"))},document.head.appendChild(s)})}async handleActionRefreshChart(){await this.fetchData()}async handleActionRetryLoad(){this.hideError(),await this.fetchData()}async handleActionExportChart(t,e){const s=e.getAttribute("data-format")||"png";this.exportChart(s)}async handleActionToggleTheme(){this.toggleTheme()}async handleActionSetChartType(t,e){const s=e.getAttribute("data-type");s&&this.setChartType&&await this.setChartType(s)}async fetchData(){if(this.endpoint){this.showLoading(),this.setRefreshButtonState(!0);try{const t=await fetch(this.endpoint,{method:"GET",headers:{"Content-Type":"application/json",Accept:"application/json"}});if(!t.ok)throw new Error(`HTTP ${t.status}: ${t.statusText}`);let e=await t.json();this.dataTransform&&"function"==typeof this.dataTransform&&(e=this.dataTransform(e)),this.lastFetch=/* @__PURE__ */new Date,this.data=e,this.updateLastUpdatedTime();const s=this.getApp()?.events;s&&s.emit("chart:data-loaded",{chart:this,data:e,source:"http",endpoint:this.endpoint})}catch(t){console.error("Failed to fetch chart data:",t),this.showError(`Failed to load data: ${t.message}`),this.emit("chart:error",{chart:this,error:t,source:"http",endpoint:this.endpoint})}finally{this.hideLoading(),this.setRefreshButtonState(!1)}}}async connectWebSocket(){if(this.websocketUrl)try{this.websocket=new s.WebSocketClient({url:this.websocketUrl,autoReconnect:this.websocketReconnect,dataTransform:this.dataTransform,eventBus:this.getApp()?.events,debug:!1}),this.websocket.on("connected",()=>{this.showWebSocketStatus(!0)}),this.websocket.on("disconnected",()=>{this.showWebSocketStatus(!1)}),this.websocket.on("data",async t=>{await this.updateChart(t),this.updateLastUpdatedTime(),this.emit("chart:data-updated",{chart:this,data:t,source:"websocket"})}),this.websocket.on("error",t=>{console.error("WebSocket error:",t),this.showWebSocketStatus(!1,"error")}),await this.websocket.connect()}catch(t){console.error("Failed to connect WebSocket:",t),this.showWebSocketStatus(!1,"error")}}async updateChart(t,e=!1){if(!t)return void this.showNoData();if(this.data=t,!this.canvas||void 0===window.Chart)return;this.hideAllOverlays();const s=this.processChartData(t);e&&this.chart&&(this.chart.destroy(),this.chart=null),this.chart?(this.chart.data=s,this.chart.update("none")):await this.createChart(s),this.updateDataStats(s),(this.height||this.width)&&this._updateChartDimensions()}processChartData(t){let e={...t};const s=this.normalizeAxis(this.xAxis);return s&&s.formatter&&e.labels&&(e.labels=e.labels.map(t=>this.dataFormatter.pipe(t,s.formatter))),e}async createChart(t){if(!this.canvas||void 0===window.Chart)throw new Error("Chart.js not loaded or canvas not found");const e={type:this.chartType,data:t,options:this.buildChartOptions()};try{this.chart=new window.Chart(this.canvas,e),this.setupChartEventHandlers()}catch(s){throw console.error("Failed to create chart:",s),s}}buildChartOptions(){const t={...this.chartOptions};(this.width||this.height)&&(t.responsive=!0,t.maintainAspectRatio=!1);const e=this.normalizeAxis(this.xAxis),s=this.normalizeAxis(this.yAxis);return t.scales=t.scales||{},t.scales.x={type:this._detectAxisType(this.data,e,"x"),display:!0,title:{display:!!e.label,text:e.label||""},grid:{display:!0},ticks:{}},e.formatter&&(t.scales.x.ticks.callback=this._createFormatterCallback(e.formatter)),t.scales.y={type:this._detectAxisType(this.data,s,"y"),display:!0,beginAtZero:!1!==s.beginAtZero,title:{display:!!s.label,text:s.label||""},grid:{display:!0},ticks:{}},s.formatter&&(t.scales.y.ticks.callback=this._createFormatterCallback(s.formatter)),this.applyThemeToOptions(t),(this.tooltipFormatters.x||this.tooltipFormatters.y)&&(t.plugins=t.plugins||{},t.plugins.tooltip=t.plugins.tooltip||{},t.plugins.tooltip.callbacks=t.plugins.tooltip.callbacks||{},this.tooltipFormatters.x&&(t.plugins.tooltip.callbacks.title=t=>{const e=t[0]?.label;return e?this.dataFormatter.pipe(e,this.tooltipFormatters.x):e}),this.tooltipFormatters.y&&(t.plugins.tooltip.callbacks.label=t=>{const e=t.raw,s=this.dataFormatter.pipe(e,this.tooltipFormatters.y);return`${t.dataset.label}: ${s}`})),"function"==typeof this.applySubclassChartOptions&&this.applySubclassChartOptions(t),t}_createFormatterCallback(t){return t?e=>{try{return this.dataFormatter.pipe(e,t)}catch(s){return console.warn("Chart formatter error:",s),e}}:null}normalizeAxis(t){if(!t)return{};if("string"==typeof t)return{formatter:t};if("object"==typeof t){const{formatter:e,label:s,type:a,beginAtZero:i,...r}=t;return{formatter:e,label:s,type:a,beginAtZero:i,...r}}return{}}_detectAxisType(t,e,s="x"){if(e&&e.type)return e.type;if(e&&e.formatter){const t=e.formatter.toLowerCase();if(t.includes("date")||t.includes("time"))return"time"}if(t){if("x"===s&&t.labels&&t.labels.length>0){const e=t.labels[0];return"string"!=typeof e||/^\d+\.?\d*$/.test(e.trim())?e instanceof Date||"string"==typeof e&&!isNaN(Date.parse(e))?"time":"linear":"category"}if("y"===s&&t.datasets&&t.datasets.length>0){const e=t.datasets[0];if(e.data&&e.data.length>0){const t=e.data[0];return"number"!=typeof t&&isNaN(parseFloat(t))?"category":"linear"}}}return"x"===s?"category":"linear"}setupChartEventHandlers(){this.chart&&(this.chart.options.onClick=(t,e)=>{if(e.length>0){const t=e[0],s=t.datasetIndex,a=t.index,i=this.chart.data.datasets[s].data[a],r=this.chart.data.labels[a];this.emit("chart:point-clicked",{chart:this,datasetIndex:s,index:a,value:i,label:r,dataset:this.chart.data.datasets[s]})}},this.chart.options.onHover=(t,e)=>{this.canvas.style.cursor=e.length>0?"pointer":"default"})}applyTheme(){this.element.setAttribute("data-theme",this.theme),this.chart&&(this.chart.options=this.buildChartOptions(),this.chart.update("none"))}applyThemeToOptions(t){const e="dark"===this.theme;t.scales&&Object.keys(t.scales).forEach(s=>{const a=t.scales[s];a.grid=a.grid||{},a.ticks=a.ticks||{},a.grid.color=e?"rgba(255,255,255,0.1)":"rgba(0,0,0,0.1)",a.ticks.color=e?"#e9ecef":"#495057"}),t.plugins?.legend&&(t.plugins.legend.labels=t.plugins.legend.labels||{},t.plugins.legend.labels.color=e?"#e9ecef":"#495057"),t.plugins?.title&&(t.plugins.title.color=e?"#ffffff":"#212529")}toggleTheme(){this.theme="light"===this.theme?"dark":"light",this.applyTheme(),this.emit("chart:theme-changed",{chart:this,theme:this.theme})}startAutoRefresh(){this.endpoint&&this.refreshInterval&&(this.stopAutoRefresh(),this.refreshTimer=setInterval(()=>{this.fetchData()},this.refreshInterval),this.updateRefreshStatus(!0))}stopAutoRefresh(){this.refreshTimer&&(clearInterval(this.refreshTimer),this.refreshTimer=null),this.updateRefreshStatus(!1)}exportChart(t="png"){if(this.chart)try{if("csv"===t)this.exportCSV();else{const e=this.chart.toBase64Image("image/"+t,1),s=document.createElement("a");s.download=`chart-${Date.now()}.${t}`,s.href=e,s.click(),this.emit("chart:exported",{chart:this,format:t,filename:s.download})}}catch(e){console.error("Failed to export chart:",e),this.showError("Failed to export chart")}}exportCSV(){if(this.chart&&this.chart.data)try{const t=this.generateCSV(),e=new Blob([t],{type:"text/csv;charset=utf-8;"}),s=URL.createObjectURL(e),a=document.createElement("a");a.download=`chart-data-${Date.now()}.csv`,a.href=s,a.click(),URL.revokeObjectURL(s),this.emit("chart:exported",{chart:this,format:"csv",filename:a.download})}catch(t){console.error("Failed to export CSV:",t),this.showError("Failed to export CSV")}}generateCSV(){const t=this.chart.data,e=t.labels||[],s=t.datasets||[];let a="Label";return s.forEach(t=>{a+=","+(t.label||"Data")}),a+="\n",e.forEach((t,e)=>{a+=`"${t}"`,s.forEach(t=>{const s=t.data[e]||"";a+=","+s}),a+="\n"}),a}showLoading(){this.isLoading=!0,this.hideAllOverlays(),this.loadingOverlay?.classList.remove("d-none")}hideLoading(){this.isLoading=!1,this.loadingOverlay?.classList.add("d-none")}showError(t){this.hasError=!0,this.hideAllOverlays();const e=this.errorOverlay?.querySelector(".error-message");e&&(e.textContent=t),this.errorOverlay?.classList.remove("d-none")}hideError(){this.hasError=!1,this.errorOverlay?.classList.add("d-none")}showNoData(){this.hideAllOverlays(),this.noDataOverlay?.classList.remove("d-none")}hideAllOverlays(){this.loadingOverlay?.classList.add("d-none"),this.errorOverlay?.classList.add("d-none"),this.noDataOverlay?.classList.add("d-none")}showWebSocketStatus(t,e="connected"){this.websocketStatus&&(t?(this.websocketStatus.className="badge bg-success",this.websocketStatus.innerHTML='<i class="bi bi-wifi"></i> Live'):(this.websocketStatus.className="error"===e?"badge bg-danger":"badge bg-secondary",this.websocketStatus.innerHTML="error"===e?'<i class="bi bi-wifi-off"></i> Error':'<i class="bi bi-wifi-off"></i> Offline'),this.websocketStatus.style.display="inline-block")}setRefreshButtonState(t){if(!this.refreshBtn)return;const e=this.refreshBtn.querySelector("i");t?(this.refreshBtn.disabled=!0,e?.classList.add("spin")):(this.refreshBtn.disabled=!1,e?.classList.remove("spin"))}updateLastUpdatedTime(){const t=this.element.querySelector(".last-updated"),e=this.element.querySelector(".timestamp");t&&e&&(e.textContent=/* @__PURE__ */(new Date).toLocaleTimeString(),t.style.display="block")}updateRefreshStatus(t){const e=this.element.querySelector(".refresh-status");e&&(e.textContent=t?`Every ${this.refreshInterval/1e3}s`:"Off")}updateDataStats(t){let e=0;t.datasets&&(e=t.datasets.reduce((t,e)=>t+(e.data?e.data.length:0),0)),this.dataPoints=e;const s=this.element.querySelector(".data-points");s&&(s.textContent=`${e} data point${1!==e?"s":""}`)}showFooter(){this.footerElement&&(this.footerElement.style.display="block")}setupResizeObserver(){if(!window.ResizeObserver||!this.contentElement)return;const t=new ResizeObserver(()=>{this.chart&&this.chart.resize()});t.observe(this.contentElement),this._resizeObserver=t}async onBeforeDestroy(){this.stopAutoRefresh(),this.websocket&&(this.websocket.disconnect(),this.websocket=null),this.chart&&(this.chart.destroy(),this.chart=null),this._resizeObserver&&(this._resizeObserver.disconnect(),this._resizeObserver=null),this._essentialListeners&&(this._essentialListeners.forEach(({el:t,type:e,fn:s})=>{t&&t.removeEventListener(e,s)}),this._essentialListeners=[]),this.emit("chart:destroyed",{chart:this})}setData(t){return this.data=t,this.updateChart(t)}setEndpoint(t){if(this.endpoint=t,t)return this.fetchData()}setWebSocketUrl(t){if(this.websocket&&this.websocket.disconnect(),this.websocketUrl=t,t)return this.connectWebSocket()}setWidth(t){this.width=t,this.contentStyle=[this.width?`width: ${this.width}px;`:"",this.height?`height: ${this.height}px;`:""].filter(Boolean).join(" "),this.contentElement&&this._updateChartDimensions()}setHeight(t){this.height=t,this.contentStyle=[this.width?`width: ${this.width}px;`:"",this.height?`height: ${this.height}px;`:""].filter(Boolean).join(" "),this.contentElement&&this._updateChartDimensions()}setDimensions(t,e){this.width=t,this.height=e,this.contentStyle=[this.width?`width: ${this.width}px;`:"",this.height?`height: ${this.height}px;`:""].filter(Boolean).join(" "),this.contentElement&&this._updateChartDimensions()}_updateChartDimensions(){this.chart&&(this.width||this.height?(this.chart.options.responsive=!0,this.chart.options.maintainAspectRatio=!1,this.width&&this.contentElement&&(this.contentElement.style.width=this.width?this.width+"px":""),this.height&&this.contentElement&&(this.contentElement.style.height=this.height?this.height+"px":"")):(this.chart.options.responsive=!0,this.chart.options.maintainAspectRatio=this.chartOptions.maintainAspectRatio),this.chart.resize())}resize(){this.chart&&this.chart.resize()}refresh(){return this.fetchData()}export(t="png"){return this.exportChart(t)}setTheme(t){this.theme=t,this.applyTheme()}getStats(){return{isLoading:this.isLoading,hasError:this.hasError,dataPoints:this.dataPoints,lastFetch:this.lastFetch,theme:this.theme,chartType:this.chartType,autoRefresh:!!this.refreshTimer,websocketConnected:this.websocket?.isConnected||!1}}}class ChartHeaderView extends e.View{constructor(t={}){super({...t,className:`mojo-chart-header ${t.className||""}`,tagName:"div"}),this.titleHtml=t.titleHtml||"",this.chartTitle=t.chartTitle||"",this.showExport=!0===t.showExport,this.showRefresh=!!t.showRefresh,this.showTheme=!1,this.showTheme=!0===t.showTheme,this.controls=Array.isArray(t.controls)?t.controls:[],this.controlsHtml=this._buildControlsHtml(this.controls)}async getTemplate(){return'\n <div class="d-flex justify-content-between align-items-center">\n <div class="chart-title-section">\n <h5 class="mb-2 chart-title">{{{titleHtml}}}</h5>\n <small class="text-muted last-updated" style="display: none;">\n Last updated: <span class="timestamp"></span>\n </small>\n </div>\n\n <div class="chart-controls">\n <div class="btn-toolbar" role="toolbar">\n {{{controlsHtml}}}\n\n <div class="btn-group btn-group-sm" role="group">\n\n {{#showTheme}}\n <button type="button" class="btn btn-outline-secondary theme-toggle" data-action="toggle-theme" title="Toggle Theme">\n <i class="bi bi-palette"></i>\n </button>\n {{/showTheme}}\n\n {{#showExport}}\n <div class="btn-group btn-group-sm" role="group">\n <button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" title="Export Chart">\n <i class="bi bi-download"></i>\n </button>\n <ul class="dropdown-menu">\n <li><a class="dropdown-item" href="#" data-action="export-chart" data-format="png">\n <i class="bi bi-image"></i> PNG\n </a></li>\n <li><a class="dropdown-item" href="#" data-action="export-chart" data-format="jpg">\n <i class="bi bi-image"></i> JPEG\n </a></li>\n <li><a class="dropdown-item" href="#" data-action="export-chart" data-format="csv">\n <i class="bi bi-file-earmark-spreadsheet"></i> CSV\n </a></li>\n </ul>\n </div>\n {{/showExport}}\n\n {{#showRefresh}}\n <button type="button" class="btn btn-outline-secondary refresh-btn" data-action="refresh-chart" title="Refresh Data">\n <i class="bi bi-arrow-clockwise"></i>\n </button>\n {{/showRefresh}}\n </div>\n </div>\n </div>\n </div>\n '}_buildControlsHtml(t){if(!Array.isArray(t)||0===t.length)return"";const e=[];return t.forEach(t=>{if(t&&t.type)switch(t.type){case"select":{const s=`form-select${"md"===t.size?"":" form-select-sm"} ${t.className||""}`.trim(),a=(t.options||[]).map(t=>`<option value="${this._escapeAttr(t.value)}"${t.selected?" selected":""}>${this._escapeHtml(t.label)}</option>`).join("");e.push(`\n <div class="btn-group btn-group-sm me-2" role="group">\n <select class="${s}" data-change-action="${this._escapeAttr(t.action||t.name||"select-changed")}" style="width: auto;">\n ${a}\n </select>\n </div>\n `);break}case"button":{const{variant:s="outline-secondary",size:a="sm"}=t,i=`btn btn-${s}${"md"===a?"":" btn-sm"} ${t.className||""}`.trim(),r=t.title?` title="${this._escapeAttr(t.title)}"`:"",h=this._buildDataAttrs(t.data);e.push(`\n <div class="btn-group btn-group-sm me-2" role="group">\n <button type="button" class="${i}" data-action="${this._escapeAttr(t.action||"button-action")}"${r}${h}>\n ${t.labelHtml||""}\n </button>\n </div>\n `);break}case"buttongroup":{const s=t.size||"sm",a=`btn-group btn-group-${s} me-2 ${t.className||""}`.trim(),i=(t.buttons||[]).map(t=>{const e=`btn btn-${t.variant||"outline-secondary"}${"md"===s?"":" btn-sm"} ${t.className||""}`.trim(),a=t.title?` title="${this._escapeAttr(t.title)}"`:"",i=this._buildDataAttrs(t.data);return`<button type="button" class="${e}" data-action="${this._escapeAttr(t.action||"button-action")}"${a}${i}>${t.labelHtml||""}</button>`}).join("");e.push(`\n <div class="${a}" role="group">\n ${i}\n </div>\n `);break}case"divider":e.push('<div class="vr mx-2"></div>');break;case"html":{const s=t.html||"";e.push(`<div class="me-2 d-inline-block">${s}</div>`);break}}}),e.join("\n")}_buildDataAttrs(t){return t&&"object"==typeof t?Object.entries(t).map(([t,e])=>` data-${this._kebabCase(String(t))}="${this._escapeAttr(String(e))}"`).join(""):""}_kebabCase(t){return t.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,"$1-$2").toLowerCase().replace(/[^a-z0-9\-]/g,"-").replace(/--+/g,"-").replace(/^-|-$/g,"")}_escapeAttr(t){return String(t).replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}_escapeHtml(t){return String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}}class SeriesChart extends BaseChart{constructor(t={}){super({...t,chartType:t.chartType||"line"}),this.showTypeSwitch=!0,void 0!==t.showTypeSwitch&&(this.showTypeSwitch=t.showTypeSwitch),this.orientation=t.orientation||"vertical",this.stacked=t.stacked||!1,this.stepped=t.stepped||!1,this.tension=t.tension||.4,this.fill=t.fill||!1,this.showRefreshButton=!1!==t.showRefreshButton,this.headerConfig||(this.headerConfig={titleHtml:this.title||"",chartTitle:this.chartTitle||"",showExport:this.exportEnabled,showRefresh:this.refreshEnabled,showTheme:!0,controls:[]}),this.series=t.series||[],this.xField=t.xField||"x",this.yField=t.yField||"y",this.colors=t.colors||["rgba(54, 162, 235, 0.8)","rgba(255, 99, 132, 0.8)","rgba(75, 192, 192, 0.8)","rgba(255, 206, 86, 0.8)","rgba(153, 102, 255, 0.8)","rgba(255, 159, 64, 0.8)","rgba(199, 199, 199, 0.8)","rgba(83, 102, 255, 0.8)"],this.tooltipFormatters=t.tooltip||{}}async getTemplate(){return await super.getTemplate()}async onInit(){this.showTypeSwitch&&this.headerConfig.controls.push({type:"buttongroup",size:"sm",buttons:[{action:"set-chart-type",labelHtml:'<i class="bi bi-graph-up"></i>',title:"Line",variant:"line"===this.chartType?"primary":"outline-primary",data:{type:"line"}},{action:"set-chart-type",labelHtml:'<i class="bi bi-bar-chart"></i>',title:"Bar",variant:"bar"===this.chartType?"primary":"outline-primary",data:{type:"bar"}}]}),await super.onInit()}async onActionSetChartType(t,e){t.stopPropagation();const s=e.getAttribute("data-type");s&&s!==this.chartType&&await this.setChartType(s)}async rebuildChart(){if(this.chart&&this.data){this.chart.destroy(),this.chart=null;const t=this.processChartData(this.data);await this.createChart(t)}}async setChartType(t){if(!["line","bar"].includes(t))throw new Error(`Unsupported chart type: ${t}`);const e=this.chartType;if(this.chartType=t,this.chart&&this.data){this.chart.destroy(),this.chart=null;const t=this.processChartData(this.data);await this.createChart(t)}this._updateTypeSwitcherButtons();const s=this.getApp()?.events;s&&s.emit("chart:type-changed",{chart:this,oldType:e,newType:this.chartType})}processChartData(t){if(!t)return t;let e;return e=Array.isArray(t)?this.processArrayData(t):t.labels&&t.datasets?this.processChartJSData(t):t.series?this.processSeriesData(t):t,this.applyFormattersToData(e)}processArrayData(t){const e=[],s=[];return t.forEach(t=>{const a=t[this.xField],i=t[this.yField];e.push(a),s.push(i)}),{labels:e,datasets:[{label:this.title||"Data",data:s,backgroundColor:this.colors[0].replace("0.8","0.6"),borderColor:this.colors[0],borderWidth:2,tension:"line"===this.chartType?this.tension:0,fill:"line"===this.chartType&&this.fill,stepped:"line"===this.chartType&&this.stepped}]}}processChartJSData(t){const e={...t};return e.datasets=e.datasets.map((t,e)=>({...t,backgroundColor:t.backgroundColor||this.colors[e%this.colors.length].replace("0.8","0.6"),borderColor:t.borderColor||this.colors[e%this.colors.length],borderWidth:t.borderWidth||2,tension:"line"===this.chartType?t.tension??this.tension:0,fill:"line"===this.chartType&&(t.fill??this.fill),stepped:"line"===this.chartType&&(t.stepped??this.stepped)})),e}processSeriesData(t){const e=t.labels||[],s=[];return t.series.forEach((t,e)=>{s.push({label:t.name||t.label||`Series ${e+1}`,data:t.data||[],backgroundColor:t.backgroundColor||this.colors[e%this.colors.length].replace("0.8","0.6"),borderColor:t.borderColor||this.colors[e%this.colors.length],borderWidth:t.borderWidth||2,tension:"line"===this.chartType?t.tension??this.tension:0,fill:"line"===this.chartType&&(t.fill??this.fill),stepped:"line"===this.chartType&&(t.stepped??this.stepped)})}),{labels:e,datasets:s}}applyFormattersToData(t){if(!t)return t;const e={...t},s=this.normalizeAxis?this.normalizeAxis(this.xAxis):{};return s.formatter&&e.labels&&(e.labels=e.labels.map(t=>this.dataFormatter.pipe(t,s.formatter))),e}applySubclassChartOptions(t){this.stacked&&"bar"===this.chartType&&t.scales&&(t.scales.x&&(t.scales.x.stacked=!0),t.scales.y&&(t.scales.y.stacked=!0)),"bar"===this.chartType&&"horizontal"===this.orientation&&(t.indexAxis="y"),t.interaction=t.interaction||{},t.interaction.intersect=!1,t.interaction.mode="line"===this.chartType?"index":"nearest",t.elements=t.elements||{},t.elements.line={...t.elements.line||{},tension:this.tension,borderWidth:2},t.elements.point={...t.elements.point||{},radius:"line"===this.chartType?4:0,hoverRadius:6,hitRadius:8},t.elements.bar={...t.elements.bar||{},borderWidth:1,borderSkipped:!1}}processAxisConfig(t){return t?"string"==typeof t?{formatter:t}:"object"==typeof t?{formatter:t.formatter,label:t.label,type:t.type,beginAtZero:t.beginAtZero,...t}:{}:{}}_updateTypeSwitcherButtons(){const t=this.element?.querySelectorAll('[data-action="set-chart-type"]');t&&0!==t.length&&t.forEach(t=>{const e=t.getAttribute("data-type")===this.chartType;t.classList.toggle("btn-primary",e),t.classList.toggle("btn-outline-primary",!e),t.classList.toggle("active",e)})}setOrientation(t){if(!["vertical","horizontal"].includes(t))throw new Error(`Invalid orientation: ${t}`);if(this.orientation=t,this.chart&&(this.chart.destroy(),this.chart=null,this.data)){const t=this.processChartData(this.data);this.createChart(t)}}setStacked(t){this.stacked=t,this.chart&&(this.chart.options.scales.x.stacked=t,this.chart.options.scales.y.stacked=t,this.chart.update())}addSeries(t){if(!this.data||!this.data.datasets)return;const e={label:t.label||t.name||`Series ${this.data.datasets.length+1}`,data:t.data||[],backgroundColor:t.backgroundColor||this.colors[this.data.datasets.length%this.colors.length].replace("0.8","0.6"),borderColor:t.borderColor||this.colors[this.data.datasets.length%this.colors.length],borderWidth:t.borderWidth||2,tension:"line"===this.chartType?t.tension??this.tension:0,fill:"line"===this.chartType&&(t.fill??this.fill)};this.data.datasets.push(e),this.chart&&(this.chart.data.datasets.push(e),this.chart.update());const s=this.getApp()?.events;s&&s.emit("chart:series-added",{chart:this,series:e})}removeSeries(t){if(!this.data||!this.data.datasets||t<0||t>=this.data.datasets.length)return;const e=this.data.datasets.splice(t,1)[0];this.chart&&(this.chart.data.datasets.splice(t,1),this.chart.update());const s=this.getApp()?.events;s&&s.emit("chart:series-removed",{chart:this,series:e,index:t})}static async showDialog(e={}){const{title:s="Chart Viewer",size:a="xl",...i}=e,r=new SeriesChart({...i,title:s}),h=new t.default({title:s,body:r,size:a,centered:!0,backdrop:"static",keyboard:!0,buttons:[{text:"Export PNG",action:"export",class:"btn btn-outline-primary"},{text:"Close",action:"close",class:"btn btn-secondary",dismiss:!0}]});return await h.render(),document.body.appendChild(h.element),await h.mount(),h.show(),new Promise(t=>{h.on("hidden",()=>{h.destroy(),t(r)}),h.on("action:export",()=>{r.exportChart("png")}),h.on("action:close",()=>{h.hide()})})}}class PieChart extends BaseChart{constructor(t={}){super({...t,chartType:"pie"}),this.cutout=t.cutout||0,this.rotation=t.rotation||0,this.circumference=t.circumference||360,this.borderWidth=t.borderWidth||2,this.borderColor=t.borderColor||"#ffffff",this.hoverBorderWidth=t.hoverBorderWidth||3,this.showLabels=!1!==t.showLabels,this.labelPosition=t.labelPosition||"outside",this.labelFormatter=t.labelFormatter||null,this.valueFormatter=t.valueFormatter||null,this.labelField=t.labelField||"label",this.valueField=t.valueField||"value",this.colors=t.colors||["#FF6384","#36A2EB","#FFCE56","#4BC0C0","#9966FF","#FF9F40","#C9CBCF","#4BC0C0","#FF6384","#36A2EB"],this.animateRotate=!1!==t.animateRotate,this.animateScale=t.animateScale||!1,this.clickable=!1!==t.clickable,this.hoverable=!1!==t.hoverable,this.selectedSegment=null,this.highlightedSegments=/* @__PURE__ */new Set,this.valueFormatter=t.valueFormatter||null}processChartData(t){if(!t)return t;let e;return e=Array.isArray(t)?this.processArrayData(t):t.labels&&t.datasets?this.processChartJSData(t):"object"!=typeof t||t.labels?t:this.processObjectData(t),this.applyFormattersToData(e)}processArrayData(t){const e=[],s=[];return t.forEach(t=>{const a=t[this.labelField],i=t[this.valueField];void 0!==a&&void 0!==i&&(e.push(a),s.push(i))}),{labels:e,datasets:[{data:s,backgroundColor:this.generateColors(e.length),borderColor:this.borderColor,borderWidth:this.borderWidth,hoverBorderWidth:this.hoverBorderWidth}]}}processChartJSData(t){const e={...t};return e.datasets=e.datasets.map(t=>({...t,backgroundColor:t.backgroundColor||this.generateColors(e.labels.length),borderColor:t.borderColor||this.borderColor,borderWidth:t.borderWidth||this.borderWidth,hoverBorderWidth:t.hoverBorderWidth||this.hoverBorderWidth})),e}processObjectData(t){const e=Object.keys(t);return{labels:e,datasets:[{data:Object.values(t),backgroundColor:this.generateColors(e.length),borderColor:this.borderColor,borderWidth:this.borderWidth,hoverBorderWidth:this.hoverBorderWidth}]}}applyFormattersToData(t){if(!t)return t;const e={...t};return this.labelFormatter&&e.labels&&(e.labels=e.labels.map(t=>this.dataFormatter.pipe(t,this.labelFormatter))),e}generateColors(t){const e=[];for(let s=0;s<t;s++)e.push(this.colors[s%this.colors.length]);return e}buildChartOptions(){const t=super.buildChartOptions();return t.cutout=this.cutout,t.rotation=this.rotation,t.circumference=this.circumference,t.animation={animateRotate:this.animateRotate,animateScale:this.animateScale,duration:this.animations?1e3:0},t.plugins={...t.plugins,legend:{...t.plugins.legend,position:t.plugins.legend.position||"right",labels:{...t.plugins.legend.labels,usePointStyle:!0,padding:20,generateLabels:t=>{const e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((t,s)=>{const a=e.datasets[0],i=a.data[s],r=a.backgroundColor[s];return{text:`${t} (${(i/a.data.reduce((t,e)=>t+e,0)*100).toFixed(1)}%)`,fillStyle:r,strokeStyle:r,lineWidth:0,hidden:!1,index:s}}):[]}}},tooltip:{...t.plugins.tooltip,callbacks:{...t.plugins.tooltip.callbacks,label:t=>{const e=t.label||"",s=t.raw,a=(s/t.dataset.data.reduce((t,e)=>t+e,0)*100).toFixed(1);let i=s;return this.valueFormatter?i=this.dataFormatter.pipe(s,this.valueFormatter):this.tooltipFormatters&&this.tooltipFormatters.y&&(i=this.dataFormatter.pipe(s,this.tooltipFormatters.y)),`${e}: ${i} (${a}%)`}}}},delete t.scales,t}setupChartEventHandlers(){super.setupChartEventHandlers(),this.chart&&this.clickable&&(this.chart.options.onClick=(t,e)=>{if(e.length>0){const t=e[0].index,s=this.chart.data.datasets[0],a=this.chart.data.labels[t],i=s.data[t],r=(i/s.data.reduce((t,e)=>t+e,0)*100).toFixed(1);this.toggleSegmentSelection(t);const h=this.getApp()?.events;h&&h.emit("chart:segment-clicked",{chart:this,index:t,label:a,value:i,percentage:parseFloat(r),isSelected:this.selectedSegment===t})}},this.hoverable&&(this.chart.options.onHover=(t,e)=>{if(this.canvas.style.cursor=e.length>0?"pointer":"default",e.length>0){const t=e[0].index,s=this.getApp()?.events;s&&s.emit("chart:segment-hover",{chart:this,index:t,label:this.chart.data.labels[t],value:this.chart.data.datasets[0].data[t]})}}))}toggleSegmentSelection(t){this.selectedSegment===t?(this.selectedSegment=null,this.resetSegmentStyle(t)):(null!==this.selectedSegment&&this.resetSegmentStyle(this.selectedSegment),this.selectedSegment=t,this.highlightSegment(t))}highlightSegment(t){if(!this.chart)return;const e=this.chart.getDatasetMeta(0).data[t];e&&(e.outerRadius+=10,this.chart.update("none"))}resetSegmentStyle(t){if(!this.chart)return;const e=this.chart.getDatasetMeta(0).data[t];e&&(e.outerRadius-=10,this.chart.update("none"))}highlightSegments(t){Array.isArray(t)||(t=[t]),this.highlightedSegments.clear(),t.forEach(t=>{this.highlightedSegments.add(t),this.highlightSegment(t)})}clearHighlights(){this.highlightedSegments.forEach(t=>{this.resetSegmentStyle(t)}),this.highlightedSegments.clear(),null!==this.selectedSegment&&(this.resetSegmentStyle(this.selectedSegment),this.selectedSegment=null)}selectSegment(t){t>=0&&t<this.chart?.data?.labels?.length&&this.toggleSegmentSelection(t)}getSegmentData(t){if(!this.chart||!this.chart.data)return null;const e=this.chart.data.datasets[0],s=this.chart.data.labels[t],a=e.data[t],i=(a/e.data.reduce((t,e)=>t+e,0)*100).toFixed(1);return{index:t,label:s,value:a,percentage:parseFloat(i),color:e.backgroundColor[t],isSelected:this.selectedSegment===t}}getAllSegments(){return this.chart&&this.chart.data?this.chart.data.labels.map((t,e)=>this.getSegmentData(e)):[]}updateSegmentColor(t,e){if(!this.chart||!this.chart.data.datasets[0])return;this.chart.data.datasets[0].backgroundColor[t]=e,this.chart.update("none");const s=this.getApp()?.events;s&&s.emit("chart:segment-color-changed",{chart:this,index:t,color:e,segment:this.getSegmentData(t)})}addSegment(t,e,s=null){if(!this.chart||!this.chart.data)return;const a=this.chart.data.datasets[0],i=s||this.colors[this.chart.data.labels.length%this.colors.length];this.chart.data.labels.push(t),a.data.push(e),a.backgroundColor.push(i),this.chart.update();const r=this.getApp()?.events;r&&r.emit("chart:segment-added",{chart:this,label:t,value:e,color:i,index:this.chart.data.labels.length-1})}removeSegment(t){if(!this.chart||!this.chart.data||t<0||t>=this.chart.data.labels.length)return;const e=this.chart.data.datasets[0],s=this.chart.data.labels[t],a=e.data[t];this.chart.data.labels.splice(t,1),e.data.splice(t,1),e.backgroundColor.splice(t,1),this.selectedSegment===t?this.selectedSegment=null:this.selectedSegment>t&&this.selectedSegment--,this.chart.update();const i=this.getApp()?.events;i&&i.emit("chart:segment-removed",{chart:this,label:s,value:a,index:t,removedSegment:{label:s,value:a,index:t}})}applyThemeToOptions(t){super.applyThemeToOptions(t);const e="dark"===this.theme;this.borderColor=e?"#404449":"#ffffff"}static async showDialog(e={}){const{title:s="Pie Chart",size:a="lg",...i}=e,r=new PieChart({...i,title:s}),h=new t.default({title:s,body:r,size:a,centered:!0,backdrop:"static",keyboard:!0,buttons:[{text:"Export PNG",action:"export",class:"btn btn-outline-primary"},{text:"Close",action:"close",class:"btn btn-secondary",dismiss:!0}]});return await h.render(),document.body.appendChild(h.element),await h.mount(),h.show(),new Promise(t=>{h.on("hidden",()=>{h.destroy(),t(r)}),h.on("action:export",()=>{r.exportChart("png")}),h.on("action:close",()=>{h.hide()})})}}class MiniChart extends e.View{constructor(t={}){super({className:"mini-chart",...t}),this.chartType=t.chartType||"line",this.data=t.data||[],this.width=t.width||"100%",this.height=t.height||30,this.maintainAspectRatio=t.maintainAspectRatio||!1,this.color=t.color||"rgba(54, 162, 235, 1)",this.fillColor=t.fillColor||"rgba(54, 162, 235, 0.1)",this.strokeWidth=t.strokeWidth||2,this.barGap=t.barGap||2,this.fill=!1!==t.fill,this.smoothing=t.smoothing||.3,this.padding=t.padding||2,this.minValue=t.minValue,this.maxValue=t.maxValue,this.showDots=t.showDots||!1,this.dotRadius=t.dotRadius||2,this.animate=!1!==t.animate,this.animationDuration=t.animationDuration||300,this.showTooltip=!1!==t.showTooltip,this.tooltipFormatter=t.tooltipFormatter||null,this.tooltipTemplate=t.tooltipTemplate||null,this.valueFormat=t.valueFormat||null,this.labelFormat=t.labelFormat||null,this.showCrosshair=!1!==t.showCrosshair,this.crosshairColor=t.crosshairColor||"rgba(0, 0, 0, 0.2)",this.crosshairWidth=t.crosshairWidth||1,this.showXAxis=t.showXAxis||!1,this.xAxisColor=t.xAxisColor||this.color,this.xAxisWidth=t.xAxisWidth||1,this.xAxisDashed=!1!==t.xAxisDashed,this.tooltip=null,this.crosshair=null,this.hoveredIndex=-1,this.dataFormatter=e.dataFormatter,this.labels=t.labels||null}getTemplate(){const t="number"==typeof this.width?`${this.width}px`:this.width,e="number"==typeof this.height?`${this.height}px`:this.height,s=this.maintainAspectRatio?"xMidYMid meet":"none";return`\n <div class="mini-chart-wrapper" style="position: relative; display: block; width: ${t}; height: ${e};">\n <svg\n class="mini-chart-svg"\n width="100%"\n height="100%"\n viewBox="0 0 100 ${this.height}"\n preserveAspectRatio="${s}"\n style="display: block;">\n </svg>\n ${this.showTooltip?'<div class="mini-chart-tooltip" style="display: none;"></div>':""}\n </div>\n `}async onAfterRender(){this.svg=this.element.querySelector(".mini-chart-svg"),this.tooltip=this.element.querySelector(".mini-chart-tooltip"),this.updateDimensions(),this.data&&this.data.length>0&&this.renderChart(),this.showTooltip&&this.svg&&this.setupTooltip(),this.setupResizeObserver()}updateDimensions(){if(!this.svg)return;const t=this.svg.getBoundingClientRect();this.actualWidth=t.width||100,this.actualHeight=t.height||this.height,this.svg.setAttribute("viewBox",`0 0 ${this.actualWidth} ${this.actualHeight}`)}setupResizeObserver(){"undefined"!=typeof ResizeObserver&&(this.resizeObserver=new ResizeObserver(()=>{this.updateDimensions(),this.data&&this.data.length>0&&this.renderChart()}),this.svg&&this.resizeObserver.observe(this.svg))}renderChart(){if(!this.svg||!this.data||0===this.data.length)return;this.svg.innerHTML="";const{min:t,max:e}=this.calculateBounds();if(this.showXAxis&&this.renderXAxis(t,e),"line"===this.chartType?this.renderLine(t,e):"bar"===this.chartType&&this.renderBar(t,e),this.showCrosshair){const t=this.getActualHeight();this.crosshair=this.createSVGElement("line",{x1:0,y1:0,x2:0,y2:t,stroke:this.crosshairColor,"stroke-width":this.crosshairWidth,"stroke-dasharray":"3,3",style:"display: none; pointer-events: none;"}),this.svg.appendChild(this.crosshair)}this.showTooltip&&this.tooltip&&this.setupTooltip(),this.animate&&this.applyAnimation()}renderXAxis(t,e){const s=this.getActualWidth(),a=this.getActualHeight();let i;if(t<=0&&e>=0){const s=e-t,r=(a-2*this.padding)/s;i=a-this.padding-(0-t)*r}else i=a-this.padding;const r=this.createSVGElement("line",{x1:this.padding,y1:i,x2:s-this.padding,y2:i,stroke:this.xAxisColor,"stroke-width":this.xAxisWidth,"stroke-dasharray":this.xAxisDashed?"2,2":"none","stroke-opacity":"0.5"});this.svg.appendChild(r)}calculateBounds(){const t=this.data.map(t=>"object"==typeof t?t.value:t);let e=void 0!==this.minValue?this.minValue:Math.min(...t),s=void 0!==this.maxValue?this.maxValue:Math.max(...t);return 0===s-e&&("bar"===this.chartType&&0===e?(e=0,s=1):(e-=1,s+=1)),{min:e,max:s}}getActualWidth(){return this.actualWidth||this.width||100}getActualHeight(){return this.actualHeight||this.height||30}renderLine(t,e){const s=this.data.map(t=>"object"==typeof t?t.value:t),a=this.calculatePoints(s,t,e);if(this.fill){const t=this.createAreaPath(a),e=this.createSVGElement("path",{d:t,fill:this.fillColor,stroke:"none"});this.svg.appendChild(e)}const i=this.smoothing>0?this.createSmoothPath(a):this.createLinePath(a),r=this.createSVGElement("path",{d:i,fill:"none",stroke:this.color,"stroke-width":this.strokeWidth,"stroke-linecap":"round","stroke-linejoin":"round"});this.svg.appendChild(r),this.showDots&&a.forEach(t=>{const e=this.createSVGElement("circle",{cx:t.x,cy:t.y,r:this.dotRadius,fill:this.color});this.svg.appendChild(e)})}renderBar(t,e){const s=this.data.map(t=>"object"==typeof t?t.value:t),a=this.calculatePoints(s,t,e),i=this.getActualWidth(),r=this.getActualHeight(),h=(i-2*this.padding-this.barGap*(s.length-1))/s.length;a.forEach((t,e)=>{const s=r-2*this.padding-t.y+this.padding,a=t.x-h/2,i=t.y,o=this.createSVGElement("rect",{x:a,y:i,width:h,height:s,fill:this.color,rx:1,"data-bar-index":e,class:"mini-chart-bar"});this.svg.appendChild(o)})}calculatePoints(t,e,s){const a=s-e,i=this.getActualWidth(),r=this.getActualHeight(),h=(i-2*this.padding)/(t.length-1||1),o=(r-2*this.padding)/a;return t.map((t,s)=>({x:this.padding+s*h,y:r-this.padding-(t-e)*o}))}createLinePath(t){if(0===t.length)return"";let e=`M ${t[0].x},${t[0].y}`;for(let s=1;s<t.length;s++)e+=` L ${t[s].x},${t[s].y}`;return e}createSmoothPath(t){if(t.length<2)return this.createLinePath(t);let e=`M ${t[0].x},${t[0].y}`;for(let s=0;s<t.length-1;s++){const a=t[s],i=t[s+1];e+=` C ${a.x+(i.x-a.x)*this.smoothing},${a.y} ${i.x-(i.x-a.x)*this.smoothing},${i.y} ${i.x},${i.y}`}return e}createAreaPath(t){if(0===t.length)return"";const e=this.smoothing>0?this.createSmoothPath(t):this.createLinePath(t),s=t[t.length-1],a=t[0],i=this.getActualHeight();return`${e} L ${s.x},${i-this.padding} L ${a.x},${i-this.padding} Z`}createSVGElement(t,e={}){const s=document.createElementNS("http://www.w3.org/2000/svg",t);return Object.entries(e).forEach(([t,e])=>{s.setAttribute(t,e)}),s}applyAnimation(){this.svg.querySelectorAll("path").forEach(t=>{const e=t.getTotalLength();t.style.strokeDasharray=e,t.style.strokeDashoffset=e,t.style.animation=`mini-chart-draw ${this.animationDuration}ms ease-out forwards`}),this.svg.querySelectorAll("rect").forEach((t,e)=>{t.style.transformOrigin="bottom",t.style.animation=`mini-chart-bar-grow ${this.animationDuration}ms ease-out ${20*e}ms forwards`,t.style.transform="scaleY(0)"})}setupTooltip(){if(!this.svg||!this.tooltip)return;const t=this.data.map(t=>"object"==typeof t?t.value:t),e=this.calculatePoints(t,...Object.values(this.calculateBounds())),s=this.getActualWidth(),a=this.getActualHeight(),i=s/t.length;e.forEach((t,e)=>{const s=this.createSVGElement("rect",{x:e*i,y:0,width:i,height:a,fill:"transparent",style:"cursor: pointer;"});s.addEventListener("mouseenter",t=>{this.showTooltipAtIndex(e,t)}),s.addEventListener("mousemove",t=>{this.updateTooltipPosition(t)}),s.addEventListener("mouseleave",()=>{this.hideTooltip()}),this.svg.appendChild(s)})}showTooltipAtIndex(t,e){if(!this.tooltip)return;this.hoveredIndex=t;const s="object"==typeof this.data[t]?this.data[t].value:this.data[t],a="object"==typeof this.data[t]?this.data[t].label:null,i=this.labels?this.labels[t]:a;let r;if(this.tooltipTemplate&&"function"==typeof this.tooltipTemplate)r=this.tooltipTemplate({value:s,label:i,index:t,data:this.data[t]});else{let e=s;e=this.valueFormat&&this.dataFormatter?this.dataFormatter.pipe(s,this.valueFormat):this.tooltipFormatter&&"function"==typeof this.tooltipFormatter?this.tooltipFormatter(s,t):"number"==typeof s?s.toLocaleString():s;let a=i;i&&this.labelFormat&&this.dataFormatter&&(a=this.dataFormatter.pipe(i,this.labelFormat)),r=`<strong>${e}</strong>`,a&&(r=`<div class="mini-chart-tooltip-label">${a}</div>${r}`)}if(this.tooltip.innerHTML=r,this.tooltip.style.display="block",this.updateTooltipPosition(e),"bar"===this.chartType&&this.highlightBar(t),this.crosshair&&this.showCrosshair){const e=this.getActualWidth()/this.data.length,s=t*e+e/2;this.crosshair.setAttribute("x1",s),this.crosshair.setAttribute("x2",s),this.crosshair.style.display="block"}}updateTooltipPosition(t){if(!this.tooltip||"none"===this.tooltip.style.display)return;const e=this.svg.getBoundingClientRect(),s=t.clientX-e.left,a=t.clientY-e.top;this.tooltip.style.left=`${s}px`,this.tooltip.style.top=a-10+"px",this.tooltip.style.transform="translate(-50%, -100%)"}hideTooltip(){this.tooltip&&(this.tooltip.style.display="none",this.hoveredIndex=-1),"bar"===this.chartType&&this.unhighlightBars(),this.crosshair&&(this.crosshair.style.display="none")}highlightBar(t){if(!this.svg)return;this.unhighlightBars();const e=this.svg.querySelector(`rect.mini-chart-bar[data-bar-index="${t}"]`);e&&(e.style.opacity="0.7")}unhighlightBars(){this.svg&&this.svg.querySelectorAll("rect.mini-chart-bar").forEach(t=>{t.style.opacity="1"})}setData(t){this.data=t,this.svg&&this.renderChart()}setColor(t){this.color=t,this.svg&&this.renderChart()}setType(t){["line","bar"].includes(t)&&(this.chartType=t,this.svg&&this.renderChart())}resize(t,e){this.width=t,this.height=e,this.updateDimensions(),this.svg&&this.renderChart()}async onBeforeDestroy(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),await super.onBeforeDestroy()}}class MetricsMiniChart extends MiniChart{constructor(t={}){super(t),this.endpoint=t.endpoint||"/api/metrics/fetch",this.account=t.account||"global",this.granularity=t.granularity||"hours",this.slugs=t.slugs||null,this.category=t.category||null,this.dateStart=t.dateStart||null,this.dateEnd=t.dateEnd||null,this.defaultDateRange=t.defaultDateRange||"24h",this.isLoading=!1,this.lastFetch=null,this.refreshInterval=t.refreshInterval,this.dateStart&&this.dateEnd||this.setQuickRange(this.defaultDateRange),this.slugs&&!Array.isArray(this.slugs)&&(this.slugs=[this.slugs])}async onAfterRender(){await super.onAfterRender(),!this.endpoint||this.data&&0!==this.data.length||await this.fetchData(),this.refreshInterval&&this.endpoint&&this.startAutoRefresh()}buildApiParams(){const t={granularity:this.granularity,account:this.account,with_labels:!0};return this.slugs&&this.slugs.length>0&&this.slugs.forEach(e=>{t["slugs[]"]||(t["slugs[]"]=[]),t["slugs[]"].push(e)}),this.category&&(t.category=this.category),this.dateStart&&(t.dr_start=Math.floor(this.dateStart.getTime()/1e3)),this.dateEnd&&(t.dr_end=Math.floor(this.dateEnd.getTime()/1e3)),t._=Date.now(),t}async fetchData(){if(this.endpoint){this.isLoading=!0;try{const t=this.getApp()?.rest;if(!t)throw new Error("No REST client available");const e=this.buildApiParams(),s=await t.GET(this.endpoint,e);if(!s.success)throw new Error(s.message||"Network error");if(!s.data?.status)throw new Error(s.data?.error||"Server error");const a=s.data.data;this.processMetricsData(a),this.lastFetch=/* @__PURE__ */new Date,await this.render(),this.emit("metrics:loaded",{chart:this,data:a,params:e})}catch(t){console.error("Failed to fetch metrics:",t),this.emit("metrics:error",{chart:this,error:t})}finally{this.isLoading=!1}}}processMetricsData(t){const{data:e,labels:s}=t;if(!e)return;const a=Object.keys(e);if(0===a.length)return;const i=e[a[0]].map(t=>null==t||""===t?0:"number"==typeof t?t:parseFloat(t)||0);this.labels=s||null,this.setData(i)}setQuickRange(t){const e=/* @__PURE__ */new Date;let s;switch(t){case"1h":s=new Date(e.getTime()-36e5);break;case"24h":default:s=new Date(e.getTime()-864e5);break;case"7d":s=new Date(e.getTime()-6048e5);break;case"30d":s=new Date(e.getTime()-2592e6)}this.dateStart=s,this.dateEnd=e}startAutoRefresh(){this.refreshTimer&&clearInterval(this.refreshTimer),this.refreshTimer=setInterval(()=>{this.fetchData()},this.refreshInterval)}stopAutoRefresh(){this.refreshTimer&&(clearInterval(this.refreshTimer),this.refreshTimer=null)}setGranularity(t){return this.granularity=t,this.fetchData()}setDateRange(t,e){return this.dateStart=new Date(t),this.dateEnd=new Date(e),this.fetchData()}setMetrics(t){return this.slugs=Array.isArray(t)?t:[t],this.fetchData()}refresh(){return this.fetchData()}async onBeforeDestroy(){this.stopAutoRefresh(),await super.onBeforeDestroy()}}class MetricsMiniChartWidget extends e.View{constructor(t={}){super({...t,tagName:"div",className:`metrics-mini-chart-widget ${t.className||""}`.trim()}),this.icon=t.icon||null,this.title=t.title||"",this.subtitle=t.subtitle||"",this.background=t.background||null,this.textColor=t.textColor||null,this.showTrending=!!t.showTrending,this.trendRange=t.trendRange??null,this.trendOffset=t.trendOffset??0,this.prevTrendOffset=t.prevTrendOffset??0,this.total=0,this.lastValue=0,this.prevValue=0,this.trendingPercent=0,this.trendingUp=null,this.hasTrending=!1,this.trendingClass="",this.trendingIcon="",this.trendingLabel="",this.chartOptions={endpoint:t.endpoint,account:t.account,granularity:t.granularity,slugs:t.slugs,category:t.category,dateStart:t.dateStart,dateEnd:t.dateEnd,defaultDateRange:t.defaultDateRange,refreshInterval:t.refreshInterval,chartType:t.chartType||"line",showTooltip:void 0===t.showTooltip||t.showTooltip,showXAxis:t.showXAxis||!1,height:t.height||80,width:t.chartWidth||t.width||"100%",color:t.color,fill:void 0===t.fill||t.fill,fillColor:t.fillColor,smoothing:t.smoothing??.3,strokeWidth:t.strokeWidth,barGap:t.barGap,valueFormat:t.valueFormat,labelFormat:t.labelFormat,tooltipFormatter:t.tooltipFormatter,tooltipTemplate:t.tooltipTemplate,showCrosshair:t.showCrosshair,crosshairColor:t.crosshairColor,crosshairWidth:t.crosshairWidth,xAxisColor:t.xAxisColor,xAxisWidth:t.xAxisWidth,xAxisDashed:t.xAxisDashed,padding:t.padding,minValue:t.minValue,maxValue:t.maxValue,showDots:t.showDots,dotRadius:t.dotRadius,animate:t.animate,animationDuration:t.animationDuration}}async onInit(){this.chart=new MetricsMiniChart({...this.chartOptions,containerId:"chart"}),this.addChild(this.chart),this.header=new e.View({containerId:"chart-header",title:this.title,icon:this.icon,template:`\n <div class="d-flex justify-content-between align-items-start mb-2">\n <div class="me-3">\n <h6 class="card-title mb-1" style="${this.textColor?`color: ${this.textColor}`:""}">${this.title}</h6>\n <div class="card-subtitle" style="${this.textColor?`color: ${this.textColor}`:""}">${this.subtitle}</div>\n {{#hasTrending}}\n <div class="small mt-1 fw-semibold {{trendingClass}}" style="${this.textColor?`color: ${this.textColor}`:""}">\n <i class="{{trendingIcon}} me-1"></i>{{trendingLabel}}\n </div>\n {{/hasTrending}}\n </div>\n ${this.icon?`<i class="${this.icon} fs-4 flex-shrink-0" aria-hidden="true" style="${this.textColor?`color: ${this.textColor}`:""}"></i>`:""}\n </div>`}),this.addChild(this.header),this.chart?.on&&this.chart.on("metrics:loaded",this.onChildMetricsLoaded,this),this.updateFromChartData({render:!1})}onChildMetricsLoaded(){this.updateFromChartData({render:!0})}updateFromChartData({render:t=!0}={}){const e=Array.isArray(this.chart?.data)?this.chart.data:null;if(!e||0===e.length)return this.total=0,this.hasTrending=!1,this.header.title=this.title,void(t&&this.render());const s=e.map(t=>{if("number"==typeof t)return t;if(t&&"number"==typeof t.value)return t.value;const e=parseFloat(t);return Number.isNaN(e)?0:e});this.header.title=this.title,this.header.total=s.reduce((t,e)=>t+e,0);const a=Math.max(0,parseInt(this.trendOffset||0,10)||0),i=Math.max(0,s.length-1-a);this.header.now_value=s[i];let r=!1,h=0,o=0;const n=this.trendRange&&this.trendRange>=2?Math.max(1,Math.floor(this.trendRange/2)):1;if(i>=0){const t=i,e=t-(n-1);let a,l;if(this.prevTrendOffset&&this.prevTrendOffset>0?(a=e-this.prevTrendOffset,l=t-this.prevTrendOffset):(l=e-1,a=l-(n-1)),e>=0&&a>=0){const i=(t,e,s)=>{let a=0;for(let i=e;i<=s;i++)a+=t[i]||0;return a};h=i(s,e,t),o=i(s,a,l),r=!0}}if(!r){const t=i-(this.prevTrendOffset&&this.prevTrendOffset>0?this.prevTrendOffset:1);t>=0&&(h=s[i],o=s[t],r=!0)}if(r){this.header.lastValue=h,this.header.prevValue=o;let t=0;t=0===o?h>0?100:0:(h-o)/Math.abs(o)*100,this.header.trendingPercent=t,this.header.trendingUp=t>=0,this.textColor?this.header.trendingClass="":this.header.trendingClass=this.header.trendingUp?"text-success":"text-danger",this.header.trendingIcon=this.header.trendingUp?"bi bi-arrow-up":"bi bi-arrow-down";const e=t>0?"+":"";this.header.trendingLabel=`${e}${t.toFixed(1)}%`,this.header.hasTrending=!0}else this.header.hasTrending=!1;t&&this.header.render()}get cardStyle(){const t=[];return this.background&&t.push(`background: ${this.background}`),this.textColor&&t.push(`color: ${this.textColor}`),t.push("border: 0"),t.join("; ")}async getTemplate(){return`\n <div class="card h-100 shadow-sm" style="${this.cardStyle}">\n <div class="card-body p-3">\n <div data-container="chart-header"></div>\n <div data-container="chart"></div>\n </div>\n </div>\n `}async onBeforeDestroy(){this.chart?.off&&this.chart.off("metrics:loaded",this.onChildMetricsLoaded,this),await super.onBeforeDestroy()}refresh(){this.chart&&(this.chart.account=this.account,this.chart.refresh())}}exports.BaseChart=BaseChart,exports.MetricsChart=class extends SeriesChart{constructor(t={}){super({...t,chartType:t.chartType||"line",title:t.title||"Metrics",colors:t.colors||["rgba(54, 162, 235, 0.8)","rgba(255, 99, 132, 0.8)","rgba(75, 192, 192, 0.8)","rgba(255, 206, 86, 0.8)","rgba(153, 102, 255, 0.8)","rgba(255, 159, 64, 0.8)","rgba(199, 199, 199, 0.8)","rgba(83, 102, 255, 0.8)"],yAxis:t.yAxis||{label:"Count",beginAtZero:!0},tooltip:t.tooltip||{y:"number"},width:t.width,height:t.height}),this.endpoint=t.endpoint||"/api/metrics/fetch",this.account=t.account||"global",this.granularity=t.granularity||"hours",this.slugs=t.slugs||null,this.category=t.category||null,this.dateStart=t.dateStart||null,this.dateEnd=t.dateEnd||null,this.defaultDateRange=t.defaultDateRange||"24h",this.showGranularity=!1!==t.showGranularity,this.showDateRange=!1!==t.showDateRange,this.granularityOptions=t.granularityOptions||[{value:"minutes",label:"Minutes"},{value:"hours",label:"Hours"},{value:"days",label:"Days"},{value:"weeks",label:"Weeks"},{value:"months",label:"Months"}],this.quickRanges=t.quickRanges||[{value:"1h",label:"1H"},{value:"24h",label:"24H"},{value:"7d",label:"7D"},{value:"30d",label:"30D"}],this.availableMetrics=t.availableMetrics||[{value:"api_calls",label:"API Calls"},{value:"api_errors",label:"API Errors"},{value:"incident_evt",label:"System Events"},{value:"incidents",label:"Incidents"}],this.isLoading=!1,this.lastFetch=null,this.dateStart&&this.dateEnd||this.setQuickRange(this.defaultDateRange)}async onInit(){const t=[];this.showGranularity&&t.push({type:"select",name:"granularity",action:"granularity-changed",size:"sm",options:this.granularityOptions.map(t=>({value:t.value,label:t.label,selected:t.value===this.granularity}))}),this.showDateRange&&t.push({type:"button",action:"show-date-range-dialog",labelHtml:`<i class="bi bi-calendar-range me-1"></i>${this.formatDateRangeDisplay()}`,title:"Select Date Range",variant:"outline-secondary",size:"sm"}),this.headerConfig={titleHtml:this.title||"Metrics",chartTitle:this.chartTitle||"",showExport:!0===this.exportEnabled,showRefresh:this.refreshEnabled,showTheme:!1,controls:t},await super.onInit()}async onActionGranularityChanged(t,e){const s=e.value;s&&s!==this.granularity&&(this.granularity=s,await this.fetchData())}async onActionShowDateRangeDialog(){try{const e=await t.default.showForm({title:"Select Date Range",size:"md",fields:[{name:"dateRange",type:"daterange",label:"Date Range",startName:"dt_start",endName:"dt_end",startDate:this.formatDateTimeLocal(this.dateStart),endDate:this.formatDateTimeLocal(this.dateEnd),required:!0}],formConfig:{options:{submitButton:!1,resetButton:!1}}});if(e&&e.startDate&&e.endDate){this.dateStart=new Date(e.startDate),this.dateEnd=new Date(e.endDate);const t=this.element?.querySelector('[data-action="show-date-range-dialog"]');t&&(t.innerHTML=`<i class="bi bi-calendar-range me-1"></i>${this.formatDateRangeDisplay()}`),await this.fetchData()}}catch(e){console.error("Date range dialog error:",e)}}buildApiParams(){const t={granularity:this.granularity,account:this.account,with_labels:!0};return this.slugs&&this.slugs.forEach(e=>{t["slugs[]"]||(t["slugs[]"]=[]),t["slugs[]"].push(e)}),this.category&&(t.category=this.category),this.dateStart&&(t.dr_start=Math.floor(this.dateStart.getTime()/1e3)),this.dateEnd&&(t.dr_end=Math.floor(this.dateEnd.getTime()/1e3)),t._=Date.now(),t}async fetchData(){if(this.endpoint){this.isLoading=!0,this.showLoading();try{const t=this.getApp()?.rest;if(!t)throw new Error("No REST client available");const e=this.buildApiParams(),s=await t.GET(this.endpoint,e);if(!s.success)throw new Error(s.message||"Network error");if(!s.data?.status)throw new Error(s.data?.error||"Server error");const a=s.data.data,i=this.processMetricsData(a);await this.setData(i),this.lastFetch=/* @__PURE__ */new Date,this.emit("metrics:data-loaded",{chart:this,data:a,params:e})}catch(t){console.error("Failed to fetch metrics data:",t),this.showError(`Failed to load metrics: ${t.message}`),this.emit("metrics:error",{chart:this,error:t})}finally{this.isLoading=!1,this.hideLoading()}}}processMetricsData(t){const{data:e,labels:s}=t,a=[];return Object.keys(e).forEach((t,s)=>{const i=e[t].map(t=>null==t||""===t?0:"number"==typeof t?t:parseFloat(t)||0);a.push({label:this.formatMetricLabel(t),data:i,backgroundColor:this.colors[s%this.colors.length].replace("0.8","0.6"),borderColor:this.colors[s%this.colors.length],borderWidth:2,tension:"line"===this.chartType?.4:0,fill:!1,pointRadius:"line"===this.chartType?3:0,pointHoverRadius:5})}),{labels:s,datasets:a}}formatMetricLabel(t){return t.split("_").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}setQuickRange(t){const e=/* @__PURE__ */new Date;let s;switch(t){case"1h":s=new Date(e.getTime()-36e5);break;case"24h":default:s=new Date(e.getTime()-864e5);break;case"7d":s=new Date(e.getTime()-6048e5);break;case"30d":s=new Date(e.getTime()-2592e6)}this.dateStart=s,this.dateEnd=e}formatDateTimeLocal(t){return t?`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}T${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`:""}setGranularity(t){return this.granularity=t,this.fetchData()}setDateRange(t,e){return this.dateStart=new Date(t),this.dateEnd=new Date(e),this.fetchData()}setMetrics(t){return this.slugs=[...t],this.fetchData()}getStats(){return{...super.getStats(),lastFetch:this.lastFetch,granularity:this.granularity,slugs:[...this.slugs],dateRange:{start:this.dateStart,end:this.dateEnd}}}},exports.MetricsMiniChart=MetricsMiniChart,exports.MetricsMiniChartWidget=MetricsMiniChartWidget,exports.MiniChart=MiniChart,exports.PieChart=PieChart,exports.SeriesChart=SeriesChart;
|
|
2
|
-
//# sourceMappingURL=MetricsMiniChartWidget-Esvv-lFp.js.map
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const e=require("./Rest-BNYqGlnP.js");class Page extends e.View{constructor(e={}){e.tagName=e.tagName||"main",e.className=e.className||"mojo-page";const t=e.pageName||"";t&&!e.id&&(e.id="page_"+t.toLowerCase().replace(/\s+/g,"_")),super(e),this.pageName=e.pageName||this.constructor.pageName||"",this.route=e.route||this.constructor.route||"",this.title=e.title||this.pageName||"",this.id||!this.constructor.pageName||e.pageName||(this.id="page_"+this.constructor.pageName.toLowerCase().replace(/\s+/g,"_")),this.pageIcon=e.icon||e.pageIcon||this.constructor.pageIcon||"bi bi-file-text",this.displayName=e.displayName||this.constructor.displayName||this.pageName||"",this.pageDescription=e.pageDescription||this.constructor.pageDescription||"",this.params={},this.query={},this.matched=!1,this.isActive=!1,this.pageOptions={title:e.title||this.pageName||"Untitled Page",description:e.description||"",requiresAuth:e.requiresAuth||!1,...e.pageOptions},this.savedState=null,this.pageName,this.route}async onParams(e={},t={}){this.params=e,this.query=t}canEnter(){if(this.options.permissions){const e=this.getApp().activeUser;if(!e||!e.hasPermission(this.options.permissions))return!1}return!(this.options.requiresGroup&&!this.getApp().activeGroup)}async onEnter(){this.isActive=!0,await this.onInitView(),this.savedState&&(this.restoreState(this.savedState),this.savedState=null),this.pageOptions&&this.pageOptions.title&&"undefined"!=typeof document&&(document.title=this.pageOptions.title),this.emit("activated",{page:this.getMetadata()}),this.pageName}async onExit(){this.savedState=this.captureState(),this.isActive=!1,this.emit("deactivated",{page:this.getMetadata()}),this.pageName}getMetadata(){return{name:this.pageName,displayName:this.displayName||this.pageName,icon:this.pageIcon,description:this.pageDescription,route:this.route,isActive:this.isActive}}async onActionDefault(e){this.pageName}async makeActive(){this.getApp().showPage(this)}async onActionNavigate(e,t){e.preventDefault();const s=t.dataset.page;this.getApp().showPage(s)}captureState(){return this.element?{scrollTop:this.element.scrollTop,formData:this.captureFormData(),custom:this.captureCustomState()}:null}restoreState(e){e&&this.element&&(this.element.scrollTop=e.scrollTop||0,this.restoreFormData(e.formData),e.custom&&this.restoreCustomState(e.custom))}captureFormData(){const e={};return this.element?(this.element.querySelectorAll("input, select, textarea").forEach(t=>{t.name&&("checkbox"===t.type?e[t.name]=t.checked:"radio"===t.type?t.checked&&(e[t.name]=t.value):e[t.name]=t.value)}),e):e}restoreFormData(e){e&&this.element&&Object.entries(e).forEach(([e,t])=>{const s=this.element.querySelector(`[name="${e}"]`);if(s)if("checkbox"===s.type)s.checked=t;else if("radio"===s.type){const s=this.element.querySelector(`[name="${e}"][value="${t}"]`);s&&(s.checked=!0)}else s.value=t})}captureCustomState(){return{}}restoreCustomState(e){}setMeta(e={}){if("undefined"!=typeof document){if(e.title&&(document.title=e.title,this.pageOptions.title=e.title),e.description){let t=document.querySelector('meta[name="description"]');t||(t=document.createElement("meta"),t.name="description",document.head.appendChild(t)),t.content=e.description,this.pageOptions.description=e.description}Object.entries(e).forEach(([e,t])=>{if("title"!==e&&"description"!==e){let s=document.querySelector(`meta[name="${e}"]`);s||(s=document.createElement("meta"),s.name=e,document.head.appendChild(s)),s.content=t}})}}showError(e){if(super.showError(e),this.element){const t=document.createElement("div");t.className="alert alert-danger alert-dismissible fade show",t.innerHTML=`\n ${e}\n <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>\n `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},5e3)}}showSuccess(e){if(super.showSuccess(e),this.element){const t=document.createElement("div");t.className="alert alert-success alert-dismissible fade show",t.innerHTML=`\n ${e}\n <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>\n `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},3e3)}}async onBeforeRender(){await super.onBeforeRender(),this.setMeta({title:this.pageOptions.title,description:this.pageOptions.description})}async onAfterMount(){await super.onAfterMount(),"undefined"!=typeof document&&this.pageName&&document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}async onBeforeDestroy(){await super.onBeforeDestroy(),"undefined"!=typeof document&&this.pageName&&document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}navigate(e,t={},s={}){return this.app&&this.app.router?this.app.router.navigate(e,s):"undefined"!=typeof window&&window.MOJO?.router?window.MOJO.router.navigate(e,s):void console.error("No router available for navigation")}getRoute(){if(this.route){let e=this.route;return"string"==typeof e&&e.startsWith("/")&&(e=e.substring(1)),e}return this.pageName}syncUrl(e=!0){this.updateBrowserUrl(this.query,!1,!1)}updateBrowserUrl(e=null,t=!1,s=!1){this.getApp(),this.app.router.updateBrowserUrl(this.getRoute(),e,t,s)}static define(e){class DefinedPage extends Page{constructor(t={}){super({...e,...t})}}return DefinedPage.template=e.template,DefinedPage.pageName=e.pageName,DefinedPage.route=e.route,DefinedPage}}exports.Page=Page;
|
|
2
|
-
//# sourceMappingURL=Page-CvbwEoLv.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Page-CvbwEoLv.js","sources":["../../src/core/Page.js"],"sourcesContent":["/**\n * Page - Extends View with routing capabilities for MOJO framework\n * Handles URL routing, parameters, and page-specific actions\n *\n * Event Emitter notes:\n * - Uses EventEmitter via View base class.\n * - Use .emit/.on/.off/.once for all custom events.\n */\n\nimport View from '@core/View.js';\n\nclass Page extends View {\n constructor(options = {}) {\n // Set default tag name for pages\n options.tagName = options.tagName || 'main';\n options.className = options.className || 'mojo-page';\n\n // Set page ID based on page name\n const pageName = options.pageName || '';\n if (pageName && !options.id) {\n options.id = 'page_' + pageName.toLowerCase().replace(/\\s+/g, '_');\n }\n\n super(options);\n\n // Core page properties from design doc\n this.pageName = options.pageName || this.constructor.pageName || '';\n this.route = options.route || this.constructor.route || '';\n this.title = options.title || this.pageName || '';\n\n // Set page ID if not already set and we have a page_name from constructor\n if (!this.id && this.constructor.pageName && !options.pageName) {\n this.id = 'page_' + this.constructor.pageName.toLowerCase().replace(/\\s+/g, '_');\n }\n\n // Page metadata for event system\n this.pageIcon = options.icon || options.pageIcon || this.constructor.pageIcon || 'bi bi-file-text';\n this.displayName = options.displayName || this.constructor.displayName || this.pageName || '';\n this.pageDescription = options.pageDescription || this.constructor.pageDescription || '';\n\n // Routing state\n this.params = {};\n this.query = {};\n this.matched = false;\n this.isActive = false;\n\n // Page-specific options\n this.pageOptions = {\n title: options.title || this.pageName || 'Untitled Page',\n description: options.description || '',\n requiresAuth: options.requiresAuth || false,\n ...options.pageOptions\n };\n\n // State preservation\n this.savedState = null;\n\n console.log(`Page ${this.pageName} constructed with route: ${this.route}`);\n }\n\n /**\n * Handle route parameters - from design doc\n * @param {object} params - Route parameters\n * @param {object} query - Query string parameters\n */\n async onParams(params = {}, query = {}) {\n // const paramsChanged = JSON.stringify(params) !== JSON.stringify(this.params);\n // const queryChanged = JSON.stringify(query) !== JSON.stringify(this.query);\n\n this.params = params;\n this.query = query;\n\n // Only re-render if params actually changed and page is active\n // if (this.isActive && (paramsChanged || queryChanged)) {\n // console.log(`Page ${this.pageName} params changed, re-rendering`);\n // await this.render();\n // }\n }\n\n canEnter() {\n if (this.options.permissions) {\n const user = this.getApp().activeUser;\n if (!user || !user.hasPermission(this.options.permissions)) {\n return false;\n }\n }\n if (this.options.requiresGroup && !this.getApp().activeGroup) {\n return false;\n }\n return true;\n }\n\n /**\n * Called when entering this page (before render)\n * Override this method for initialization logic\n */\n async onEnter() {\n this.isActive = true;\n await this.onInitView();\n\n // Restore saved state if exists\n if (this.savedState) {\n this.restoreState(this.savedState);\n this.savedState = null;\n }\n\n // Set page title if provided\n if (this.pageOptions && this.pageOptions.title && typeof document !== 'undefined') {\n document.title = this.pageOptions.title;\n }\n\n // Emit activation event\n this.emit('activated', {\n page: this.getMetadata()\n });\n\n console.log(`Page ${this.pageName} entered`);\n }\n\n /**\n * Called when leaving this page (before cleanup)\n * Override this method for cleanup logic like removing listeners, clearing timers, etc.\n */\n async onExit() {\n // Save state before exit\n this.savedState = this.captureState();\n this.isActive = false;\n\n // Emit deactivation event\n this.emit('deactivated', {\n page: this.getMetadata()\n });\n console.log(`Page ${this.pageName} exiting`);\n }\n\n /**\n * Get page metadata for display and events\n * @returns {object} Page metadata\n */\n getMetadata() {\n return {\n name: this.pageName,\n displayName: this.displayName || this.pageName,\n icon: this.pageIcon,\n description: this.pageDescription,\n route: this.route,\n isActive: this.isActive\n };\n }\n\n /**\n * Handle default action - fallback from design doc\n */\n async onActionDefault(action) {\n console.log(`Default action '${action}' triggered on page: ${this.pageName}`);\n }\n\n async makeActive() {\n this.getApp().showPage(this);\n }\n\n async onActionNavigate(event, element) {\n event.preventDefault();\n const page = element.dataset.page;\n this.getApp().showPage(page);\n }\n\n /**\n * Capture current page state for preservation\n * @returns {object|null} Captured state\n */\n captureState() {\n if (!this.element) return null;\n\n return {\n scrollTop: this.element.scrollTop,\n formData: this.captureFormData(),\n custom: this.captureCustomState()\n };\n }\n\n /**\n * Restore saved state\n * @param {object} state - State to restore\n */\n restoreState(state) {\n if (!state || !this.element) return;\n\n this.element.scrollTop = state.scrollTop || 0;\n this.restoreFormData(state.formData);\n if (state.custom) {\n this.restoreCustomState(state.custom);\n }\n }\n\n /**\n * Capture form data from page\n * @returns {object} Form data\n */\n captureFormData() {\n const data = {};\n if (!this.element) return data;\n\n this.element.querySelectorAll('input, select, textarea').forEach(field => {\n if (field.name) {\n if (field.type === 'checkbox') {\n data[field.name] = field.checked;\n } else if (field.type === 'radio') {\n if (field.checked) {\n data[field.name] = field.value;\n }\n } else {\n data[field.name] = field.value;\n }\n }\n });\n\n return data;\n }\n\n /**\n * Restore form data to page\n * @param {object} formData - Form data to restore\n */\n restoreFormData(formData) {\n if (!formData || !this.element) return;\n\n Object.entries(formData).forEach(([name, value]) => {\n const field = this.element.querySelector(`[name=\"${name}\"]`);\n if (field) {\n if (field.type === 'checkbox') {\n field.checked = value;\n } else if (field.type === 'radio') {\n const radio = this.element.querySelector(`[name=\"${name}\"][value=\"${value}\"]`);\n if (radio) radio.checked = true;\n } else {\n field.value = value;\n }\n }\n });\n }\n\n /**\n * Capture custom state - override in subclasses\n * @returns {object} Custom state\n */\n captureCustomState() {\n return {};\n }\n\n /**\n * Restore custom state - override in subclasses\n * @param {object} state - Custom state to restore\n */\n restoreCustomState(state) {\n // Override in subclasses\n }\n\n\n\n /**\n * Set page metadata\n * @param {object} meta - Metadata object\n */\n setMeta(meta = {}) {\n if (typeof document === 'undefined') {\n return;\n }\n\n // Set title\n if (meta.title) {\n document.title = meta.title;\n this.pageOptions.title = meta.title;\n }\n\n // Set description\n if (meta.description) {\n let descMeta = document.querySelector('meta[name=\"description\"]');\n if (!descMeta) {\n descMeta = document.createElement('meta');\n descMeta.name = 'description';\n document.head.appendChild(descMeta);\n }\n descMeta.content = meta.description;\n this.pageOptions.description = meta.description;\n }\n\n // Set other meta tags\n Object.entries(meta).forEach(([key, value]) => {\n if (key !== 'title' && key !== 'description') {\n let metaEl = document.querySelector(`meta[name=\"${key}\"]`);\n if (!metaEl) {\n metaEl = document.createElement('meta');\n metaEl.name = key;\n document.head.appendChild(metaEl);\n }\n metaEl.content = value;\n }\n });\n }\n\n\n /**\n * Show error message with page context\n * @param {string} message - Error message\n */\n showError(message) {\n super.showError(message);\n\n // Page-specific error display can be implemented here\n if (this.element) {\n // Example: Add error to page\n const errorDiv = document.createElement('div');\n errorDiv.className = 'alert alert-danger alert-dismissible fade show';\n errorDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n // Insert at top of page\n this.element.insertBefore(errorDiv, this.element.firstChild);\n\n // Auto-remove after 5 seconds\n setTimeout(() => {\n if (errorDiv.parentNode) {\n errorDiv.parentNode.removeChild(errorDiv);\n }\n }, 5000);\n }\n }\n\n /**\n * Show success message with page context\n * @param {string} message - Success message\n */\n showSuccess(message) {\n super.showSuccess(message);\n\n // Page-specific success display\n if (this.element) {\n const successDiv = document.createElement('div');\n successDiv.className = 'alert alert-success alert-dismissible fade show';\n successDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n // Insert at top of page\n this.element.insertBefore(successDiv, this.element.firstChild);\n\n // Auto-remove after 3 seconds\n setTimeout(() => {\n if (successDiv.parentNode) {\n successDiv.parentNode.removeChild(successDiv);\n }\n }, 3000);\n }\n }\n\n /**\n * Page-specific before render hook\n */\n async onBeforeRender() {\n await super.onBeforeRender();\n\n // Set page metadata before rendering\n this.setMeta({\n title: this.pageOptions.title,\n description: this.pageOptions.description\n });\n }\n\n /**\n * Page-specific after mount hook\n */\n async onAfterMount() {\n await super.onAfterMount();\n\n // Add page-specific class to body\n if (typeof document !== 'undefined' && this.pageName) {\n document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\\s+/g, '-')}`);\n }\n }\n\n /**\n * Page-specific before destroy hook\n */\n async onBeforeDestroy() {\n await super.onBeforeDestroy();\n\n // Remove page-specific class from body\n if (typeof document !== 'undefined' && this.pageName) {\n document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\\s+/g, '-')}`);\n }\n }\n\n /**\n * Navigate to another page using the app's router\n * @param {string} route - Route to navigate to\n * @param {object} params - Route parameters\n * @param {object} options - Navigation options\n */\n navigate(route, params = {}, options = {}) {\n // Delegate to app's router\n if (this.app && this.app.router) {\n return this.app.router.navigate(route, options);\n }\n\n // Fallback to MOJO global router\n if (typeof window !== 'undefined' && window.MOJO?.router) {\n return window.MOJO.router.navigate(route, options);\n }\n\n console.error('No router available for navigation');\n }\n\n getRoute() {\n if (this.route) {\n let route = this.route;\n if (typeof route === 'string' && route.startsWith('/')) {\n route = route.substring(1);\n }\n return route;\n }\n return this.pageName;\n }\n\n syncUrl(force = true) {\n this.updateBrowserUrl(this.query, false, false);\n }\n\n updateBrowserUrl(query = null, replace = false, trigger = false) {\n this.getApp();\n // we need to do this to normalize the URL\n // const targetPath = this.app.buildPagePath(this, this.params, query);\n // const { pageName, queryParams } = this.app.router.parseInput(targetPath);\n this.app.router.updateBrowserUrl(this.getRoute(), query, replace, trigger);\n }\n\n /**\n * Static method to define a page class with metadata\n * @param {object} definition - Page class definition\n * @returns {class} Page class\n */\n static define(definition) {\n class DefinedPage extends Page {\n constructor(options = {}) {\n super({\n ...definition,\n ...options\n });\n }\n }\n\n // Copy static properties\n DefinedPage.template = definition.template;\n DefinedPage.pageName = definition.pageName;\n DefinedPage.route = definition.route;\n\n return DefinedPage;\n }\n}\n\nexport default Page;\n"],"names":["Page","View","constructor","options","tagName","className","pageName","id","toLowerCase","replace","super","this","route","title","pageIcon","icon","displayName","pageDescription","params","query","matched","isActive","pageOptions","description","requiresAuth","savedState","onParams","canEnter","permissions","user","getApp","activeUser","hasPermission","requiresGroup","activeGroup","onEnter","onInitView","restoreState","document","emit","page","getMetadata","onExit","captureState","name","onActionDefault","action","makeActive","showPage","onActionNavigate","event","element","preventDefault","dataset","scrollTop","formData","captureFormData","custom","captureCustomState","state","restoreFormData","restoreCustomState","data","querySelectorAll","forEach","field","type","checked","value","Object","entries","querySelector","radio","setMeta","meta","descMeta","createElement","head","appendChild","content","key","metaEl","showError","message","errorDiv","innerHTML","insertBefore","firstChild","setTimeout","parentNode","removeChild","showSuccess","successDiv","onBeforeRender","onAfterMount","body","classList","add","onBeforeDestroy","remove","navigate","app","router","window","MOJO","console","error","getRoute","startsWith","substring","syncUrl","force","updateBrowserUrl","trigger","define","definition","DefinedPage","template"],"mappings":"mDAWA,MAAMA,aAAaC,EAAAA,KACjB,WAAAC,CAAYC,EAAU,IAEpBA,EAAQC,QAAUD,EAAQC,SAAW,OACrCD,EAAQE,UAAYF,EAAQE,WAAa,YAGzC,MAAMC,EAAWH,EAAQG,UAAY,GACjCA,IAAaH,EAAQI,KACvBJ,EAAQI,GAAK,QAAUD,EAASE,cAAcC,QAAQ,OAAQ,MAGhEC,MAAMP,GAGNQ,KAAKL,SAAWH,EAAQG,UAAYK,KAAKT,YAAYI,UAAY,GACjEK,KAAKC,MAAQT,EAAQS,OAASD,KAAKT,YAAYU,OAAS,GACxDD,KAAKE,MAAQV,EAAQU,OAASF,KAAKL,UAAY,GAG1CK,KAAKJ,KAAMI,KAAKT,YAAYI,UAAaH,EAAQG,WACpDK,KAAKJ,GAAK,QAAUI,KAAKT,YAAYI,SAASE,cAAcC,QAAQ,OAAQ,MAI9EE,KAAKG,SAAWX,EAAQY,MAAQZ,EAAQW,UAAYH,KAAKT,YAAYY,UAAY,kBACjFH,KAAKK,YAAcb,EAAQa,aAAeL,KAAKT,YAAYc,aAAeL,KAAKL,UAAY,GAC3FK,KAAKM,gBAAkBd,EAAQc,iBAAmBN,KAAKT,YAAYe,iBAAmB,GAGtFN,KAAKO,OAAS,CAAA,EACdP,KAAKQ,MAAQ,CAAA,EACbR,KAAKS,SAAU,EACfT,KAAKU,UAAW,EAGhBV,KAAKW,YAAc,CACjBT,MAAOV,EAAQU,OAASF,KAAKL,UAAY,gBACzCiB,YAAapB,EAAQoB,aAAe,GACpCC,aAAcrB,EAAQqB,eAAgB,KACnCrB,EAAQmB,aAIbX,KAAKc,WAAa,KAEEd,KAAKL,SAAoCK,KAAKC,KACpE,CAOA,cAAMc,CAASR,EAAS,GAAIC,EAAQ,CAAA,GAIlCR,KAAKO,OAASA,EACdP,KAAKQ,MAAQA,CAOf,CAEA,QAAAQ,GACE,GAAIhB,KAAKR,QAAQyB,YAAa,CAC5B,MAAMC,EAAOlB,KAAKmB,SAASC,WAC3B,IAAKF,IAASA,EAAKG,cAAcrB,KAAKR,QAAQyB,aAC5C,OAAO,CAEX,CACA,QAAIjB,KAAKR,QAAQ8B,gBAAkBtB,KAAKmB,SAASI,YAInD,CAMA,aAAMC,GACJxB,KAAKU,UAAW,QACVV,KAAKyB,aAGPzB,KAAKc,aACPd,KAAK0B,aAAa1B,KAAKc,YACvBd,KAAKc,WAAa,MAIhBd,KAAKW,aAAeX,KAAKW,YAAYT,OAA6B,oBAAbyB,WACvDA,SAASzB,MAAQF,KAAKW,YAAYT,OAIpCF,KAAK4B,KAAK,YAAa,CACrBC,KAAM7B,KAAK8B,gBAGO9B,KAAKL,QAC3B,CAMA,YAAMoC,GAEJ/B,KAAKc,WAAad,KAAKgC,eACvBhC,KAAKU,UAAW,EAGhBV,KAAK4B,KAAK,cAAe,CACvBC,KAAM7B,KAAK8B,gBAEO9B,KAAKL,QAC3B,CAMA,WAAAmC,GACE,MAAO,CACLG,KAAMjC,KAAKL,SACXU,YAAaL,KAAKK,aAAeL,KAAKL,SACtCS,KAAMJ,KAAKG,SACXS,YAAaZ,KAAKM,gBAClBL,MAAOD,KAAKC,MACZS,SAAUV,KAAKU,SAEnB,CAKA,qBAAMwB,CAAgBC,GACyCnC,KAAKL,QACpE,CAEA,gBAAMyC,GACFpC,KAAKmB,SAASkB,SAASrC,KAC3B,CAEA,sBAAMsC,CAAiBC,EAAOC,GAC1BD,EAAME,iBACN,MAAMZ,EAAOW,EAAQE,QAAQb,KAC7B7B,KAAKmB,SAASkB,SAASR,EAC3B,CAMA,YAAAG,GACE,OAAKhC,KAAKwC,QAEH,CACLG,UAAW3C,KAAKwC,QAAQG,UACxBC,SAAU5C,KAAK6C,kBACfC,OAAQ9C,KAAK+C,sBALW,IAO5B,CAMA,YAAArB,CAAasB,GACNA,GAAUhD,KAAKwC,UAEpBxC,KAAKwC,QAAQG,UAAYK,EAAML,WAAa,EAC5C3C,KAAKiD,gBAAgBD,EAAMJ,UACvBI,EAAMF,QACR9C,KAAKkD,mBAAmBF,EAAMF,QAElC,CAMA,eAAAD,GACE,MAAMM,EAAO,CAAA,EACb,OAAKnD,KAAKwC,SAEVxC,KAAKwC,QAAQY,iBAAiB,2BAA2BC,QAAQC,IAC3DA,EAAMrB,OACW,aAAfqB,EAAMC,KACRJ,EAAKG,EAAMrB,MAAQqB,EAAME,QACD,UAAfF,EAAMC,KACXD,EAAME,UACRL,EAAKG,EAAMrB,MAAQqB,EAAMG,OAG3BN,EAAKG,EAAMrB,MAAQqB,EAAMG,SAKxBN,GAhBmBA,CAiB5B,CAMA,eAAAF,CAAgBL,GACTA,GAAa5C,KAAKwC,SAEvBkB,OAAOC,QAAQf,GAAUS,QAAQ,EAAEpB,EAAMwB,MACvC,MAAMH,EAAQtD,KAAKwC,QAAQoB,cAAc,UAAU3B,OACnD,GAAIqB,EACF,GAAmB,aAAfA,EAAMC,KACRD,EAAME,QAAUC,OAClB,GAA0B,UAAfH,EAAMC,KAAkB,CACjC,MAAMM,EAAQ7D,KAAKwC,QAAQoB,cAAc,UAAU3B,cAAiBwB,OAChEI,MAAaL,SAAU,EAC7B,MACEF,EAAMG,MAAQA,GAItB,CAMA,kBAAAV,GACE,MAAO,CAAA,CACT,CAMA,kBAAAG,CAAmBF,GAEnB,CAQA,OAAAc,CAAQC,EAAO,IACb,GAAwB,oBAAbpC,SAAX,CAWA,GANIoC,EAAK7D,QACPyB,SAASzB,MAAQ6D,EAAK7D,MACtBF,KAAKW,YAAYT,MAAQ6D,EAAK7D,OAI5B6D,EAAKnD,YAAa,CACpB,IAAIoD,EAAWrC,SAASiC,cAAc,4BACjCI,IACHA,EAAWrC,SAASsC,cAAc,QAClCD,EAAS/B,KAAO,cAChBN,SAASuC,KAAKC,YAAYH,IAE5BA,EAASI,QAAUL,EAAKnD,YACxBZ,KAAKW,YAAYC,YAAcmD,EAAKnD,WACtC,CAGA8C,OAAOC,QAAQI,GAAMV,QAAQ,EAAEgB,EAAKZ,MAClC,GAAY,UAARY,GAA2B,gBAARA,EAAuB,CAC5C,IAAIC,EAAS3C,SAASiC,cAAc,cAAcS,OAC7CC,IACHA,EAAS3C,SAASsC,cAAc,QAChCK,EAAOrC,KAAOoC,EACd1C,SAASuC,KAAKC,YAAYG,IAE5BA,EAAOF,QAAUX,CACnB,GA9BF,CAgCF,CAOA,SAAAc,CAAUC,GAIR,GAHAzE,MAAMwE,UAAUC,GAGZxE,KAAKwC,QAAS,CAEhB,MAAMiC,EAAW9C,SAASsC,cAAc,OACxCQ,EAAS/E,UAAY,iDACrB+E,EAASC,UAAY,aACjBF,kHAKJxE,KAAKwC,QAAQmC,aAAaF,EAAUzE,KAAKwC,QAAQoC,YAGjDC,WAAW,KACLJ,EAASK,YACXL,EAASK,WAAWC,YAAYN,IAEjC,IACL,CACF,CAMA,WAAAO,CAAYR,GAIV,GAHAzE,MAAMiF,YAAYR,GAGdxE,KAAKwC,QAAS,CAChB,MAAMyC,EAAatD,SAASsC,cAAc,OAC1CgB,EAAWvF,UAAY,kDACvBuF,EAAWP,UAAY,aACnBF,kHAKJxE,KAAKwC,QAAQmC,aAAaM,EAAYjF,KAAKwC,QAAQoC,YAGnDC,WAAW,KACLI,EAAWH,YACbG,EAAWH,WAAWC,YAAYE,IAEnC,IACL,CACF,CAKA,oBAAMC,SACEnF,MAAMmF,iBAGZlF,KAAK8D,QAAQ,CACX5D,MAAOF,KAAKW,YAAYT,MACxBU,YAAaZ,KAAKW,YAAYC,aAElC,CAKA,kBAAMuE,SACEpF,MAAMoF,eAGY,oBAAbxD,UAA4B3B,KAAKL,UAC1CgC,SAASyD,KAAKC,UAAUC,IAAI,QAAQtF,KAAKL,SAASE,cAAcC,QAAQ,OAAQ,OAEpF,CAKA,qBAAMyF,SACExF,MAAMwF,kBAGY,oBAAb5D,UAA4B3B,KAAKL,UAC1CgC,SAASyD,KAAKC,UAAUG,OAAO,QAAQxF,KAAKL,SAASE,cAAcC,QAAQ,OAAQ,OAEvF,CAQA,QAAA2F,CAASxF,EAAOM,EAAS,CAAA,EAAIf,EAAU,CAAA,GAErC,OAAIQ,KAAK0F,KAAO1F,KAAK0F,IAAIC,OAChB3F,KAAK0F,IAAIC,OAAOF,SAASxF,EAAOT,GAInB,oBAAXoG,QAA0BA,OAAOC,MAAMF,OACzCC,OAAOC,KAAKF,OAAOF,SAASxF,EAAOT,QAG5CsG,QAAQC,MAAM,qCAChB,CAEA,QAAAC,GACI,GAAIhG,KAAKC,MAAO,CACZ,IAAIA,EAAQD,KAAKC,MAIjB,MAHqB,iBAAVA,GAAsBA,EAAMgG,WAAW,OAC9ChG,EAAQA,EAAMiG,UAAU,IAErBjG,CACX,CACA,OAAOD,KAAKL,QAChB,CAEA,OAAAwG,CAAQC,GAAQ,GACZpG,KAAKqG,iBAAiBrG,KAAKQ,OAAO,GAAO,EAC7C,CAEA,gBAAA6F,CAAiB7F,EAAQ,KAAMV,GAAU,EAAOwG,GAAU,GACxDtG,KAAKmB,SAILnB,KAAK0F,IAAIC,OAAOU,iBAAiBrG,KAAKgG,WAAYxF,EAAOV,EAASwG,EACpE,CAOA,aAAOC,CAAOC,GACZ,MAAMC,oBAAoBpH,KACxB,WAAAE,CAAYC,EAAU,IACpBO,MAAM,IACDyG,KACAhH,GAEP,EAQF,OAJAiH,YAAYC,SAAWF,EAAWE,SAClCD,YAAY9G,SAAW6G,EAAW7G,SAClC8G,YAAYxG,MAAQuG,EAAWvG,MAExBwG,WACT"}
|