web-mojo 2.1.534 → 2.1.535

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.
Files changed (58) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.es.js +10 -10
  3. package/dist/auth.cjs.js +1 -1
  4. package/dist/auth.cjs.js.map +1 -1
  5. package/dist/auth.es.js +3 -3
  6. package/dist/auth.es.js.map +1 -1
  7. package/dist/charts.cjs.js +1 -1
  8. package/dist/charts.es.js +3 -3
  9. package/dist/chunks/{ChatView-ClUFUtJe.js → ChatView-7uXqcmuN.js} +2 -2
  10. package/dist/chunks/{ChatView-ClUFUtJe.js.map → ChatView-7uXqcmuN.js.map} +1 -1
  11. package/dist/chunks/{ChatView-BR4MUIQ7.js → ChatView-B6_WuQEq.js} +6 -6
  12. package/dist/chunks/{ChatView-BR4MUIQ7.js.map → ChatView-B6_WuQEq.js.map} +1 -1
  13. package/dist/chunks/{ContextMenu-CKGSU-E0.js → ContextMenu-CUFAbhvX.js} +2 -2
  14. package/dist/chunks/{ContextMenu-CKGSU-E0.js.map → ContextMenu-CUFAbhvX.js.map} +1 -1
  15. package/dist/chunks/{ContextMenu-r62gqzG_.js → ContextMenu-DL21mK-K.js} +2 -2
  16. package/dist/chunks/{ContextMenu-r62gqzG_.js.map → ContextMenu-DL21mK-K.js.map} +1 -1
  17. package/dist/chunks/{DataView-DRUU1yu6.js → DataView-pjnkp_s6.js} +2 -2
  18. package/dist/chunks/{DataView-DRUU1yu6.js.map → DataView-pjnkp_s6.js.map} +1 -1
  19. package/dist/chunks/{DataView-C00j4YuH.js → DataView-ztckztwQ.js} +2 -2
  20. package/dist/chunks/{DataView-C00j4YuH.js.map → DataView-ztckztwQ.js.map} +1 -1
  21. package/dist/chunks/{Dialog-DOltX5lG.js → Dialog-Be3ZkGtt.js} +5 -5
  22. package/dist/chunks/{Dialog-DOltX5lG.js.map → Dialog-Be3ZkGtt.js.map} +1 -1
  23. package/dist/chunks/{Dialog-CvWIF2VC.js → Dialog-DZcy_jFe.js} +2 -2
  24. package/dist/chunks/{Dialog-CvWIF2VC.js.map → Dialog-DZcy_jFe.js.map} +1 -1
  25. package/dist/chunks/{FormView-CvUhsQ87.js → FormView-CyHZtouJ.js} +2 -2
  26. package/dist/chunks/{FormView-CvUhsQ87.js.map → FormView-CyHZtouJ.js.map} +1 -1
  27. package/dist/chunks/{FormView-DxyOHSb3.js → FormView-RJ5niGVY.js} +2 -2
  28. package/dist/chunks/{FormView-DxyOHSb3.js.map → FormView-RJ5niGVY.js.map} +1 -1
  29. package/dist/chunks/{MetricsChart-XlMOtMvk.js → MetricsChart-DK9a46gB.js} +2 -2
  30. package/dist/chunks/{MetricsChart-XlMOtMvk.js.map → MetricsChart-DK9a46gB.js.map} +1 -1
  31. package/dist/chunks/{MetricsChart-CqX2FxtM.js → MetricsChart-HFbmINDC.js} +3 -3
  32. package/dist/chunks/{MetricsChart-CqX2FxtM.js.map → MetricsChart-HFbmINDC.js.map} +1 -1
  33. package/dist/chunks/{PDFViewer-BzQuNUos.js → PDFViewer-DSL5K-RC.js} +3 -3
  34. package/dist/chunks/{PDFViewer-BzQuNUos.js.map → PDFViewer-DSL5K-RC.js.map} +1 -1
  35. package/dist/chunks/{PDFViewer-0iyFqFcD.js → PDFViewer-DtTeFEKS.js} +2 -2
  36. package/dist/chunks/{PDFViewer-0iyFqFcD.js.map → PDFViewer-DtTeFEKS.js.map} +1 -1
  37. package/dist/chunks/{Page-CFEQrTH-.js → Page-D8fANAJX.js} +2 -2
  38. package/dist/chunks/{Page-CFEQrTH-.js.map → Page-D8fANAJX.js.map} +1 -1
  39. package/dist/chunks/{Page-B0pLNM5B.js → Page-fyywOoGg.js} +2 -2
  40. package/dist/chunks/{Page-B0pLNM5B.js.map → Page-fyywOoGg.js.map} +1 -1
  41. package/dist/chunks/{TopNav-Cqtmux-V.js → TopNav-BYsRM6BZ.js} +2 -2
  42. package/dist/chunks/{TopNav-Cqtmux-V.js.map → TopNav-BYsRM6BZ.js.map} +1 -1
  43. package/dist/chunks/{TopNav-DtOyaoKD.js → TopNav-DnpYXG9a.js} +2 -2
  44. package/dist/chunks/{TopNav-DtOyaoKD.js.map → TopNav-DnpYXG9a.js.map} +1 -1
  45. package/dist/chunks/{WebApp-DhGNFpF5.js → WebApp-Brh2wRZ3.js} +15 -15
  46. package/dist/chunks/WebApp-Brh2wRZ3.js.map +1 -0
  47. package/dist/chunks/WebApp-XHTE27hi.js +2 -0
  48. package/dist/chunks/WebApp-XHTE27hi.js.map +1 -0
  49. package/dist/docit.cjs.js +1 -1
  50. package/dist/docit.es.js +5 -5
  51. package/dist/index.cjs.js +1 -1
  52. package/dist/index.es.js +11 -11
  53. package/dist/lightbox.cjs.js +1 -1
  54. package/dist/lightbox.es.js +4 -4
  55. package/package.json +1 -1
  56. package/dist/chunks/WebApp-CcfMKEFQ.js +0 -2
  57. package/dist/chunks/WebApp-CcfMKEFQ.js.map +0 -1
  58. package/dist/chunks/WebApp-DhGNFpF5.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"TopNav-Cqtmux-V.js","sources":["../../src/core/views/navigation/TopNav.js"],"sourcesContent":["/**\n * TopNav - Bootstrap navbar component for MOJO framework\n * Provides clean, responsive top navigation\n */\n\nimport View from '@core/View.js';\n\nclass TopNav extends View {\n constructor(options = {}) {\n // Define theme-to-class mappings\n const themes = {\n light: 'navbar navbar-expand-lg navbar-light topnav-light',\n dark: 'navbar navbar-expand-lg navbar-dark topnav-dark',\n clean: 'navbar navbar-expand-lg navbar-light topnav-clean',\n gradient: 'navbar navbar-expand-lg navbar-dark topnav-gradient',\n };\n\n // Set a default theme and determine the final class string\n const themeName = options.theme || 'light';\n let navbarClass = themes[themeName] || themes.light;\n\n // Add shadow class if specified\n if (options.shadow) {\n navbarClass += ` topnav-shadow-${options.shadow}`;\n }\n\n super({\n tagName: 'nav',\n className: navbarClass,\n style: 'position: relative; z-index: 1030;',\n ...options\n });\n\n // Display mode configuration\n this.displayMode = options.displayMode || 'both'; // 'menu' | 'page' | 'both'\n this.showPageIcon = options.showPageIcon !== false;\n this.showPageDescription = options.showPageDescription || false;\n this.showBreadcrumbs = options.showBreadcrumbs || false;\n\n // Current page tracking\n this.currentPage = null;\n this.previousPage = null;\n\n // Store raw config for processing in onBeforeRender\n this.config = {\n brand: options.brand || 'MOJO App',\n brandIcon: options.brandIcon || 'bi bi-play-circle',\n brandRoute: options.brandRoute || '/',\n navItems: options.navItems || [],\n rightItems: options.rightItems || [],\n showSidebarToggle: options.showSidebarToggle || false,\n sidebarToggleAction: options.sidebarToggleAction || 'toggle-sidebar',\n ...options\n };\n this.userMenu = options.userMenu || this.findMenuItem('user');\n if (this.userMenu) this.userMenu.id = \"user\";\n this.loginMenu = options.loginMenu || this.findMenuItem('login');\n // Setup page event listeners\n this.setupPageListeners();\n }\n\n findMenuItem(id) {\n let item = this.config.navItems.find(item => item.id === id);\n if (!item) {\n item = this.config.rightItems.find(item => item.id === id);\n }\n return item || null;\n }\n\n replaceMenuItem(id, new_menu) {\n // Find and replace in navItems\n const navIndex = this.config.navItems.findIndex(item => item.id === id);\n if (navIndex !== -1) {\n this.config.navItems[navIndex] = new_menu;\n return true;\n }\n\n // Find and replace in rightItems\n const rightIndex = this.config.rightItems.findIndex(item => item.id === id);\n if (rightIndex !== -1) {\n this.config.rightItems[rightIndex] = new_menu;\n return true;\n }\n\n return false;\n }\n\n setBrand(brand, icon=null) {\n this.config.brand = brand;\n this.config.brandIcon = icon || this.config.brandIcon;\n this.render();\n }\n\n setUser(user) {\n if (!user) {\n this.replaceMenuItem('user', this.loginMenu);\n } else {\n this.userMenu.label = user.get(\"display_name\");\n this.replaceMenuItem('login', this.userMenu);\n }\n this.setModel(user);\n }\n\n _onModelChange() {\n if (this.model) {\n this.userMenu.label = this.model.get(\"display_name\");\n }\n if (this.isMounted()) {\n this.render();\n }\n }\n\n /**\n * Get template based on display mode\n */\n async getTemplate() {\n return `\n <div class=\"container-fluid\">\n {{#data.showSidebarToggle}}\n <button class=\"topnav-sidebar-toggle me-2\" data-action=\"{{data.sidebarToggleAction}}\" aria-label=\"Toggle Sidebar\">\n <i class=\"bi bi-chevron-right toggle-chevron\"></i>\n </button>\n {{/data.showSidebarToggle}}\n\n {{#data.showPageInfo}}\n <div class=\"navbar-brand d-flex align-items-center\">\n {{#data.currentPageIcon}}<i class=\"{{data.currentPageIcon}} me-2\"></i>{{/data.currentPageIcon}}\n <div>\n <span>{{data.currentPageName}}</span>\n {{#data.currentPageDescription}}\n <small class=\"d-block\" style=\"font-size: 0.75rem; line-height: 1;\">{{data.currentPageDescription}}</small>\n {{/data.currentPageDescription}}\n </div>\n </div>\n {{/data.showPageInfo}}\n\n {{^data.showPageInfo}}\n <a class=\"navbar-brand\" href=\"{{data.brandRoute}}\">\n {{#data.brandIcon}}<i class=\"{{data.brandIcon}} me-2\"></i>{{/data.brandIcon}}\n {{data.brand}}\n </a>\n {{/data.showPageInfo}}\n\n <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#{{data.navbarId}}\">\n <span class=\"navbar-toggler-icon\"></span>\n </button>\n\n <div class=\"collapse navbar-collapse\" id=\"{{data.navbarId}}\">\n {{#data.showNavItems}}\n <ul class=\"navbar-nav me-auto mb-2 mb-lg-0\">\n {{#data.navItems}}\n <li class=\"nav-item\">\n <a class=\"nav-link {{#active}}active{{/active}}\" href=\"{{route}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{text}}\n </a>\n </li>\n {{/data.navItems}}\n </ul>\n {{/data.showNavItems}}\n\n {{#data.hasRightItems}}\n <div class=\"navbar-nav ms-auto\">\n {{#data.rightItems}}\n {{#isDropdown}}\n <div class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" role=\"button\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n {{#items}}\n {{#divider}}\n <li><hr class=\"dropdown-divider\"></li>\n {{/divider}}\n {{^divider}}\n <li>\n <a class=\"dropdown-item\" role=\"button\" {{#action}}data-action=\"{{action}}\"{{/action}}>\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n </li>\n {{/divider}}\n {{/items}}\n </ul>\n </div>\n {{/isDropdown}}\n {{^isDropdown}}\n {{#isButton}}\n <button class=\"{{buttonClass}}\" data-action=\"{{action}}\" data-id=\"{{id}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </button>\n {{/isButton}}\n {{^isButton}}\n <a class=\"nav-link\" href=\"{{href}}\" {{#action}}data-action=\"{{action}}\"{{/action}}>\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n {{/isButton}}\n {{/isDropdown}}\n {{/data.rightItems}}\n </div>\n {{/data.hasRightItems}}\n </div>\n </div>\n `;\n }\n\n /**\n * Process and normalize data before rendering (like Sidebar)\n */\n async onBeforeRender() {\n await super.onBeforeRender();\n\n const showPageInfo = this.displayMode === 'page' || this.displayMode === 'both';\n const showNavItems = this.displayMode === 'menu' || this.displayMode === 'both';\n\n // Filter navItems based on permissions\n const navItems = this.filterItemsByPermissions(this.config.navItems || []);\n\n // Process right items\n const rightItems = this.processRightItems(this.config.rightItems || []);\n\n this.data = {\n // Brand information\n brand: this.config.brand,\n brandIcon: this.config.brandIcon,\n brandRoute: this.config.brandRoute,\n\n // Navbar configuration\n navbarId: `navbar-${this.id}`,\n\n // Navigation items\n navItems: navItems,\n showNavItems: showNavItems,\n\n // Right items\n rightItems: rightItems,\n hasRightItems: rightItems.length > 0,\n\n // Page display\n showPageInfo: showPageInfo,\n currentPageName: this.currentPage?.title || this.currentPage?.name || '',\n currentPageIcon: this.currentPage?.icon || this.currentPage?.pageIcon || '',\n currentPageDescription: this.showPageDescription ? this.currentPage?.description : '',\n\n // Sidebar toggle\n showSidebarToggle: this.config.showSidebarToggle,\n sidebarToggleAction: this.config.sidebarToggleAction,\n\n // Display mode\n displayMode: this.displayMode\n };\n }\n\n /**\n * Process right items configuration\n */\n processRightItems(rightItems) {\n return this.filterItemsByPermissions(rightItems).map(item => {\n const processedItem = { ...item };\n\n // Filter dropdown items by permissions if they exist\n if (item.items) {\n processedItem.items = this.filterItemsByPermissions(item.items);\n }\n\n // Determine item type\n if (processedItem.items && processedItem.items.length > 0) {\n // Dropdown menu\n processedItem.isDropdown = true;\n processedItem.isButton = false;\n } else if (item.buttonClass) {\n // Button\n processedItem.isButton = true;\n processedItem.isDropdown = false;\n } else {\n // Link\n processedItem.isButton = false;\n processedItem.isDropdown = false;\n }\n\n // Store handler if provided\n if (item.handler) {\n this.rightItemHandlers = this.rightItemHandlers || new Map();\n this.rightItemHandlers.set(item.id, item.handler);\n }\n\n return processedItem;\n });\n }\n\n /**\n * Setup listeners for page change events\n */\n setupPageListeners() {\n // Use global MOJO event bus if available\n this.getApp().events.on([\"page:show\", \"page:hide\", \"page:denied\"], (data) => {\n this.onPageChanged(data);\n });\n }\n\n /**\n * Handle page before change event\n * @param {object} data - Event data\n */\n onPageBeforeChange(data) {\n // Can be used to show loading state\n if (this.displayMode === 'page' || this.displayMode === 'both') {\n // Optionally show loading indicator\n }\n }\n\n /**\n * Handle page changed event\n * @param {object} data - Event data with previousPage and currentPage\n */\n onPageChanged(data) {\n this.previousPage = this.currentPage;\n this.currentPage = data.page;\n\n // Update display based on mode\n if (this.displayMode === 'page' || this.displayMode === 'both') {\n this.updatePageDisplay();\n }\n\n // Update active menu items\n if (this.displayMode === 'menu' || this.displayMode === 'both') {\n if (this.currentPage && this.currentPage.route) {\n this.updateActiveItem(this.currentPage.route);\n }\n }\n }\n\n /**\n * Update the display to show current page info\n */\n updatePageDisplay() {\n if (!this.currentPage) return;\n\n // Just trigger re-render, onBeforeRender will handle the data processing\n if (this.mounted) {\n this.render();\n }\n }\n\n updateActiveItem(currentRoute) {\n // Normalize routes for comparison\n const normalizeRoute = (route) => {\n if (!route) return '/';\n return route.startsWith('/') ? route : `/${route}`;\n };\n\n const normalizedCurrentRoute = normalizeRoute(currentRoute);\n\n // Update active states with improved matching\n const navItems = this.data.navItems.map(item => {\n const normalizedItemRoute = normalizeRoute(item.route);\n\n // Check for active state\n let isActive = false;\n\n if (normalizedItemRoute === '/' && normalizedCurrentRoute === '/') {\n // Exact match for home route\n isActive = true;\n } else if (normalizedItemRoute !== '/' && normalizedCurrentRoute !== '/') {\n // For non-home routes, check if current route starts with nav item route\n // This allows /users to be active when on /users/123\n isActive = normalizedCurrentRoute.startsWith(normalizedItemRoute) ||\n normalizedCurrentRoute === normalizedItemRoute;\n }\n\n return {\n ...item,\n active: isActive\n };\n });\n\n this.updateData({ navItems }, true);\n }\n\n onPassThruActionProfile() {\n // Implement profile functionality here\n this.getApp().events.emit(\"portal:action\", {action: \"profile\"});\n }\n\n onActionSettings() {\n // Implement settings functionality here\n this.getApp().events.emit(\"portal:action\", {action: \"settings\"});\n }\n\n onActionLogout() {\n // Implement logout functionality here\n this.getApp().events.emit(\"auth:logout\", {action: \"logout\"});\n }\n\n /**\n * Handle dynamic action dispatch for right items\n */\n async handleAction(actionName, event, element) {\n // Check for custom handler first\n const itemId = element.getAttribute('data-id');\n if (itemId && this.rightItemHandlers && this.rightItemHandlers.has(itemId)) {\n const handler = this.rightItemHandlers.get(itemId);\n if (typeof handler === 'function') {\n return await handler.call(this, actionName, event, element);\n }\n }\n\n // Fallback to default action methods\n const methodName = `onAction${actionName.charAt(0).toUpperCase() + actionName.slice(1).replace(/-([a-z])/g, (g) => g[1].toUpperCase())}`;\n if (typeof this[methodName] === 'function') {\n return await this[methodName](event, element);\n }\n\n // Emit action event if no handler found\n this.emit('action', {\n action: actionName,\n event: event,\n element: element,\n topnav: this\n });\n }\n\n /**\n * Handle default actions by searching through rightItems and navItems\n */\n async onActionDefault(action, event, el) {\n // Check navItems first\n if (this.config.navItems) {\n for (const item of this.config.navItems) {\n if (item.action === action && item.handler) {\n await item.handler.call(this, action, event, el);\n return true;\n }\n }\n }\n\n // Check rightItems\n if (this.config.rightItems) {\n for (const item of this.config.rightItems) {\n if (item.action === action && item.handler) {\n await item.handler.call(this, action, event, el);\n return true;\n }\n // Also check dropdown items\n if (item.items) {\n for (const subItem of item.items) {\n if (subItem.action === action && subItem.handler) {\n await subItem.handler.call(this, action, event, el);\n return true;\n }\n }\n }\n }\n }\n\n this.getApp().events.emit(\"portal:action\", { action, event, el });\n\n return false;\n }\n\n /**\n * Filter items by user permissions\n */\n filterItemsByPermissions(items) {\n if (!items) return [];\n\n const app = this.getApp();\n const activeUser = app?.activeUser;\n\n return items.filter(item => {\n // If item has permissions and user exists, check permissions\n if (item.permissions && activeUser) {\n return activeUser.hasPermission(item.permissions);\n }\n // If no permissions required or no user, show the item\n return true;\n });\n }\n\n}\n\nexport default TopNav;\n"],"names":["item"],"mappings":"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"}
