web-mojo 2.1.295 → 2.1.338

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 (68) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +54 -53
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +3 -3
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +2 -2
  11. package/dist/chunks/{ContextMenu-DKKmjtgo.js → ContextMenu-BRqHEC2q.js} +106 -29
  12. package/dist/chunks/ContextMenu-BRqHEC2q.js.map +1 -0
  13. package/dist/chunks/ContextMenu-DoWfr0BY.js +3 -0
  14. package/dist/chunks/ContextMenu-DoWfr0BY.js.map +1 -0
  15. package/dist/chunks/{DataView-CX04UO6n.js → DataView-DgOTz7NS.js} +2 -2
  16. package/dist/chunks/{DataView-CX04UO6n.js.map → DataView-DgOTz7NS.js.map} +1 -1
  17. package/dist/chunks/{DataView-CjjREbqE.js → DataView-FYalUi83.js} +2 -2
  18. package/dist/chunks/{DataView-CjjREbqE.js.map → DataView-FYalUi83.js.map} +1 -1
  19. package/dist/chunks/{Dialog-Dvb1SU1y.js → Dialog-CpvYwnK7.js} +2 -2
  20. package/dist/chunks/{Dialog-Dvb1SU1y.js.map → Dialog-CpvYwnK7.js.map} +1 -1
  21. package/dist/chunks/{Dialog-C7XcgLt8.js → Dialog-D7HCQQMK.js} +5 -5
  22. package/dist/chunks/{Dialog-C7XcgLt8.js.map → Dialog-D7HCQQMK.js.map} +1 -1
  23. package/dist/chunks/{FilePreviewView-CB0yqQtV.js → FilePreviewView-BokAhylL.js} +13 -10
  24. package/dist/chunks/FilePreviewView-BokAhylL.js.map +1 -0
  25. package/dist/chunks/FilePreviewView-Ct4v1mkr.js +2 -0
  26. package/dist/chunks/FilePreviewView-Ct4v1mkr.js.map +1 -0
  27. package/dist/chunks/{FormView-DJxUvj0p.js → FormView-CWNpkr0M.js} +212 -67
  28. package/dist/chunks/FormView-CWNpkr0M.js.map +1 -0
  29. package/dist/chunks/FormView-WUi-NXkB.js +2 -0
  30. package/dist/chunks/FormView-WUi-NXkB.js.map +1 -0
  31. package/dist/chunks/{MetricsChart-DQUOkYQ9.js → MetricsChart-2JMgp_0p.js} +2 -2
  32. package/dist/chunks/{MetricsChart-DQUOkYQ9.js.map → MetricsChart-2JMgp_0p.js.map} +1 -1
  33. package/dist/chunks/{MetricsChart-CpfACPrE.js → MetricsChart-CgX9kX8T.js} +3 -3
  34. package/dist/chunks/{MetricsChart-CpfACPrE.js.map → MetricsChart-CgX9kX8T.js.map} +1 -1
  35. package/dist/chunks/{PDFViewer-BUOnvSfV.js → PDFViewer-BZSQSArl.js} +2 -2
  36. package/dist/chunks/{PDFViewer-BUOnvSfV.js.map → PDFViewer-BZSQSArl.js.map} +1 -1
  37. package/dist/chunks/{PDFViewer-I1qlINnm.js → PDFViewer-kZOAIH9e.js} +3 -3
  38. package/dist/chunks/{PDFViewer-I1qlINnm.js.map → PDFViewer-kZOAIH9e.js.map} +1 -1
  39. package/dist/chunks/{Page-ZtYiac8Q.js → Page-BnFeIkwG.js} +2 -2
  40. package/dist/chunks/{Page-ZtYiac8Q.js.map → Page-BnFeIkwG.js.map} +1 -1
  41. package/dist/chunks/{Page-h5bbwzKf.js → Page-CHwwfFau.js} +2 -2
  42. package/dist/chunks/{Page-h5bbwzKf.js.map → Page-CHwwfFau.js.map} +1 -1
  43. package/dist/chunks/{TopNav-DVX8V6fL.js → TopNav-BSelpZnA.js} +2 -2
  44. package/dist/chunks/{TopNav-DVX8V6fL.js.map → TopNav-BSelpZnA.js.map} +1 -1
  45. package/dist/chunks/{TopNav-mtuzmduR.js → TopNav-V_BlTDgB.js} +2 -2
  46. package/dist/chunks/{TopNav-mtuzmduR.js.map → TopNav-V_BlTDgB.js.map} +1 -1
  47. package/dist/chunks/{WebApp-BgZwmmBY.js → WebApp-CEW6rw8N.js} +12 -12
  48. package/dist/chunks/{WebApp-BgZwmmBY.js.map → WebApp-CEW6rw8N.js.map} +1 -1
  49. package/dist/chunks/{WebApp-C_DpNvNx.js → WebApp-CTalj3eA.js} +2 -2
  50. package/dist/chunks/{WebApp-C_DpNvNx.js.map → WebApp-CTalj3eA.js.map} +1 -1
  51. package/dist/core.css +114 -0
  52. package/dist/css/web-mojo.css +1 -1
  53. package/dist/docit.cjs.js +1 -1
  54. package/dist/docit.es.js +5 -5
  55. package/dist/index.cjs.js +1 -1
  56. package/dist/index.es.js +11 -11
  57. package/dist/lightbox.cjs.js +1 -1
  58. package/dist/lightbox.es.js +4 -4
  59. package/package.json +1 -1
  60. package/dist/chunks/ContextMenu-BhyvSOLE.js +0 -3
  61. package/dist/chunks/ContextMenu-BhyvSOLE.js.map +0 -1
  62. package/dist/chunks/ContextMenu-DKKmjtgo.js.map +0 -1
  63. package/dist/chunks/FilePreviewView-CB0yqQtV.js.map +0 -1
  64. package/dist/chunks/FilePreviewView-DgVmlBjn.js +0 -2
  65. package/dist/chunks/FilePreviewView-DgVmlBjn.js.map +0 -1
  66. package/dist/chunks/FormView-CWFqrlKb.js +0 -2
  67. package/dist/chunks/FormView-CWFqrlKb.js.map +0 -1
  68. package/dist/chunks/FormView-DJxUvj0p.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"TopNav-DVX8V6fL.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-BSelpZnA.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-C_DpNvNx.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-mtuzmduR.js.map