1
+ {"version":3,"file":"TopNav-BYsRM6BZ.js","sources":["../../src/core/views/navigation/TopNav.js"],"sourcesContent":["/**\n * TopNav - Bootstrap navbar component for MOJO framework\n * Provides clean, responsive top navigation\n */\n\nimport View from '@core/View.js';\n\nclass TopNav extends View {\n constructor(options = {}) {\n // Define theme-to-class mappings\n const themes = {\n light: 'navbar navbar-expand-lg navbar-light topnav-light',\n dark: 'navbar navbar-expand-lg navbar-dark topnav-dark',\n clean: 'navbar navbar-expand-lg navbar-light topnav-clean',\n gradient: 'navbar navbar-expand-lg navbar-dark topnav-gradient',\n };\n\n // Set a default theme and determine the final class string\n const themeName = options.theme || 'light';\n let navbarClass = themes[themeName] || themes.light;\n\n // Add shadow class if specified\n if (options.shadow) {\n navbarClass += ` topnav-shadow-${options.shadow}`;\n }\n\n super({\n tagName: 'nav',\n className: navbarClass,\n style: 'position: relative; z-index: 1030;',\n ...options\n });\n\n // Display mode configuration\n this.displayMode = options.displayMode || 'both'; // 'menu' | 'page' | 'both'\n this.showPageIcon = options.showPageIcon !== false;\n this.showPageDescription = options.showPageDescription || false;\n this.showBreadcrumbs = options.showBreadcrumbs || false;\n\n // Current page tracking\n this.currentPage = null;\n this.previousPage = null;\n\n // Store raw config for processing in onBeforeRender\n this.config = {\n brand: options.brand || 'MOJO App',\n brandIcon: options.brandIcon || 'bi bi-play-circle',\n brandRoute: options.brandRoute || '/',\n navItems: options.navItems || [],\n rightItems: options.rightItems || [],\n showSidebarToggle: options.showSidebarToggle || false,\n sidebarToggleAction: options.sidebarToggleAction || 'toggle-sidebar',\n ...options\n };\n this.userMenu = options.userMenu || this.findMenuItem('user');\n if (this.userMenu) this.userMenu.id = \"user\";\n this.loginMenu = options.loginMenu || this.findMenuItem('login');\n // Setup page event listeners\n this.setupPageListeners();\n }\n\n findMenuItem(id) {\n let item = this.config.navItems.find(item => item.id === id);\n if (!item) {\n item = this.config.rightItems.find(item => item.id === id);\n }\n return item || null;\n }\n\n replaceMenuItem(id, new_menu) {\n // Find and replace in navItems\n const navIndex = this.config.navItems.findIndex(item => item.id === id);\n if (navIndex !== -1) {\n this.config.navItems[navIndex] = new_menu;\n return true;\n }\n\n // Find and replace in rightItems\n const rightIndex = this.config.rightItems.findIndex(item => item.id === id);\n if (rightIndex !== -1) {\n this.config.rightItems[rightIndex] = new_menu;\n return true;\n }\n\n return false;\n }\n\n setBrand(brand, icon=null) {\n this.config.brand = brand;\n this.config.brandIcon = icon || this.config.brandIcon;\n this.render();\n }\n\n setUser(user) {\n if (!user) {\n this.replaceMenuItem('user', this.loginMenu);\n } else {\n this.userMenu.label = user.get(\"display_name\");\n this.replaceMenuItem('login', this.userMenu);\n }\n this.setModel(user);\n }\n\n _onModelChange() {\n if (this.model) {\n this.userMenu.label = this.model.get(\"display_name\");\n }\n if (this.isMounted()) {\n this.render();\n }\n }\n\n /**\n * Get template based on display mode\n */\n async getTemplate() {\n return `\n <div class=\"container-fluid\">\n {{#data.showSidebarToggle}}\n <button class=\"topnav-sidebar-toggle me-2\" data-action=\"{{data.sidebarToggleAction}}\" aria-label=\"Toggle Sidebar\">\n <i class=\"bi bi-chevron-right toggle-chevron\"></i>\n </button>\n {{/data.showSidebarToggle}}\n\n {{#data.showPageInfo}}\n <div class=\"navbar-brand d-flex align-items-center\">\n {{#data.currentPageIcon}}<i class=\"{{data.currentPageIcon}} me-2\"></i>{{/data.currentPageIcon}}\n <div>\n <span>{{data.currentPageName}}</span>\n {{#data.currentPageDescription}}\n <small class=\"d-block\" style=\"font-size: 0.75rem; line-height: 1;\">{{data.currentPageDescription}}</small>\n {{/data.currentPageDescription}}\n </div>\n </div>\n {{/data.showPageInfo}}\n\n {{^data.showPageInfo}}\n <a class=\"navbar-brand\" href=\"{{data.brandRoute}}\">\n {{#data.brandIcon}}<i class=\"{{data.brandIcon}} me-2\"></i>{{/data.brandIcon}}\n {{data.brand}}\n </a>\n {{/data.showPageInfo}}\n\n <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#{{data.navbarId}}\">\n <span class=\"navbar-toggler-icon\"></span>\n </button>\n\n <div class=\"collapse navbar-collapse\" id=\"{{data.navbarId}}\">\n {{#data.showNavItems}}\n <ul class=\"navbar-nav me-auto mb-2 mb-lg-0\">\n {{#data.navItems}}\n <li class=\"nav-item\">\n <a class=\"nav-link {{#active}}active{{/active}}\" href=\"{{route}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{text}}\n </a>\n </li>\n {{/data.navItems}}\n </ul>\n {{/data.showNavItems}}\n\n {{#data.hasRightItems}}\n <div class=\"navbar-nav ms-auto\">\n {{#data.rightItems}}\n {{#isDropdown}}\n <div class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" role=\"button\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n {{#items}}\n {{#divider}}\n <li><hr class=\"dropdown-divider\"></li>\n {{/divider}}\n {{^divider}}\n <li>\n <a class=\"dropdown-item\" role=\"button\" {{#action}}data-action=\"{{action}}\"{{/action}}>\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n </li>\n {{/divider}}\n {{/items}}\n </ul>\n </div>\n {{/isDropdown}}\n {{^isDropdown}}\n {{#isButton}}\n <button class=\"{{buttonClass}}\" data-action=\"{{action}}\" data-id=\"{{id}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </button>\n {{/isButton}}\n {{^isButton}}\n <a class=\"nav-link\" href=\"{{href}}\" {{#action}}data-action=\"{{action}}\"{{/action}}>\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n {{/isButton}}\n {{/isDropdown}}\n {{/data.rightItems}}\n </div>\n {{/data.hasRightItems}}\n </div>\n </div>\n `;\n }\n\n /**\n * Process and normalize data before rendering (like Sidebar)\n */\n async onBeforeRender() {\n await super.onBeforeRender();\n\n const showPageInfo = this.displayMode === 'page' || this.displayMode === 'both';\n const showNavItems = this.displayMode === 'menu' || this.displayMode === 'both';\n\n // Filter navItems based on permissions\n const navItems = this.filterItemsByPermissions(this.config.navItems || []);\n\n // Process right items\n const rightItems = this.processRightItems(this.config.rightItems || []);\n\n this.data = {\n // Brand information\n brand: this.config.brand,\n brandIcon: this.config.brandIcon,\n brandRoute: this.config.brandRoute,\n\n // Navbar configuration\n navbarId: `navbar-${this.id}`,\n\n // Navigation items\n navItems: navItems,\n showNavItems: showNavItems,\n\n // Right items\n rightItems: rightItems,\n hasRightItems: rightItems.length > 0,\n\n // Page display\n showPageInfo: showPageInfo,\n currentPageName: this.currentPage?.title || this.currentPage?.name || '',\n currentPageIcon: this.currentPage?.icon || this.currentPage?.pageIcon || '',\n currentPageDescription: this.showPageDescription ? this.currentPage?.description : '',\n\n // Sidebar toggle\n showSidebarToggle: this.config.showSidebarToggle,\n sidebarToggleAction: this.config.sidebarToggleAction,\n\n // Display mode\n displayMode: this.displayMode\n };\n }\n\n /**\n * Process right items configuration\n */\n processRightItems(rightItems) {\n return this.filterItemsByPermissions(rightItems).map(item => {\n const processedItem = { ...item };\n\n // Filter dropdown items by permissions if they exist\n if (item.items) {\n processedItem.items = this.filterItemsByPermissions(item.items);\n }\n\n // Determine item type\n if (processedItem.items && processedItem.items.length > 0) {\n // Dropdown menu\n processedItem.isDropdown = true;\n processedItem.isButton = false;\n } else if (item.buttonClass) {\n // Button\n processedItem.isButton = true;\n processedItem.isDropdown = false;\n } else {\n // Link\n processedItem.isButton = false;\n processedItem.isDropdown = false;\n }\n\n // Store handler if provided\n if (item.handler) {\n this.rightItemHandlers = this.rightItemHandlers || new Map();\n this.rightItemHandlers.set(item.id, item.handler);\n }\n\n return processedItem;\n });\n }\n\n /**\n * Setup listeners for page change events\n */\n setupPageListeners() {\n // Use global MOJO event bus if available\n this.getApp().events.on([\"page:show\", \"page:hide\", \"page:denied\"], (data) => {\n this.onPageChanged(data);\n });\n }\n\n /**\n * Handle page before change event\n * @param {object} data - Event data\n */\n onPageBeforeChange(data) {\n // Can be used to show loading state\n if (this.displayMode === 'page' || this.displayMode === 'both') {\n // Optionally show loading indicator\n }\n }\n\n /**\n * Handle page changed event\n * @param {object} data - Event data with previousPage and currentPage\n */\n onPageChanged(data) {\n this.previousPage = this.currentPage;\n this.currentPage = data.page;\n\n // Update display based on mode\n if (this.displayMode === 'page' || this.displayMode === 'both') {\n this.updatePageDisplay();\n }\n\n // Update active menu items\n if (this.displayMode === 'menu' || this.displayMode === 'both') {\n if (this.currentPage && this.currentPage.route) {\n this.updateActiveItem(this.currentPage.route);\n }\n }\n }\n\n /**\n * Update the display to show current page info\n */\n updatePageDisplay() {\n if (!this.currentPage) return;\n\n // Just trigger re-render, onBeforeRender will handle the data processing\n if (this.mounted) {\n this.render();\n }\n }\n\n updateActiveItem(currentRoute) {\n // Normalize routes for comparison\n const normalizeRoute = (route) => {\n if (!route) return '/';\n return route.startsWith('/') ? route : `/${route}`;\n };\n\n const normalizedCurrentRoute = normalizeRoute(currentRoute);\n\n // Update active states with improved matching\n const navItems = this.data.navItems.map(item => {\n const normalizedItemRoute = normalizeRoute(item.route);\n\n // Check for active state\n let isActive = false;\n\n if (normalizedItemRoute === '/' && normalizedCurrentRoute === '/') {\n // Exact match for home route\n isActive = true;\n } else if (normalizedItemRoute !== '/' && normalizedCurrentRoute !== '/') {\n // For non-home routes, check if current route starts with nav item route\n // This allows /users to be active when on /users/123\n isActive = normalizedCurrentRoute.startsWith(normalizedItemRoute) ||\n normalizedCurrentRoute === normalizedItemRoute;\n }\n\n return {\n ...item,\n active: isActive\n };\n });\n\n this.updateData({ navItems }, true);\n }\n\n onPassThruActionProfile() {\n // Implement profile functionality here\n this.getApp().events.emit(\"portal:action\", {action: \"profile\"});\n }\n\n onActionSettings() {\n // Implement settings functionality here\n this.getApp().events.emit(\"portal:action\", {action: \"settings\"});\n }\n\n onActionLogout() {\n // Implement logout functionality here\n this.getApp().events.emit(\"auth:logout\", {action: \"logout\"});\n }\n\n /**\n * Handle dynamic action dispatch for right items\n */\n async handleAction(actionName, event, element) {\n // Check for custom handler first\n const itemId = element.getAttribute('data-id');\n if (itemId && this.rightItemHandlers && this.rightItemHandlers.has(itemId)) {\n const handler = this.rightItemHandlers.get(itemId);\n if (typeof handler === 'function') {\n return await handler.call(this, actionName, event, element);\n }\n }\n\n // Fallback to default action methods\n const methodName = `onAction${actionName.charAt(0).toUpperCase() + actionName.slice(1).replace(/-([a-z])/g, (g) => g[1].toUpperCase())}`;\n if (typeof this[methodName] === 'function') {\n return await this[methodName](event, element);\n }\n\n // Emit action event if no handler found\n this.emit('action', {\n action: actionName,\n event: event,\n element: element,\n topnav: this\n });\n }\n\n /**\n * Handle default actions by searching through rightItems and navItems\n */\n async onActionDefault(action, event, el) {\n // Check navItems first\n if (this.config.navItems) {\n for (const item of this.config.navItems) {\n if (item.action === action && item.handler) {\n await item.handler.call(this, action, event, el);\n return true;\n }\n }\n }\n\n // Check rightItems\n if (this.config.rightItems) {\n for (const item of this.config.rightItems) {\n if (item.action === action && item.handler) {\n await item.handler.call(this, action, event, el);\n return true;\n }\n // Also check dropdown items\n if (item.items) {\n for (const subItem of item.items) {\n if (subItem.action === action && subItem.handler) {\n await subItem.handler.call(this, action, event, el);\n return true;\n }\n }\n }\n }\n }\n\n this.getApp().events.emit(\"portal:action\", { action, event, el });\n\n return false;\n }\n\n /**\n * Filter items by user permissions\n */\n filterItemsByPermissions(items) {\n if (!items) return [];\n\n const app = this.getApp();\n const activeUser = app?.activeUser;\n\n return items.filter(item => {\n // If item has permissions and user exists, check permissions\n if (item.permissions && activeUser) {\n return activeUser.hasPermission(item.permissions);\n }\n // If no permissions required or no user, show the item\n return true;\n });\n }\n\n}\n\nexport default TopNav;\n"],"names":["item"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";const t=require("./WebApp-CcfMKEFQ.js");class TopNav extends t.View{constructor(t={}){const n={light:"navbar navbar-expand-lg navbar-light topnav-light",dark:"navbar navbar-expand-lg navbar-dark topnav-dark",clean:"navbar navbar-expand-lg navbar-light topnav-clean",gradient:"navbar navbar-expand-lg navbar-dark topnav-gradient"};let e=n[t.theme||"light"]||n.light;t.shadow&&(e+=` topnav-shadow-${t.shadow}`),super({tagName:"nav",className:e,style:"position: relative; z-index: 1030;",...t}),this.displayMode=t.displayMode||"both",this.showPageIcon=!1!==t.showPageIcon,this.showPageDescription=t.showPageDescription||!1,this.showBreadcrumbs=t.showBreadcrumbs||!1,this.currentPage=null,this.previousPage=null,this.config={brand:t.brand||"MOJO App",brandIcon:t.brandIcon||"bi bi-play-circle",brandRoute:t.brandRoute||"/",navItems:t.navItems||[],rightItems:t.rightItems||[],showSidebarToggle:t.showSidebarToggle||!1,sidebarToggleAction:t.sidebarToggleAction||"toggle-sidebar",...t},this.userMenu=t.userMenu||this.findMenuItem("user"),this.userMenu&&(this.userMenu.id="user"),this.loginMenu=t.loginMenu||this.findMenuItem("login"),this.setupPageListeners()}findMenuItem(t){let n=this.config.navItems.find(n=>n.id===t);return n||(n=this.config.rightItems.find(n=>n.id===t)),n||null}replaceMenuItem(t,n){const e=this.config.navItems.findIndex(n=>n.id===t);if(-1!==e)return this.config.navItems[e]=n,!0;const a=this.config.rightItems.findIndex(n=>n.id===t);return-1!==a&&(this.config.rightItems[a]=n,!0)}setBrand(t,n=null){this.config.brand=t,this.config.brandIcon=n||this.config.brandIcon,this.render()}setUser(t){t?(this.userMenu.label=t.get("display_name"),this.replaceMenuItem("login",this.userMenu)):this.replaceMenuItem("user",this.loginMenu),this.setModel(t)}_onModelChange(){this.model&&(this.userMenu.label=this.model.get("display_name")),this.isMounted()&&this.render()}async getTemplate(){return'\n <div class="container-fluid">\n {{#data.showSidebarToggle}}\n <button class="topnav-sidebar-toggle me-2" data-action="{{data.sidebarToggleAction}}" aria-label="Toggle Sidebar">\n <i class="bi bi-chevron-right toggle-chevron"></i>\n </button>\n {{/data.showSidebarToggle}}\n\n {{#data.showPageInfo}}\n <div class="navbar-brand d-flex align-items-center">\n {{#data.currentPageIcon}}<i class="{{data.currentPageIcon}} me-2"></i>{{/data.currentPageIcon}}\n <div>\n <span>{{data.currentPageName}}</span>\n {{#data.currentPageDescription}}\n <small class="d-block" style="font-size: 0.75rem; line-height: 1;">{{data.currentPageDescription}}</small>\n {{/data.currentPageDescription}}\n </div>\n </div>\n {{/data.showPageInfo}}\n\n {{^data.showPageInfo}}\n <a class="navbar-brand" href="{{data.brandRoute}}">\n {{#data.brandIcon}}<i class="{{data.brandIcon}} me-2"></i>{{/data.brandIcon}}\n {{data.brand}}\n </a>\n {{/data.showPageInfo}}\n\n <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#{{data.navbarId}}">\n <span class="navbar-toggler-icon"></span>\n </button>\n\n <div class="collapse navbar-collapse" id="{{data.navbarId}}">\n {{#data.showNavItems}}\n <ul class="navbar-nav me-auto mb-2 mb-lg-0">\n {{#data.navItems}}\n <li class="nav-item">\n <a class="nav-link {{#active}}active{{/active}}" href="{{route}}">\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{text}}\n </a>\n </li>\n {{/data.navItems}}\n </ul>\n {{/data.showNavItems}}\n\n {{#data.hasRightItems}}\n <div class="navbar-nav ms-auto">\n {{#data.rightItems}}\n {{#isDropdown}}\n <div class="nav-item dropdown">\n <a class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n <ul class="dropdown-menu dropdown-menu-end">\n {{#items}}\n {{#divider}}\n <li><hr class="dropdown-divider"></li>\n {{/divider}}\n {{^divider}}\n <li>\n <a class="dropdown-item" role="button" {{#action}}data-action="{{action}}"{{/action}}>\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n </li>\n {{/divider}}\n {{/items}}\n </ul>\n </div>\n {{/isDropdown}}\n {{^isDropdown}}\n {{#isButton}}\n <button class="{{buttonClass}}" data-action="{{action}}" data-id="{{id}}">\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </button>\n {{/isButton}}\n {{^isButton}}\n <a class="nav-link" href="{{href}}" {{#action}}data-action="{{action}}"{{/action}}>\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n {{/isButton}}\n {{/isDropdown}}\n {{/data.rightItems}}\n </div>\n {{/data.hasRightItems}}\n </div>\n </div>\n '}async onBeforeRender(){await super.onBeforeRender();const t="page"===this.displayMode||"both"===this.displayMode,n="menu"===this.displayMode||"both"===this.displayMode,e=this.filterItemsByPermissions(this.config.navItems||[]),a=this.processRightItems(this.config.rightItems||[]);this.data={brand:this.config.brand,brandIcon:this.config.brandIcon,brandRoute:this.config.brandRoute,navbarId:`navbar-${this.id}`,navItems:e,showNavItems:n,rightItems:a,hasRightItems:a.length>0,showPageInfo:t,currentPageName:this.currentPage?.title||this.currentPage?.name||"",currentPageIcon:this.currentPage?.icon||this.currentPage?.pageIcon||"",currentPageDescription:this.showPageDescription?this.currentPage?.description:"",showSidebarToggle:this.config.showSidebarToggle,sidebarToggleAction:this.config.sidebarToggleAction,displayMode:this.displayMode}}processRightItems(t){return this.filterItemsByPermissions(t).map(t=>{const n={...t};return t.items&&(n.items=this.filterItemsByPermissions(t.items)),n.items&&n.items.length>0?(n.isDropdown=!0,n.isButton=!1):t.buttonClass?(n.isButton=!0,n.isDropdown=!1):(n.isButton=!1,n.isDropdown=!1),t.handler&&(this.rightItemHandlers=this.rightItemHandlers||/* @__PURE__ */new Map,this.rightItemHandlers.set(t.id,t.handler)),n})}setupPageListeners(){this.getApp().events.on(["page:show","page:hide","page:denied"],t=>{this.onPageChanged(t)})}onPageBeforeChange(t){"page"===this.displayMode||this.displayMode}onPageChanged(t){this.previousPage=this.currentPage,this.currentPage=t.page,"page"!==this.displayMode&&"both"!==this.displayMode||this.updatePageDisplay(),"menu"!==this.displayMode&&"both"!==this.displayMode||this.currentPage&&this.currentPage.route&&this.updateActiveItem(this.currentPage.route)}updatePageDisplay(){this.currentPage&&this.mounted&&this.render()}updateActiveItem(t){const n=t=>t?t.startsWith("/")?t:`/${t}`:"/",e=n(t),a=this.data.navItems.map(t=>{const a=n(t.route);let i=!1;return"/"===a&&"/"===e?i=!0:"/"!==a&&"/"!==e&&(i=e.startsWith(a)||e===a),{...t,active:i}});this.updateData({navItems:a},!0)}onPassThruActionProfile(){this.getApp().events.emit("portal:action",{action:"profile"})}onActionSettings(){this.getApp().events.emit("portal:action",{action:"settings"})}onActionLogout(){this.getApp().events.emit("auth:logout",{action:"logout"})}async handleAction(t,n,e){const a=e.getAttribute("data-id");if(a&&this.rightItemHandlers&&this.rightItemHandlers.has(a)){const i=this.rightItemHandlers.get(a);if("function"==typeof i)return await i.call(this,t,n,e)}const i=`onAction${t.charAt(0).toUpperCase()+t.slice(1).replace(/-([a-z])/g,t=>t[1].toUpperCase())}`;if("function"==typeof this[i])return await this[i](n,e);this.emit("action",{action:t,event:n,element:e,topnav:this})}async onActionDefault(t,n,e){if(this.config.navItems)for(const a of this.config.navItems)if(a.action===t&&a.handler)return await a.handler.call(this,t,n,e),!0;if(this.config.rightItems)for(const a of this.config.rightItems){if(a.action===t&&a.handler)return await a.handler.call(this,t,n,e),!0;if(a.items)for(const i of a.items)if(i.action===t&&i.handler)return await i.handler.call(this,t,n,e),!0}return this.getApp().events.emit("portal:action",{action:t,event:n,el:e}),!1}filterItemsByPermissions(t){if(!t)return[];const n=this.getApp(),e=n?.activeUser;return t.filter(t=>!t.permissions||!e||e.hasPermission(t.permissions))}}exports.TopNav=TopNav;
2
- //# sourceMappingURL=TopNav-DtOyaoKD.js.map
1
+ "use strict";const t=require("./WebApp-XHTE27hi.js");class TopNav extends t.View{constructor(t={}){const n={light:"navbar navbar-expand-lg navbar-light topnav-light",dark:"navbar navbar-expand-lg navbar-dark topnav-dark",clean:"navbar navbar-expand-lg navbar-light topnav-clean",gradient:"navbar navbar-expand-lg navbar-dark topnav-gradient"};let e=n[t.theme||"light"]||n.light;t.shadow&&(e+=` topnav-shadow-${t.shadow}`),super({tagName:"nav",className:e,style:"position: relative; z-index: 1030;",...t}),this.displayMode=t.displayMode||"both",this.showPageIcon=!1!==t.showPageIcon,this.showPageDescription=t.showPageDescription||!1,this.showBreadcrumbs=t.showBreadcrumbs||!1,this.currentPage=null,this.previousPage=null,this.config={brand:t.brand||"MOJO App",brandIcon:t.brandIcon||"bi bi-play-circle",brandRoute:t.brandRoute||"/",navItems:t.navItems||[],rightItems:t.rightItems||[],showSidebarToggle:t.showSidebarToggle||!1,sidebarToggleAction:t.sidebarToggleAction||"toggle-sidebar",...t},this.userMenu=t.userMenu||this.findMenuItem("user"),this.userMenu&&(this.userMenu.id="user"),this.loginMenu=t.loginMenu||this.findMenuItem("login"),this.setupPageListeners()}findMenuItem(t){let n=this.config.navItems.find(n=>n.id===t);return n||(n=this.config.rightItems.find(n=>n.id===t)),n||null}replaceMenuItem(t,n){const e=this.config.navItems.findIndex(n=>n.id===t);if(-1!==e)return this.config.navItems[e]=n,!0;const a=this.config.rightItems.findIndex(n=>n.id===t);return-1!==a&&(this.config.rightItems[a]=n,!0)}setBrand(t,n=null){this.config.brand=t,this.config.brandIcon=n||this.config.brandIcon,this.render()}setUser(t){t?(this.userMenu.label=t.get("display_name"),this.replaceMenuItem("login",this.userMenu)):this.replaceMenuItem("user",this.loginMenu),this.setModel(t)}_onModelChange(){this.model&&(this.userMenu.label=this.model.get("display_name")),this.isMounted()&&this.render()}async getTemplate(){return'\n <div class="container-fluid">\n {{#data.showSidebarToggle}}\n <button class="topnav-sidebar-toggle me-2" data-action="{{data.sidebarToggleAction}}" aria-label="Toggle Sidebar">\n <i class="bi bi-chevron-right toggle-chevron"></i>\n </button>\n {{/data.showSidebarToggle}}\n\n {{#data.showPageInfo}}\n <div class="navbar-brand d-flex align-items-center">\n {{#data.currentPageIcon}}<i class="{{data.currentPageIcon}} me-2"></i>{{/data.currentPageIcon}}\n <div>\n <span>{{data.currentPageName}}</span>\n {{#data.currentPageDescription}}\n <small class="d-block" style="font-size: 0.75rem; line-height: 1;">{{data.currentPageDescription}}</small>\n {{/data.currentPageDescription}}\n </div>\n </div>\n {{/data.showPageInfo}}\n\n {{^data.showPageInfo}}\n <a class="navbar-brand" href="{{data.brandRoute}}">\n {{#data.brandIcon}}<i class="{{data.brandIcon}} me-2"></i>{{/data.brandIcon}}\n {{data.brand}}\n </a>\n {{/data.showPageInfo}}\n\n <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#{{data.navbarId}}">\n <span class="navbar-toggler-icon"></span>\n </button>\n\n <div class="collapse navbar-collapse" id="{{data.navbarId}}">\n {{#data.showNavItems}}\n <ul class="navbar-nav me-auto mb-2 mb-lg-0">\n {{#data.navItems}}\n <li class="nav-item">\n <a class="nav-link {{#active}}active{{/active}}" href="{{route}}">\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{text}}\n </a>\n </li>\n {{/data.navItems}}\n </ul>\n {{/data.showNavItems}}\n\n {{#data.hasRightItems}}\n <div class="navbar-nav ms-auto">\n {{#data.rightItems}}\n {{#isDropdown}}\n <div class="nav-item dropdown">\n <a class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n <ul class="dropdown-menu dropdown-menu-end">\n {{#items}}\n {{#divider}}\n <li><hr class="dropdown-divider"></li>\n {{/divider}}\n {{^divider}}\n <li>\n <a class="dropdown-item" role="button" {{#action}}data-action="{{action}}"{{/action}}>\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n </li>\n {{/divider}}\n {{/items}}\n </ul>\n </div>\n {{/isDropdown}}\n {{^isDropdown}}\n {{#isButton}}\n <button class="{{buttonClass}}" data-action="{{action}}" data-id="{{id}}">\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </button>\n {{/isButton}}\n {{^isButton}}\n <a class="nav-link" href="{{href}}" {{#action}}data-action="{{action}}"{{/action}}>\n {{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}\n {{label}}\n </a>\n {{/isButton}}\n {{/isDropdown}}\n {{/data.rightItems}}\n </div>\n {{/data.hasRightItems}}\n </div>\n </div>\n '}async onBeforeRender(){await super.onBeforeRender();const t="page"===this.displayMode||"both"===this.displayMode,n="menu"===this.displayMode||"both"===this.displayMode,e=this.filterItemsByPermissions(this.config.navItems||[]),a=this.processRightItems(this.config.rightItems||[]);this.data={brand:this.config.brand,brandIcon:this.config.brandIcon,brandRoute:this.config.brandRoute,navbarId:`navbar-${this.id}`,navItems:e,showNavItems:n,rightItems:a,hasRightItems:a.length>0,showPageInfo:t,currentPageName:this.currentPage?.title||this.currentPage?.name||"",currentPageIcon:this.currentPage?.icon||this.currentPage?.pageIcon||"",currentPageDescription:this.showPageDescription?this.currentPage?.description:"",showSidebarToggle:this.config.showSidebarToggle,sidebarToggleAction:this.config.sidebarToggleAction,displayMode:this.displayMode}}processRightItems(t){return this.filterItemsByPermissions(t).map(t=>{const n={...t};return t.items&&(n.items=this.filterItemsByPermissions(t.items)),n.items&&n.items.length>0?(n.isDropdown=!0,n.isButton=!1):t.buttonClass?(n.isButton=!0,n.isDropdown=!1):(n.isButton=!1,n.isDropdown=!1),t.handler&&(this.rightItemHandlers=this.rightItemHandlers||/* @__PURE__ */new Map,this.rightItemHandlers.set(t.id,t.handler)),n})}setupPageListeners(){this.getApp().events.on(["page:show","page:hide","page:denied"],t=>{this.onPageChanged(t)})}onPageBeforeChange(t){"page"===this.displayMode||this.displayMode}onPageChanged(t){this.previousPage=this.currentPage,this.currentPage=t.page,"page"!==this.displayMode&&"both"!==this.displayMode||this.updatePageDisplay(),"menu"!==this.displayMode&&"both"!==this.displayMode||this.currentPage&&this.currentPage.route&&this.updateActiveItem(this.currentPage.route)}updatePageDisplay(){this.currentPage&&this.mounted&&this.render()}updateActiveItem(t){const n=t=>t?t.startsWith("/")?t:`/${t}`:"/",e=n(t),a=this.data.navItems.map(t=>{const a=n(t.route);let i=!1;return"/"===a&&"/"===e?i=!0:"/"!==a&&"/"!==e&&(i=e.startsWith(a)||e===a),{...t,active:i}});this.updateData({navItems:a},!0)}onPassThruActionProfile(){this.getApp().events.emit("portal:action",{action:"profile"})}onActionSettings(){this.getApp().events.emit("portal:action",{action:"settings"})}onActionLogout(){this.getApp().events.emit("auth:logout",{action:"logout"})}async handleAction(t,n,e){const a=e.getAttribute("data-id");if(a&&this.rightItemHandlers&&this.rightItemHandlers.has(a)){const i=this.rightItemHandlers.get(a);if("function"==typeof i)return await i.call(this,t,n,e)}const i=`onAction${t.charAt(0).toUpperCase()+t.slice(1).replace(/-([a-z])/g,t=>t[1].toUpperCase())}`;if("function"==typeof this[i])return await this[i](n,e);this.emit("action",{action:t,event:n,element:e,topnav:this})}async onActionDefault(t,n,e){if(this.config.navItems)for(const a of this.config.navItems)if(a.action===t&&a.handler)return await a.handler.call(this,t,n,e),!0;if(this.config.rightItems)for(const a of this.config.rightItems){if(a.action===t&&a.handler)return await a.handler.call(this,t,n,e),!0;if(a.items)for(const i of a.items)if(i.action===t&&i.handler)return await i.handler.call(this,t,n,e),!0}return this.getApp().events.emit("portal:action",{action:t,event:n,el:e}),!1}filterItemsByPermissions(t){if(!t)return[];const n=this.getApp(),e=n?.activeUser;return t.filter(t=>!t.permissions||!e||e.hasPermission(t.permissions))}}exports.TopNav=TopNav;
2
+ //# sourceMappingURL=TopNav-DnpYXG9a.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TopNav-DtOyaoKD.js","sources":["../../src/core/views/navigation/TopNav.js"],"sourcesContent":["/**\n * TopNav - Bootstrap navbar component for MOJO framework\n * Provides clean, responsive top navigation\n */\n\nimport View from '@core/View.js';\n\nclass TopNav extends View {\n constructor(options = {}) {\n // Define theme-to-class mappings\n const themes = {\n light: 'navbar navbar-expand-lg navbar-light topnav-light',\n dark: 'navbar navbar-expand-lg navbar-dark topnav-dark',\n clean: 'navbar navbar-expand-lg navbar-light topnav-clean',\n gradient: 'navbar navbar-expand-lg navbar-dark topnav-gradient',\n };\n\n // Set a default theme and determine the final class string\n const themeName = options.theme || 'light';\n let navbarClass = themes[themeName] || themes.light;\n\n // Add shadow class if specified\n if (options.shadow) {\n navbarClass += ` topnav-shadow-${options.shadow}`;\n }\n\n super({\n tagName: 'nav',\n className: navbarClass,\n style: 'position: relative; z-index: 1030;',\n ...options\n });\n\n // Display mode configuration\n this.displayMode = options.displayMode || 'both'; // 'menu' | 'page' | 'both'\n this.showPageIcon = options.showPageIcon !== false;\n this.showPageDescription = options.showPageDescription || false;\n this.showBreadcrumbs = options.showBreadcrumbs || false;\n\n // Current page tracking\n this.currentPage = null;\n this.previousPage = null;\n\n // Store raw config for processing in onBeforeRender\n this.config = {\n brand: options.brand || 'MOJO App',\n brandIcon: options.brandIcon || 'bi bi-play-circle',\n brandRoute: options.brandRoute || '/',\n navItems: options.navItems || [],\n rightItems: options.rightItems || [],\n showSidebarToggle: options.showSidebarToggle || false,\n sidebarToggleAction: options.sidebarToggleAction || 'toggle-sidebar',\n ...options\n };\n this.userMenu = options.userMenu || this.findMenuItem('user');\n if (this.userMenu) this.userMenu.id = \"user\";\n this.loginMenu = options.loginMenu || this.findMenuItem('login');\n // Setup page event listeners\n this.setupPageListeners();\n }\n\n findMenuItem(id) {\n let item = this.config.navItems.find(item => item.id === id);\n if (!item) {\n item = this.config.rightItems.find(item => item.id === id);\n }\n return item || null;\n }\n\n replaceMenuItem(id, new_menu) {\n // Find and replace in navItems\n const navIndex = this.config.navItems.findIndex(item => item.id === id);\n if (navIndex !== -1) {\n this.config.navItems[navIndex] = new_menu;\n return true;\n }\n\n // Find and replace in rightItems\n const rightIndex = this.config.rightItems.findIndex(item => item.id === id);\n if (rightIndex !== -1) {\n this.config.rightItems[rightIndex] = new_menu;\n return true;\n }\n\n return false;\n }\n\n setBrand(brand, icon=null) {\n this.config.brand = brand;\n this.config.brandIcon = icon || this.config.brandIcon;\n this.render();\n }\n\n setUser(user) {\n if (!user) {\n this.replaceMenuItem('user', this.loginMenu);\n } else {\n this.userMenu.label = user.get(\"display_name\");\n this.replaceMenuItem('login', this.userMenu);\n }\n this.setModel(user);\n }\n\n _onModelChange() {\n if (this.model) {\n this.userMenu.label = this.model.get(\"display_name\");\n }\n if (this.isMounted()) {\n this.render();\n }\n }\n\n /**\n * Get template based on display mode\n */\n async getTemplate() {\n return `\n <div class=\"container-fluid\">\n {{#data.showSidebarToggle}}\n <button class=\"topnav-sidebar-toggle me-2\" data-action=\"{{data.sidebarToggleAction}}\" aria-label=\"Toggle Sidebar\">\n <i class=\"bi bi-chevron-right toggle-chevron\"></i>\n </button>\n {{/data.showSidebarToggle}}\n\n {{#data.showPageInfo}}\n <div class=\"navbar-brand d-flex align-items-center\">\n {{#data.currentPageIcon}}<i class=\"{{data.currentPageIcon}} me-2\"></i>{{/data.currentPageIcon}}\n <div>\n <span>{{data.currentPageName}}</span>\n {{#data.currentPageDescription}}\n <small class=\"d-block\" style=\"font-size: 0.75rem; line-height: 1;\">{{data.currentPageDescription}}</small>\n {{/data.currentPageDescription}}\n </div>\n </div>\n {{/data.showPageInfo}}\n\n {{^data.showPageInfo}}\n <a class=\"navbar-brand\" href=\"{{data.brandRoute}}\">\n {{#data.brandIcon}}<i class=\"{{data.brandIcon}} me-2\"></i>{{/data.brandIcon}}\n {{data.brand}}\n </a>\n {{/data.showPageInfo}}\n\n <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#{{data.navbarId}}\">\n <span class=\"navbar-toggler-icon\"></span>\n </button>\n\n <div class=\"collapse navbar-collapse\" id=\"{{data.navbarId}}\">\n {{#data.showNavItems}}\n <ul class=\"navbar-nav me-auto mb-2 mb-lg-0\">\n {{#data.navItems}}\n <li class=\"nav-item\">\n <a class=\"nav-link {{#active}}active{{/active}}\" href=\"{{route}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{text}}\n </a>\n </li>\n {{/data.navItems}}\n </ul>\n {{/data.showNavItems}}\n\n {{#data.hasRightItems}}\n <div class=\"navbar-nav ms-auto\">\n {{#data.rightItems}}\n {{#isDropdown}}\n <div class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" role=\"button\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n {{#items}}\n {{#divider}}\n <li><hr class=\"dropdown-divider\"></li>\n {{/divider}}\n {{^divider}}\n <li>\n <a class=\"dropdown-item\" role=\"button\" {{#action}}data-action=\"{{action}}\"{{/action}}>\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n </li>\n {{/divider}}\n {{/items}}\n </ul>\n </div>\n {{/isDropdown}}\n {{^isDropdown}}\n {{#isButton}}\n <button class=\"{{buttonClass}}\" data-action=\"{{action}}\" data-id=\"{{id}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </button>\n {{/isButton}}\n {{^isButton}}\n <a class=\"nav-link\" href=\"{{href}}\" {{#action}}data-action=\"{{action}}\"{{/action}}>\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n {{/isButton}}\n {{/isDropdown}}\n {{/data.rightItems}}\n </div>\n {{/data.hasRightItems}}\n </div>\n </div>\n `;\n }\n\n /**\n * Process and normalize data before rendering (like Sidebar)\n */\n async onBeforeRender() {\n await super.onBeforeRender();\n\n const showPageInfo = this.displayMode === 'page' || this.displayMode === 'both';\n const showNavItems = this.displayMode === 'menu' || this.displayMode === 'both';\n\n // Filter navItems based on permissions\n const navItems = this.filterItemsByPermissions(this.config.navItems || []);\n\n // Process right items\n const rightItems = this.processRightItems(this.config.rightItems || []);\n\n this.data = {\n // Brand information\n brand: this.config.brand,\n brandIcon: this.config.brandIcon,\n brandRoute: this.config.brandRoute,\n\n // Navbar configuration\n navbarId: `navbar-${this.id}`,\n\n // Navigation items\n navItems: navItems,\n showNavItems: showNavItems,\n\n // Right items\n rightItems: rightItems,\n hasRightItems: rightItems.length > 0,\n\n // Page display\n showPageInfo: showPageInfo,\n currentPageName: this.currentPage?.title || this.currentPage?.name || '',\n currentPageIcon: this.currentPage?.icon || this.currentPage?.pageIcon || '',\n currentPageDescription: this.showPageDescription ? this.currentPage?.description : '',\n\n // Sidebar toggle\n showSidebarToggle: this.config.showSidebarToggle,\n sidebarToggleAction: this.config.sidebarToggleAction,\n\n // Display mode\n displayMode: this.displayMode\n };\n }\n\n /**\n * Process right items configuration\n */\n processRightItems(rightItems) {\n return this.filterItemsByPermissions(rightItems).map(item => {\n const processedItem = { ...item };\n\n // Filter dropdown items by permissions if they exist\n if (item.items) {\n processedItem.items = this.filterItemsByPermissions(item.items);\n }\n\n // Determine item type\n if (processedItem.items && processedItem.items.length > 0) {\n // Dropdown menu\n processedItem.isDropdown = true;\n processedItem.isButton = false;\n } else if (item.buttonClass) {\n // Button\n processedItem.isButton = true;\n processedItem.isDropdown = false;\n } else {\n // Link\n processedItem.isButton = false;\n processedItem.isDropdown = false;\n }\n\n // Store handler if provided\n if (item.handler) {\n this.rightItemHandlers = this.rightItemHandlers || new Map();\n this.rightItemHandlers.set(item.id, item.handler);\n }\n\n return processedItem;\n });\n }\n\n /**\n * Setup listeners for page change events\n */\n setupPageListeners() {\n // Use global MOJO event bus if available\n this.getApp().events.on([\"page:show\", \"page:hide\", \"page:denied\"], (data) => {\n this.onPageChanged(data);\n });\n }\n\n /**\n * Handle page before change event\n * @param {object} data - Event data\n */\n onPageBeforeChange(data) {\n // Can be used to show loading state\n if (this.displayMode === 'page' || this.displayMode === 'both') {\n // Optionally show loading indicator\n }\n }\n\n /**\n * Handle page changed event\n * @param {object} data - Event data with previousPage and currentPage\n */\n onPageChanged(data) {\n this.previousPage = this.currentPage;\n this.currentPage = data.page;\n\n // Update display based on mode\n if (this.displayMode === 'page' || this.displayMode === 'both') {\n this.updatePageDisplay();\n }\n\n // Update active menu items\n if (this.displayMode === 'menu' || this.displayMode === 'both') {\n if (this.currentPage && this.currentPage.route) {\n this.updateActiveItem(this.currentPage.route);\n }\n }\n }\n\n /**\n * Update the display to show current page info\n */\n updatePageDisplay() {\n if (!this.currentPage) return;\n\n // Just trigger re-render, onBeforeRender will handle the data processing\n if (this.mounted) {\n this.render();\n }\n }\n\n updateActiveItem(currentRoute) {\n // Normalize routes for comparison\n const normalizeRoute = (route) => {\n if (!route) return '/';\n return route.startsWith('/') ? route : `/${route}`;\n };\n\n const normalizedCurrentRoute = normalizeRoute(currentRoute);\n\n // Update active states with improved matching\n const navItems = this.data.navItems.map(item => {\n const normalizedItemRoute = normalizeRoute(item.route);\n\n // Check for active state\n let isActive = false;\n\n if (normalizedItemRoute === '/' && normalizedCurrentRoute === '/') {\n // Exact match for home route\n isActive = true;\n } else if (normalizedItemRoute !== '/' && normalizedCurrentRoute !== '/') {\n // For non-home routes, check if current route starts with nav item route\n // This allows /users to be active when on /users/123\n isActive = normalizedCurrentRoute.startsWith(normalizedItemRoute) ||\n normalizedCurrentRoute === normalizedItemRoute;\n }\n\n return {\n ...item,\n active: isActive\n };\n });\n\n this.updateData({ navItems }, true);\n }\n\n onPassThruActionProfile() {\n // Implement profile functionality here\n this.getApp().events.emit(\"portal:action\", {action: \"profile\"});\n }\n\n onActionSettings() {\n // Implement settings functionality here\n this.getApp().events.emit(\"portal:action\", {action: \"settings\"});\n }\n\n onActionLogout() {\n // Implement logout functionality here\n this.getApp().events.emit(\"auth:logout\", {action: \"logout\"});\n }\n\n /**\n * Handle dynamic action dispatch for right items\n */\n async handleAction(actionName, event, element) {\n // Check for custom handler first\n const itemId = element.getAttribute('data-id');\n if (itemId && this.rightItemHandlers && this.rightItemHandlers.has(itemId)) {\n const handler = this.rightItemHandlers.get(itemId);\n if (typeof handler === 'function') {\n return await handler.call(this, actionName, event, element);\n }\n }\n\n // Fallback to default action methods\n const methodName = `onAction${actionName.charAt(0).toUpperCase() + actionName.slice(1).replace(/-([a-z])/g, (g) => g[1].toUpperCase())}`;\n if (typeof this[methodName] === 'function') {\n return await this[methodName](event, element);\n }\n\n // Emit action event if no handler found\n this.emit('action', {\n action: actionName,\n event: event,\n element: element,\n topnav: this\n });\n }\n\n /**\n * Handle default actions by searching through rightItems and navItems\n */\n async onActionDefault(action, event, el) {\n // Check navItems first\n if (this.config.navItems) {\n for (const item of this.config.navItems) {\n if (item.action === action && item.handler) {\n await item.handler.call(this, action, event, el);\n return true;\n }\n }\n }\n\n // Check rightItems\n if (this.config.rightItems) {\n for (const item of this.config.rightItems) {\n if (item.action === action && item.handler) {\n await item.handler.call(this, action, event, el);\n return true;\n }\n // Also check dropdown items\n if (item.items) {\n for (const subItem of item.items) {\n if (subItem.action === action && subItem.handler) {\n await subItem.handler.call(this, action, event, el);\n return true;\n }\n }\n }\n }\n }\n\n this.getApp().events.emit(\"portal:action\", { action, event, el });\n\n return false;\n }\n\n /**\n * Filter items by user permissions\n */\n filterItemsByPermissions(items) {\n if (!items) return [];\n\n const app = this.getApp();\n const activeUser = app?.activeUser;\n\n return items.filter(item => {\n // If item has permissions and user exists, check permissions\n if (item.permissions && activeUser) {\n return activeUser.hasPermission(item.permissions);\n }\n // If no permissions required or no user, show the item\n return true;\n });\n }\n\n}\n\nexport default TopNav;\n"],"names":["TopNav","View","constructor","options","themes","light","dark","clean","gradient","navbarClass","theme","shadow","super","tagName","className","style","this","displayMode","showPageIcon","showPageDescription","showBreadcrumbs","currentPage","previousPage","config","brand","brandIcon","brandRoute","navItems","rightItems","showSidebarToggle","sidebarToggleAction","userMenu","findMenuItem","id","loginMenu","setupPageListeners","item","find","replaceMenuItem","new_menu","navIndex","findIndex","rightIndex","setBrand","icon","render","setUser","user","label","get","setModel","_onModelChange","model","isMounted","getTemplate","onBeforeRender","showPageInfo","showNavItems","filterItemsByPermissions","processRightItems","data","navbarId","hasRightItems","length","currentPageName","title","name","currentPageIcon","pageIcon","currentPageDescription","description","map","processedItem","items","isDropdown","isButton","buttonClass","handler","rightItemHandlers","Map","set","getApp","events","on","onPageChanged","onPageBeforeChange","page","updatePageDisplay","route","updateActiveItem","mounted","currentRoute","normalizeRoute","startsWith","normalizedCurrentRoute","normalizedItemRoute","isActive","active","updateData","onPassThruActionProfile","emit","action","onActionSettings","onActionLogout","handleAction","actionName","event","element","itemId","getAttribute","has","call","methodName","charAt","toUpperCase","slice","replace","g","topnav","onActionDefault","el","subItem","app","activeUser","filter","permissions","hasPermission"],"mappings":"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"}
1
+ {"version":3,"file":"TopNav-DnpYXG9a.js","sources":["../../src/core/views/navigation/TopNav.js"],"sourcesContent":["/**\n * TopNav - Bootstrap navbar component for MOJO framework\n * Provides clean, responsive top navigation\n */\n\nimport View from '@core/View.js';\n\nclass TopNav extends View {\n constructor(options = {}) {\n // Define theme-to-class mappings\n const themes = {\n light: 'navbar navbar-expand-lg navbar-light topnav-light',\n dark: 'navbar navbar-expand-lg navbar-dark topnav-dark',\n clean: 'navbar navbar-expand-lg navbar-light topnav-clean',\n gradient: 'navbar navbar-expand-lg navbar-dark topnav-gradient',\n };\n\n // Set a default theme and determine the final class string\n const themeName = options.theme || 'light';\n let navbarClass = themes[themeName] || themes.light;\n\n // Add shadow class if specified\n if (options.shadow) {\n navbarClass += ` topnav-shadow-${options.shadow}`;\n }\n\n super({\n tagName: 'nav',\n className: navbarClass,\n style: 'position: relative; z-index: 1030;',\n ...options\n });\n\n // Display mode configuration\n this.displayMode = options.displayMode || 'both'; // 'menu' | 'page' | 'both'\n this.showPageIcon = options.showPageIcon !== false;\n this.showPageDescription = options.showPageDescription || false;\n this.showBreadcrumbs = options.showBreadcrumbs || false;\n\n // Current page tracking\n this.currentPage = null;\n this.previousPage = null;\n\n // Store raw config for processing in onBeforeRender\n this.config = {\n brand: options.brand || 'MOJO App',\n brandIcon: options.brandIcon || 'bi bi-play-circle',\n brandRoute: options.brandRoute || '/',\n navItems: options.navItems || [],\n rightItems: options.rightItems || [],\n showSidebarToggle: options.showSidebarToggle || false,\n sidebarToggleAction: options.sidebarToggleAction || 'toggle-sidebar',\n ...options\n };\n this.userMenu = options.userMenu || this.findMenuItem('user');\n if (this.userMenu) this.userMenu.id = \"user\";\n this.loginMenu = options.loginMenu || this.findMenuItem('login');\n // Setup page event listeners\n this.setupPageListeners();\n }\n\n findMenuItem(id) {\n let item = this.config.navItems.find(item => item.id === id);\n if (!item) {\n item = this.config.rightItems.find(item => item.id === id);\n }\n return item || null;\n }\n\n replaceMenuItem(id, new_menu) {\n // Find and replace in navItems\n const navIndex = this.config.navItems.findIndex(item => item.id === id);\n if (navIndex !== -1) {\n this.config.navItems[navIndex] = new_menu;\n return true;\n }\n\n // Find and replace in rightItems\n const rightIndex = this.config.rightItems.findIndex(item => item.id === id);\n if (rightIndex !== -1) {\n this.config.rightItems[rightIndex] = new_menu;\n return true;\n }\n\n return false;\n }\n\n setBrand(brand, icon=null) {\n this.config.brand = brand;\n this.config.brandIcon = icon || this.config.brandIcon;\n this.render();\n }\n\n setUser(user) {\n if (!user) {\n this.replaceMenuItem('user', this.loginMenu);\n } else {\n this.userMenu.label = user.get(\"display_name\");\n this.replaceMenuItem('login', this.userMenu);\n }\n this.setModel(user);\n }\n\n _onModelChange() {\n if (this.model) {\n this.userMenu.label = this.model.get(\"display_name\");\n }\n if (this.isMounted()) {\n this.render();\n }\n }\n\n /**\n * Get template based on display mode\n */\n async getTemplate() {\n return `\n <div class=\"container-fluid\">\n {{#data.showSidebarToggle}}\n <button class=\"topnav-sidebar-toggle me-2\" data-action=\"{{data.sidebarToggleAction}}\" aria-label=\"Toggle Sidebar\">\n <i class=\"bi bi-chevron-right toggle-chevron\"></i>\n </button>\n {{/data.showSidebarToggle}}\n\n {{#data.showPageInfo}}\n <div class=\"navbar-brand d-flex align-items-center\">\n {{#data.currentPageIcon}}<i class=\"{{data.currentPageIcon}} me-2\"></i>{{/data.currentPageIcon}}\n <div>\n <span>{{data.currentPageName}}</span>\n {{#data.currentPageDescription}}\n <small class=\"d-block\" style=\"font-size: 0.75rem; line-height: 1;\">{{data.currentPageDescription}}</small>\n {{/data.currentPageDescription}}\n </div>\n </div>\n {{/data.showPageInfo}}\n\n {{^data.showPageInfo}}\n <a class=\"navbar-brand\" href=\"{{data.brandRoute}}\">\n {{#data.brandIcon}}<i class=\"{{data.brandIcon}} me-2\"></i>{{/data.brandIcon}}\n {{data.brand}}\n </a>\n {{/data.showPageInfo}}\n\n <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#{{data.navbarId}}\">\n <span class=\"navbar-toggler-icon\"></span>\n </button>\n\n <div class=\"collapse navbar-collapse\" id=\"{{data.navbarId}}\">\n {{#data.showNavItems}}\n <ul class=\"navbar-nav me-auto mb-2 mb-lg-0\">\n {{#data.navItems}}\n <li class=\"nav-item\">\n <a class=\"nav-link {{#active}}active{{/active}}\" href=\"{{route}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{text}}\n </a>\n </li>\n {{/data.navItems}}\n </ul>\n {{/data.showNavItems}}\n\n {{#data.hasRightItems}}\n <div class=\"navbar-nav ms-auto\">\n {{#data.rightItems}}\n {{#isDropdown}}\n <div class=\"nav-item dropdown\">\n <a class=\"nav-link dropdown-toggle\" role=\"button\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n {{#items}}\n {{#divider}}\n <li><hr class=\"dropdown-divider\"></li>\n {{/divider}}\n {{^divider}}\n <li>\n <a class=\"dropdown-item\" role=\"button\" {{#action}}data-action=\"{{action}}\"{{/action}}>\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n </li>\n {{/divider}}\n {{/items}}\n </ul>\n </div>\n {{/isDropdown}}\n {{^isDropdown}}\n {{#isButton}}\n <button class=\"{{buttonClass}}\" data-action=\"{{action}}\" data-id=\"{{id}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </button>\n {{/isButton}}\n {{^isButton}}\n <a class=\"nav-link\" href=\"{{href}}\" {{#action}}data-action=\"{{action}}\"{{/action}}>\n {{#icon}}<i class=\"{{icon}} me-1\"></i>{{/icon}}\n {{label}}\n </a>\n {{/isButton}}\n {{/isDropdown}}\n {{/data.rightItems}}\n </div>\n {{/data.hasRightItems}}\n </div>\n </div>\n `;\n }\n\n /**\n * Process and normalize data before rendering (like Sidebar)\n */\n async onBeforeRender() {\n await super.onBeforeRender();\n\n const showPageInfo = this.displayMode === 'page' || this.displayMode === 'both';\n const showNavItems = this.displayMode === 'menu' || this.displayMode === 'both';\n\n // Filter navItems based on permissions\n const navItems = this.filterItemsByPermissions(this.config.navItems || []);\n\n // Process right items\n const rightItems = this.processRightItems(this.config.rightItems || []);\n\n this.data = {\n // Brand information\n brand: this.config.brand,\n brandIcon: this.config.brandIcon,\n brandRoute: this.config.brandRoute,\n\n // Navbar configuration\n navbarId: `navbar-${this.id}`,\n\n // Navigation items\n navItems: navItems,\n showNavItems: showNavItems,\n\n // Right items\n rightItems: rightItems,\n hasRightItems: rightItems.length > 0,\n\n // Page display\n showPageInfo: showPageInfo,\n currentPageName: this.currentPage?.title || this.currentPage?.name || '',\n currentPageIcon: this.currentPage?.icon || this.currentPage?.pageIcon || '',\n currentPageDescription: this.showPageDescription ? this.currentPage?.description : '',\n\n // Sidebar toggle\n showSidebarToggle: this.config.showSidebarToggle,\n sidebarToggleAction: this.config.sidebarToggleAction,\n\n // Display mode\n displayMode: this.displayMode\n };\n }\n\n /**\n * Process right items configuration\n */\n processRightItems(rightItems) {\n return this.filterItemsByPermissions(rightItems).map(item => {\n const processedItem = { ...item };\n\n // Filter dropdown items by permissions if they exist\n if (item.items) {\n processedItem.items = this.filterItemsByPermissions(item.items);\n }\n\n // Determine item type\n if (processedItem.items && processedItem.items.length > 0) {\n // Dropdown menu\n processedItem.isDropdown = true;\n processedItem.isButton = false;\n } else if (item.buttonClass) {\n // Button\n processedItem.isButton = true;\n processedItem.isDropdown = false;\n } else {\n // Link\n processedItem.isButton = false;\n processedItem.isDropdown = false;\n }\n\n // Store handler if provided\n if (item.handler) {\n this.rightItemHandlers = this.rightItemHandlers || new Map();\n this.rightItemHandlers.set(item.id, item.handler);\n }\n\n return processedItem;\n });\n }\n\n /**\n * Setup listeners for page change events\n */\n setupPageListeners() {\n // Use global MOJO event bus if available\n this.getApp().events.on([\"page:show\", \"page:hide\", \"page:denied\"], (data) => {\n this.onPageChanged(data);\n });\n }\n\n /**\n * Handle page before change event\n * @param {object} data - Event data\n */\n onPageBeforeChange(data) {\n // Can be used to show loading state\n if (this.displayMode === 'page' || this.displayMode === 'both') {\n // Optionally show loading indicator\n }\n }\n\n /**\n * Handle page changed event\n * @param {object} data - Event data with previousPage and currentPage\n */\n onPageChanged(data) {\n this.previousPage = this.currentPage;\n this.currentPage = data.page;\n\n // Update display based on mode\n if (this.displayMode === 'page' || this.displayMode === 'both') {\n this.updatePageDisplay();\n }\n\n // Update active menu items\n if (this.displayMode === 'menu' || this.displayMode === 'both') {\n if (this.currentPage && this.currentPage.route) {\n this.updateActiveItem(this.currentPage.route);\n }\n }\n }\n\n /**\n * Update the display to show current page info\n */\n updatePageDisplay() {\n if (!this.currentPage) return;\n\n // Just trigger re-render, onBeforeRender will handle the data processing\n if (this.mounted) {\n this.render();\n }\n }\n\n updateActiveItem(currentRoute) {\n // Normalize routes for comparison\n const normalizeRoute = (route) => {\n if (!route) return '/';\n return route.startsWith('/') ? route : `/${route}`;\n };\n\n const normalizedCurrentRoute = normalizeRoute(currentRoute);\n\n // Update active states with improved matching\n const navItems = this.data.navItems.map(item => {\n const normalizedItemRoute = normalizeRoute(item.route);\n\n // Check for active state\n let isActive = false;\n\n if (normalizedItemRoute === '/' && normalizedCurrentRoute === '/') {\n // Exact match for home route\n isActive = true;\n } else if (normalizedItemRoute !== '/' && normalizedCurrentRoute !== '/') {\n // For non-home routes, check if current route starts with nav item route\n // This allows /users to be active when on /users/123\n isActive = normalizedCurrentRoute.startsWith(normalizedItemRoute) ||\n normalizedCurrentRoute === normalizedItemRoute;\n }\n\n return {\n ...item,\n active: isActive\n };\n });\n\n this.updateData({ navItems }, true);\n }\n\n onPassThruActionProfile() {\n // Implement profile functionality here\n this.getApp().events.emit(\"portal:action\", {action: \"profile\"});\n }\n\n onActionSettings() {\n // Implement settings functionality here\n this.getApp().events.emit(\"portal:action\", {action: \"settings\"});\n }\n\n onActionLogout() {\n // Implement logout functionality here\n this.getApp().events.emit(\"auth:logout\", {action: \"logout\"});\n }\n\n /**\n * Handle dynamic action dispatch for right items\n */\n async handleAction(actionName, event, element) {\n // Check for custom handler first\n const itemId = element.getAttribute('data-id');\n if (itemId && this.rightItemHandlers && this.rightItemHandlers.has(itemId)) {\n const handler = this.rightItemHandlers.get(itemId);\n if (typeof handler === 'function') {\n return await handler.call(this, actionName, event, element);\n }\n }\n\n // Fallback to default action methods\n const methodName = `onAction${actionName.charAt(0).toUpperCase() + actionName.slice(1).replace(/-([a-z])/g, (g) => g[1].toUpperCase())}`;\n if (typeof this[methodName] === 'function') {\n return await this[methodName](event, element);\n }\n\n // Emit action event if no handler found\n this.emit('action', {\n action: actionName,\n event: event,\n element: element,\n topnav: this\n });\n }\n\n /**\n * Handle default actions by searching through rightItems and navItems\n */\n async onActionDefault(action, event, el) {\n // Check navItems first\n if (this.config.navItems) {\n for (const item of this.config.navItems) {\n if (item.action === action && item.handler) {\n await item.handler.call(this, action, event, el);\n return true;\n }\n }\n }\n\n // Check rightItems\n if (this.config.rightItems) {\n for (const item of this.config.rightItems) {\n if (item.action === action && item.handler) {\n await item.handler.call(this, action, event, el);\n return true;\n }\n // Also check dropdown items\n if (item.items) {\n for (const subItem of item.items) {\n if (subItem.action === action && subItem.handler) {\n await subItem.handler.call(this, action, event, el);\n return true;\n }\n }\n }\n }\n }\n\n this.getApp().events.emit(\"portal:action\", { action, event, el });\n\n return false;\n }\n\n /**\n * Filter items by user permissions\n */\n filterItemsByPermissions(items) {\n if (!items) return [];\n\n const app = this.getApp();\n const activeUser = app?.activeUser;\n\n return items.filter(item => {\n // If item has permissions and user exists, check permissions\n if (item.permissions && activeUser) {\n return activeUser.hasPermission(item.permissions);\n }\n // If no permissions required or no user, show the item\n return true;\n });\n }\n\n}\n\nexport default TopNav;\n"],"names":["TopNav","View","constructor","options","themes","light","dark","clean","gradient","navbarClass","theme","shadow","super","tagName","className","style","this","displayMode","showPageIcon","showPageDescription","showBreadcrumbs","currentPage","previousPage","config","brand","brandIcon","brandRoute","navItems","rightItems","showSidebarToggle","sidebarToggleAction","userMenu","findMenuItem","id","loginMenu","setupPageListeners","item","find","replaceMenuItem","new_menu","navIndex","findIndex","rightIndex","setBrand","icon","render","setUser","user","label","get","setModel","_onModelChange","model","isMounted","getTemplate","onBeforeRender","showPageInfo","showNavItems","filterItemsByPermissions","processRightItems","data","navbarId","hasRightItems","length","currentPageName","title","name","currentPageIcon","pageIcon","currentPageDescription","description","map","processedItem","items","isDropdown","isButton","buttonClass","handler","rightItemHandlers","Map","set","getApp","events","on","onPageChanged","onPageBeforeChange","page","updatePageDisplay","route","updateActiveItem","mounted","currentRoute","normalizeRoute","startsWith","normalizedCurrentRoute","normalizedItemRoute","isActive","active","updateData","onPassThruActionProfile","emit","action","onActionSettings","onActionLogout","handleAction","actionName","event","element","itemId","getAttribute","has","call","methodName","charAt","toUpperCase","slice","replace","g","topnav","onActionDefault","el","subItem","app","activeUser","filter","permissions","hasPermission"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- const VERSION = "2.1.534";
1
+ const VERSION = "2.1.535";
2
2
  const VERSION_MAJOR = 2;
3
3
  const VERSION_MINOR = 1;
4
- const VERSION_REVISION = 534;
5
- const BUILD_TIME = "2025-10-01T13:44:40.935Z";
4
+ const VERSION_REVISION = 535;
5
+ const BUILD_TIME = "2025-10-01T13:51:55.569Z";
6
6
  const VERSION_INFO = {
7
7
  full: VERSION,
8
8
  major: VERSION_MAJOR,
@@ -1498,7 +1498,7 @@ class DataFormatter {
1498
1498
  * {{value|tooltip:'Help text':top}}
1499
1499
  * {{value|tooltip:'Info':bottom:html}}
1500
1500
  *
1501
- * @param {*} value - Value to display
1501
+ * @param {*} value - Value to display (not escaped, works with formatter chains)
1502
1502
  * @param {string} text - Tooltip text content
1503
1503
  * @param {string} placement - Tooltip placement: top, bottom, left, right (default: top)
1504
1504
  * @param {string} html - 'html' to allow HTML in tooltip (default: text only)
@@ -1506,7 +1506,7 @@ class DataFormatter {
1506
1506
  */
1507
1507
  tooltip(value, text = "", placement = "top", html = "") {
1508
1508
  if (value === null || value === void 0) return "";
1509
- const displayValue = this.escapeHtml(String(value));
1509
+ const displayValue = String(value);
1510
1510
  const tooltipText = html === "html" ? text : this.escapeHtml(text);
1511
1511
  const dataAttr = html === "html" ? 'data-bs-html="true"' : "";
1512
1512
  return `<span data-bs-toggle="tooltip" data-bs-placement="${placement}" ${dataAttr} data-bs-title="${tooltipText}">${displayValue}</span>`;
@@ -4860,7 +4860,7 @@ class WebApp {
4860
4860
  */
4861
4861
  async showError(message) {
4862
4862
  try {
4863
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4863
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4864
4864
  await Dialog.alert(message, "Error", { size: "md", class: "text-danger" });
4865
4865
  } catch (e) {
4866
4866
  this.events.emit("notification", { message, type: "error" });
@@ -4877,7 +4877,7 @@ class WebApp {
4877
4877
  */
4878
4878
  async showSuccess(message) {
4879
4879
  try {
4880
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4880
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4881
4881
  await Dialog.alert(message, "Success", { size: "md", class: "text-success" });
4882
4882
  } catch (e) {
4883
4883
  this.events.emit("notification", { message, type: "success" });
@@ -4894,7 +4894,7 @@ class WebApp {
4894
4894
  */
4895
4895
  async showInfo(message) {
4896
4896
  try {
4897
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4897
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4898
4898
  await Dialog.alert(message, "Information", { size: "md", class: "text-info" });
4899
4899
  } catch (e) {
4900
4900
  this.events.emit("notification", { message, type: "info" });
@@ -4911,7 +4911,7 @@ class WebApp {
4911
4911
  */
4912
4912
  async showWarning(message) {
4913
4913
  try {
4914
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4914
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4915
4915
  await Dialog.alert(message, "Warning", { size: "md", class: "text-warning" });
4916
4916
  } catch (e) {
4917
4917
  this.events.emit("notification", { message, type: "warning" });
@@ -4937,7 +4937,7 @@ class WebApp {
4937
4937
  opts = { message: opts };
4938
4938
  }
4939
4939
  try {
4940
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4940
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4941
4941
  Dialog.showBusy(opts);
4942
4942
  } catch (e) {
4943
4943
  if (typeof window !== "undefined" && window?.console) {
@@ -4951,7 +4951,7 @@ class WebApp {
4951
4951
  */
4952
4952
  async hideLoading() {
4953
4953
  try {
4954
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4954
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4955
4955
  Dialog.hideBusy();
4956
4956
  } catch (e) {
4957
4957
  if (typeof window !== "undefined" && window?.console) {
@@ -4961,7 +4961,7 @@ class WebApp {
4961
4961
  }
4962
4962
  async showModelForm(options = {}) {
4963
4963
  try {
4964
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4964
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4965
4965
  return await Dialog.showModelForm(options);
4966
4966
  } catch (e) {
4967
4967
  if (typeof window !== "undefined" && window?.console) {
@@ -4972,7 +4972,7 @@ class WebApp {
4972
4972
  }
4973
4973
  async showForm(options = {}) {
4974
4974
  try {
4975
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4975
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4976
4976
  return await Dialog.showForm(options);
4977
4977
  } catch (e) {
4978
4978
  if (typeof window !== "undefined" && window?.console) {
@@ -4982,7 +4982,7 @@ class WebApp {
4982
4982
  }
4983
4983
  }
4984
4984
  async confirm(message, title = "Confirm", options = {}) {
4985
- const Dialog = (await import("./Dialog-DOltX5lG.js")).default;
4985
+ const Dialog = (await import("./Dialog-Be3ZkGtt.js")).default;
4986
4986
  return await Dialog.confirm(message, title, options);
4987
4987
  }
4988
4988
  /**
@@ -5235,4 +5235,4 @@ export {
5235
5235
  EventEmitter as i,
5236
5236
  rest as r
5237
5237
  };
5238
- //# sourceMappingURL=WebApp-DhGNFpF5.js.map
5238
+ //# sourceMappingURL=WebApp-Brh2wRZ3.js.map