1
+ "use strict";const t=require("./WebApp-CTalj3eA.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-V_BlTDgB.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TopNav-mtuzmduR.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-V_BlTDgB.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.295";
1
+ const VERSION = "2.1.338";
2
2
  const VERSION_MAJOR = 2;
3
3
  const VERSION_MINOR = 1;
4
- const VERSION_REVISION = 295;
5
- const BUILD_TIME = "2025-09-27T16:53:54.885Z";
4
+ const VERSION_REVISION = 338;
5
+ const BUILD_TIME = "2025-09-28T04:09:05.025Z";
6
6
  const VERSION_INFO = {
7
7
  full: VERSION,
8
8
  major: VERSION_MAJOR,
@@ -4732,7 +4732,7 @@ class WebApp {
4732
4732
  */
4733
4733
  async showError(message) {
4734
4734
  try {
4735
- const Dialog = (await import("./Dialog-C7XcgLt8.js")).default;
4735
+ const Dialog = (await import("./Dialog-D7HCQQMK.js")).default;
4736
4736
  await Dialog.alert(message, "Error", { size: "md", class: "text-danger" });
4737
4737
  } catch (e) {
4738
4738
  this.events.emit("notification", { message, type: "error" });
@@ -4749,7 +4749,7 @@ class WebApp {
4749
4749
  */
4750
4750
  async showSuccess(message) {
4751
4751
  try {
4752
- const Dialog = (await import("./Dialog-C7XcgLt8.js")).default;
4752
+ const Dialog = (await import("./Dialog-D7HCQQMK.js")).default;
4753
4753
  await Dialog.alert(message, "Success", { size: "md", class: "text-success" });
4754
4754
  } catch (e) {
4755
4755
  this.events.emit("notification", { message, type: "success" });
@@ -4766,7 +4766,7 @@ class WebApp {
4766
4766
  */
4767
4767
  async showInfo(message) {
4768
4768
  try {
4769
- const Dialog = (await import("./Dialog-C7XcgLt8.js")).default;
4769
+ const Dialog = (await import("./Dialog-D7HCQQMK.js")).default;
4770
4770
  await Dialog.alert(message, "Information", { size: "md", class: "text-info" });
4771
4771
  } catch (e) {
4772
4772
  this.events.emit("notification", { message, type: "info" });
@@ -4783,7 +4783,7 @@ class WebApp {
4783
4783
  */
4784
4784
  async showWarning(message) {
4785
4785
  try {
4786
- const Dialog = (await import("./Dialog-C7XcgLt8.js")).default;
4786
+ const Dialog = (await import("./Dialog-D7HCQQMK.js")).default;
4787
4787
  await Dialog.alert(message, "Warning", { size: "md", class: "text-warning" });
4788
4788
  } catch (e) {
4789
4789
  this.events.emit("notification", { message, type: "warning" });
@@ -4809,7 +4809,7 @@ class WebApp {
4809
4809
  opts = { message: opts };
4810
4810
  }
4811
4811
  try {
4812
- const Dialog = (await import("./Dialog-C7XcgLt8.js")).default;
4812
+ const Dialog = (await import("./Dialog-D7HCQQMK.js")).default;
4813
4813
  Dialog.showBusy(opts);
4814
4814
  } catch (e) {
4815
4815
  if (typeof window !== "undefined" && window?.console) {
@@ -4823,7 +4823,7 @@ class WebApp {
4823
4823
  */
4824
4824
  async hideLoading() {
4825
4825
  try {
4826
- const Dialog = (await import("./Dialog-C7XcgLt8.js")).default;
4826
+ const Dialog = (await import("./Dialog-D7HCQQMK.js")).default;
4827
4827
  Dialog.hideBusy();
4828
4828
  } catch (e) {
4829
4829
  if (typeof window !== "undefined" && window?.console) {
@@ -4833,7 +4833,7 @@ class WebApp {
4833
4833
  }
4834
4834
  async showModelForm(options = {}) {
4835
4835
  try {
4836
- const Dialog = (await import("./Dialog-C7XcgLt8.js")).default;
4836
+ const Dialog = (await import("./Dialog-D7HCQQMK.js")).default;
4837
4837
  return await Dialog.showModelForm(options);
4838
4838
  } catch (e) {
4839
4839
  if (typeof window !== "undefined" && window?.console) {
@@ -4844,7 +4844,7 @@ class WebApp {
4844
4844
  }
4845
4845
  async showForm(options = {}) {
4846
4846
  try {
4847
- const Dialog = (await import("./Dialog-C7XcgLt8.js")).default;
4847
+ const Dialog = (await import("./Dialog-D7HCQQMK.js")).default;
4848
4848
  return await Dialog.showForm(options);
4849
4849
  } catch (e) {
4850
4850
  if (typeof window !== "undefined" && window?.console) {
@@ -5107,4 +5107,4 @@ export {
5107
5107
  EventEmitter as i,
5108
5108
  rest as r
5109
5109
  };
5110
- //# sourceMappingURL=WebApp-BgZwmmBY.js.map
5110
+ //# sourceMappingURL=WebApp-CEW6rw8N.js.map